1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Executor which can run randomly accessed tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuRandomOrderExecutor.h"
25
26 #include "deClock.h"
27 #include "deStringUtil.hpp"
28 #include "tcuCommandLine.hpp"
29 #include "tcuTestLog.hpp"
30
31 #include <algorithm>
32 #include <cstdio>
33 #include <fstream>
34
35 using std::string;
36 using std::vector;
37
38 namespace tcu
39 {
40
RandomOrderExecutor(TestPackageRoot & root,TestContext & testCtx)41 RandomOrderExecutor::RandomOrderExecutor(TestPackageRoot &root, TestContext &testCtx)
42 : m_testCtx(testCtx), m_inflater(testCtx)
43 {
44 m_nodeStack.push_back(NodeStackEntry(&root));
45 root.getChildren(m_nodeStack[0].children);
46 }
47
~RandomOrderExecutor(void)48 RandomOrderExecutor::~RandomOrderExecutor(void)
49 {
50 pruneStack(1);
51 }
52
pruneStack(size_t newStackSize)53 void RandomOrderExecutor::pruneStack(size_t newStackSize)
54 {
55 // \note Root is not managed by us
56 DE_ASSERT(de::inRange(newStackSize, size_t(1), m_nodeStack.size()));
57
58 while (m_nodeStack.size() > newStackSize)
59 {
60 NodeStackEntry &curEntry = m_nodeStack.back();
61 const bool isPkg = curEntry.node->getNodeType() == NODETYPE_PACKAGE;
62
63 DE_ASSERT((m_nodeStack.size() == 2) == isPkg);
64
65 if (curEntry.node) // Just in case we are in
66 // cleanup path after partial
67 // prune
68 {
69 if (curEntry.node->getNodeType() == NODETYPE_GROUP)
70 m_inflater.leaveGroupNode(static_cast<TestCaseGroup *>(curEntry.node));
71 else if (curEntry.node->getNodeType() == NODETYPE_PACKAGE)
72 m_inflater.leaveTestPackage(static_cast<TestPackage *>(curEntry.node));
73 else
74 DE_ASSERT(curEntry.children.empty());
75
76 curEntry.node = DE_NULL;
77 curEntry.children.clear();
78 }
79
80 if (isPkg)
81 m_caseExecutor.clear();
82
83 m_nodeStack.pop_back();
84 }
85 }
86
findNodeByName(vector<TestNode * > & nodes,const std::string & name)87 static TestNode *findNodeByName(vector<TestNode *> &nodes, const std::string &name)
88 {
89 for (vector<TestNode *>::const_iterator node = nodes.begin(); node != nodes.end(); ++node)
90 {
91 if (name == (*node)->getName())
92 return *node;
93 }
94
95 return DE_NULL;
96 }
97
seekToCase(const string & path)98 TestCase *RandomOrderExecutor::seekToCase(const string &path)
99 {
100 const vector<string> components = de::splitString(path, '.');
101 size_t compNdx;
102
103 DE_ASSERT(!m_nodeStack.empty() && m_nodeStack.front().node->getNodeType() == NODETYPE_ROOT);
104
105 for (compNdx = 0; compNdx < components.size(); compNdx++)
106 {
107 const size_t stackPos = compNdx + 1;
108
109 if (stackPos >= m_nodeStack.size())
110 break; // Stack end
111 else if (components[compNdx] != m_nodeStack[stackPos].node->getName())
112 {
113 // Current stack doesn't match any more, prune.
114 pruneStack(stackPos);
115 break;
116 }
117 }
118
119 DE_ASSERT(m_nodeStack.size() == compNdx + 1);
120
121 for (; compNdx < components.size(); compNdx++)
122 {
123 const size_t parentStackPos = compNdx;
124 TestNode *const curNode =
125 findNodeByName(m_nodeStack[parentStackPos].children, components[compNdx]);
126
127 if (!curNode)
128 throw Exception(string("Test hierarchy node not found: ") + path);
129
130 m_nodeStack.push_back(NodeStackEntry(curNode));
131
132 if (curNode->getNodeType() == NODETYPE_PACKAGE)
133 {
134 TestPackage *const testPackage = static_cast<TestPackage *>(curNode);
135
136 m_inflater.enterTestPackage(testPackage, m_nodeStack.back().children);
137 DE_ASSERT(!m_caseExecutor);
138 m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
139 }
140 else if (curNode->getNodeType() == NODETYPE_GROUP)
141 m_inflater.enterGroupNode(static_cast<TestCaseGroup *>(curNode),
142 m_nodeStack.back().children);
143 else if (compNdx + 1 != components.size())
144 throw Exception(string("Invalid test hierarchy path: ") + path);
145 }
146
147 DE_ASSERT(m_nodeStack.size() == components.size() + 1);
148
149 if (isTestNodeTypeExecutable(m_nodeStack.back().node->getNodeType()))
150 return dynamic_cast<TestCase *>(m_nodeStack.back().node);
151 else
152 throw Exception(string("Not a test case: ") + path);
153 }
154
nodeTypeToTestCaseType(TestNodeType nodeType)155 static qpTestCaseType nodeTypeToTestCaseType(TestNodeType nodeType)
156 {
157 switch (nodeType)
158 {
159 case NODETYPE_SELF_VALIDATE:
160 return QP_TEST_CASE_TYPE_SELF_VALIDATE;
161 case NODETYPE_PERFORMANCE:
162 return QP_TEST_CASE_TYPE_PERFORMANCE;
163 case NODETYPE_CAPABILITY:
164 return QP_TEST_CASE_TYPE_CAPABILITY;
165 case NODETYPE_ACCURACY:
166 return QP_TEST_CASE_TYPE_ACCURACY;
167 default:
168 DE_ASSERT(false);
169 return QP_TEST_CASE_TYPE_LAST;
170 }
171 }
172
execute(const std::string & casePath)173 TestStatus RandomOrderExecutor::execute(const std::string &casePath)
174 {
175 TestCase *const testCase = seekToCase(casePath);
176 TestLog &log = m_testCtx.getLog();
177 const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType());
178
179 m_testCtx.setTerminateAfter(false);
180 log.startCase(casePath.c_str(), caseType);
181
182 {
183 const TestStatus result = executeInner(testCase, casePath);
184 log.endCase(result.getCode(), result.getDescription().c_str());
185 return result;
186 }
187 }
188
executeInner(TestCase * testCase,const std::string & casePath)189 tcu::TestStatus RandomOrderExecutor::executeInner(TestCase *testCase, const std::string &casePath)
190 {
191 TestLog &log = m_testCtx.getLog();
192 const deUint64 testStartTime = deGetMicroseconds();
193
194 m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
195
196 // Initialize, will return immediately if fails
197 try
198 {
199 m_caseExecutor->init(testCase, casePath);
200 }
201 catch (const std::bad_alloc &)
202 {
203 m_testCtx.setTerminateAfter(true);
204 return TestStatus(QP_TEST_RESULT_RESOURCE_ERROR,
205 "Failed to allocate memory in test case init");
206 }
207 catch (const TestException &e)
208 {
209 DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
210 m_testCtx.setTerminateAfter(e.isFatal());
211 log << e;
212 return TestStatus(e.getTestResult(), e.getMessage());
213 }
214 catch (const Exception &e)
215 {
216 log << e;
217 return TestStatus(QP_TEST_RESULT_FAIL, e.getMessage());
218 }
219
220 // Execute
221 for (;;)
222 {
223 TestCase::IterateResult iterateResult = TestCase::STOP;
224
225 m_testCtx.touchWatchdog();
226
227 try
228 {
229 iterateResult = m_caseExecutor->iterate(testCase);
230 }
231 catch (const std::bad_alloc &)
232 {
233 m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR,
234 "Failed to allocate memory during test "
235 "execution");
236 }
237 catch (const TestException &e)
238 {
239 log << e;
240 m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
241 m_testCtx.setTerminateAfter(e.isFatal());
242 }
243 catch (const Exception &e)
244 {
245 log << e;
246 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
247 }
248
249 if (iterateResult == TestCase::STOP)
250 break;
251 }
252
253 DE_ASSERT(m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
254
255 if (m_testCtx.getTestResult() == QP_TEST_RESULT_RESOURCE_ERROR)
256 m_testCtx.setTerminateAfter(true);
257
258 // De-initialize
259 try
260 {
261 m_caseExecutor->deinit(testCase);
262 }
263 catch (const tcu::Exception &e)
264 {
265 log << e << TestLog::Message
266 << "Error in test case deinit, test program "
267 "will terminate."
268 << TestLog::EndMessage;
269 m_testCtx.setTerminateAfter(true);
270 }
271
272 if (m_testCtx.getWatchDog())
273 qpWatchDog_reset(m_testCtx.getWatchDog());
274
275 {
276 const TestStatus result =
277 TestStatus(m_testCtx.getTestResult(), m_testCtx.getTestResultDesc());
278 m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
279 return result;
280 }
281 }
282
283 } // namespace tcu
284