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 Test executor.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuTestSessionExecutor.hpp"
25 #include "tcuCommandLine.hpp"
26 #include "tcuTestLog.hpp"
27
28 #include "deClock.h"
29
30 namespace tcu
31 {
32
33 using std::vector;
34
nodeTypeToTestCaseType(TestNodeType nodeType)35 static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType)
36 {
37 switch (nodeType)
38 {
39 case NODETYPE_SELF_VALIDATE: return QP_TEST_CASE_TYPE_SELF_VALIDATE;
40 case NODETYPE_PERFORMANCE: return QP_TEST_CASE_TYPE_PERFORMANCE;
41 case NODETYPE_CAPABILITY: return QP_TEST_CASE_TYPE_CAPABILITY;
42 case NODETYPE_ACCURACY: return QP_TEST_CASE_TYPE_ACCURACY;
43 default:
44 DE_ASSERT(false);
45 return QP_TEST_CASE_TYPE_LAST;
46 }
47 }
48
TestSessionExecutor(TestPackageRoot & root,TestContext & testCtx)49 TestSessionExecutor::TestSessionExecutor (TestPackageRoot& root, TestContext& testCtx)
50 : m_testCtx (testCtx)
51 , m_inflater (testCtx)
52 , m_caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()))
53 , m_iterator (root, m_inflater, *m_caseListFilter)
54 , m_state (STATE_TRAVERSE_HIERARCHY)
55 , m_abortSession (false)
56 , m_isInTestCase (false)
57 , m_testStartTime (0)
58 {
59 }
60
~TestSessionExecutor(void)61 TestSessionExecutor::~TestSessionExecutor (void)
62 {
63 }
64
iterate(void)65 bool TestSessionExecutor::iterate (void)
66 {
67 while (!m_abortSession)
68 {
69 switch (m_state)
70 {
71 case STATE_TRAVERSE_HIERARCHY:
72 {
73 const TestHierarchyIterator::State hierIterState = m_iterator.getState();
74
75 if (hierIterState == TestHierarchyIterator::STATE_ENTER_NODE ||
76 hierIterState == TestHierarchyIterator::STATE_LEAVE_NODE)
77 {
78 TestNode* const curNode = m_iterator.getNode();
79 const TestNodeType nodeType = curNode->getNodeType();
80 const bool isEnter = hierIterState == TestHierarchyIterator::STATE_ENTER_NODE;
81
82 switch (nodeType)
83 {
84 case NODETYPE_PACKAGE:
85 {
86 TestPackage* const testPackage = static_cast<TestPackage*>(curNode);
87 isEnter ? enterTestPackage(testPackage) : leaveTestPackage(testPackage);
88 break;
89 }
90
91 case NODETYPE_GROUP:
92 break; // nada
93
94 case NODETYPE_SELF_VALIDATE:
95 case NODETYPE_PERFORMANCE:
96 case NODETYPE_CAPABILITY:
97 case NODETYPE_ACCURACY:
98 {
99 TestCase* const testCase = static_cast<TestCase*>(curNode);
100
101 if (isEnter)
102 {
103 if (enterTestCase(testCase, m_iterator.getNodePath()))
104 m_state = STATE_EXECUTE_TEST_CASE;
105 // else remain in TRAVERSING_HIERARCHY => node will be exited from in the next iteration
106 }
107 else
108 leaveTestCase(testCase);
109
110 break;
111 }
112
113 default:
114 DE_ASSERT(false);
115 break;
116 }
117
118 m_iterator.next();
119 break;
120 }
121 else
122 {
123 DE_ASSERT(hierIterState == TestHierarchyIterator::STATE_FINISHED);
124 m_status.isComplete = true;
125 return false;
126 }
127 }
128
129 case STATE_EXECUTE_TEST_CASE:
130 {
131 DE_ASSERT(m_iterator.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
132 isTestNodeTypeExecutable(m_iterator.getNode()->getNodeType()));
133
134 TestCase* const testCase = static_cast<TestCase*>(m_iterator.getNode());
135 const TestCase::IterateResult iterResult = iterateTestCase(testCase);
136
137 if (iterResult == TestCase::STOP)
138 m_state = STATE_TRAVERSE_HIERARCHY;
139
140 return true;
141 }
142
143 default:
144 DE_ASSERT(false);
145 break;
146 }
147 }
148
149 return false;
150 }
151
enterTestPackage(TestPackage * testPackage)152 void TestSessionExecutor::enterTestPackage (TestPackage* testPackage)
153 {
154 // Create test case wrapper
155 DE_ASSERT(!m_caseExecutor);
156 m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
157 }
158
leaveTestPackage(TestPackage * testPackage)159 void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage)
160 {
161 DE_UNREF(testPackage);
162 m_caseExecutor.clear();
163 }
164
enterTestCase(TestCase * testCase,const std::string & casePath)165 bool TestSessionExecutor::enterTestCase (TestCase* testCase, const std::string& casePath)
166 {
167 TestLog& log = m_testCtx.getLog();
168 const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType());
169 bool initOk = false;
170
171 print("\nTest case '%s'..\n", casePath.c_str());
172
173 m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
174 m_testCtx.setTerminateAfter(false);
175 log.startCase(casePath.c_str(), caseType);
176
177 m_isInTestCase = true;
178 m_testStartTime = deGetMicroseconds();
179
180 try
181 {
182 m_caseExecutor->init(testCase, casePath);
183 initOk = true;
184 }
185 catch (const std::bad_alloc&)
186 {
187 DE_ASSERT(!initOk);
188 m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init");
189 m_testCtx.setTerminateAfter(true);
190 }
191 catch (const tcu::TestException& e)
192 {
193 DE_ASSERT(!initOk);
194 DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
195 m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
196 m_testCtx.setTerminateAfter(e.isFatal());
197 log << e;
198 }
199 catch (const tcu::Exception& e)
200 {
201 DE_ASSERT(!initOk);
202 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
203 log << e;
204 }
205
206 DE_ASSERT(initOk || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
207
208 return initOk;
209 }
210
leaveTestCase(TestCase * testCase)211 void TestSessionExecutor::leaveTestCase (TestCase* testCase)
212 {
213 TestLog& log = m_testCtx.getLog();
214
215 // De-init case.
216 try
217 {
218 m_caseExecutor->deinit(testCase);
219 }
220 catch (const tcu::Exception& e)
221 {
222 log << e << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage;
223 m_testCtx.setTerminateAfter(true);
224 }
225
226 {
227 const deInt64 duration = deGetMicroseconds()-m_testStartTime;
228 m_testStartTime = 0;
229 m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
230 }
231
232 {
233 const qpTestResult testResult = m_testCtx.getTestResult();
234 const char* const testResultDesc = m_testCtx.getTestResultDesc();
235 const bool terminateAfter = m_testCtx.getTerminateAfter();
236 DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
237
238 m_isInTestCase = false;
239 m_testCtx.getLog().endCase(testResult, testResultDesc);
240
241 // Update statistics.
242 print(" %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
243
244 m_status.numExecuted += 1;
245 switch (testResult)
246 {
247 case QP_TEST_RESULT_PASS: m_status.numPassed += 1; break;
248 case QP_TEST_RESULT_NOT_SUPPORTED: m_status.numNotSupported += 1; break;
249 case QP_TEST_RESULT_QUALITY_WARNING: m_status.numWarnings += 1; break;
250 case QP_TEST_RESULT_COMPATIBILITY_WARNING: m_status.numWarnings += 1; break;
251 default: m_status.numFailed += 1; break;
252 }
253
254 // terminateAfter, Resource error or any error in deinit means that execution should end
255 if (terminateAfter || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
256 m_abortSession = true;
257 }
258
259 if (m_testCtx.getWatchDog())
260 qpWatchDog_reset(m_testCtx.getWatchDog());
261 }
262
iterateTestCase(TestCase * testCase)263 TestCase::IterateResult TestSessionExecutor::iterateTestCase (TestCase* testCase)
264 {
265 TestLog& log = m_testCtx.getLog();
266 TestCase::IterateResult iterateResult = TestCase::STOP;
267
268 m_testCtx.touchWatchdog();
269
270 try
271 {
272 iterateResult = m_caseExecutor->iterate(testCase);
273 }
274 catch (const std::bad_alloc&)
275 {
276 m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution");
277 m_testCtx.setTerminateAfter(true);
278 }
279 catch (const tcu::TestException& e)
280 {
281 log << e;
282 m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
283 m_testCtx.setTerminateAfter(e.isFatal());
284 }
285 catch (const tcu::Exception& e)
286 {
287 log << e;
288 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
289 }
290
291 return iterateResult;
292 }
293
294 } // tcu
295