• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	, m_packageStartTime	(0)
59 {
60 }
61 
~TestSessionExecutor(void)62 TestSessionExecutor::~TestSessionExecutor (void)
63 {
64 }
65 
iterate(void)66 bool TestSessionExecutor::iterate (void)
67 {
68 	while (!m_abortSession)
69 	{
70 		switch (m_state)
71 		{
72 			case STATE_TRAVERSE_HIERARCHY:
73 			{
74 				const TestHierarchyIterator::State	hierIterState	= m_iterator.getState();
75 
76 				if (hierIterState == TestHierarchyIterator::STATE_ENTER_NODE ||
77 					hierIterState == TestHierarchyIterator::STATE_LEAVE_NODE)
78 				{
79 					TestNode* const		curNode		= m_iterator.getNode();
80 					const TestNodeType	nodeType	= curNode->getNodeType();
81 					const bool			isEnter		= hierIterState == TestHierarchyIterator::STATE_ENTER_NODE;
82 
83 					switch (nodeType)
84 					{
85 						case NODETYPE_PACKAGE:
86 						{
87 							TestPackage* const testPackage = static_cast<TestPackage*>(curNode);
88 							isEnter ? enterTestPackage(testPackage) : leaveTestPackage(testPackage);
89 							break;
90 						}
91 
92 						case NODETYPE_GROUP:
93 						{
94 							isEnter ? enterTestGroup(m_iterator.getNodePath()) : leaveTestGroup(m_iterator.getNodePath());
95 							break; // nada
96 						}
97 
98 						case NODETYPE_SELF_VALIDATE:
99 						case NODETYPE_PERFORMANCE:
100 						case NODETYPE_CAPABILITY:
101 						case NODETYPE_ACCURACY:
102 						{
103 							TestCase* const testCase = static_cast<TestCase*>(curNode);
104 
105 							if (isEnter)
106 							{
107 								if (enterTestCase(testCase, m_iterator.getNodePath()))
108 									m_state = STATE_EXECUTE_TEST_CASE;
109 								// else remain in TRAVERSING_HIERARCHY => node will be exited from in the next iteration
110 							}
111 							else
112 								leaveTestCase(testCase);
113 
114 							break;
115 						}
116 
117 						default:
118 							DE_ASSERT(false);
119 							break;
120 					}
121 
122 					m_iterator.next();
123 					break;
124 				}
125 				else
126 				{
127 					DE_ASSERT(hierIterState == TestHierarchyIterator::STATE_FINISHED);
128 					m_status.isComplete = true;
129 					return false;
130 				}
131 			}
132 
133 			case STATE_EXECUTE_TEST_CASE:
134 			{
135 				DE_ASSERT(m_iterator.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
136 						  isTestNodeTypeExecutable(m_iterator.getNode()->getNodeType()));
137 
138 				TestCase* const					testCase	= static_cast<TestCase*>(m_iterator.getNode());
139 				const TestCase::IterateResult	iterResult	= iterateTestCase(testCase);
140 
141 				if (iterResult == TestCase::STOP)
142 					m_state = STATE_TRAVERSE_HIERARCHY;
143 
144 				return true;
145 			}
146 
147 			default:
148 				DE_ASSERT(false);
149 				break;
150 		}
151 	}
152 
153 	return false;
154 }
155 
enterTestPackage(TestPackage * testPackage)156 void TestSessionExecutor::enterTestPackage (TestPackage* testPackage)
157 {
158 	// Create test case wrapper
159 	DE_ASSERT(!m_caseExecutor);
160 	m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
161 	m_packageStartTime	= deGetMicroseconds();
162 }
163 
leaveTestPackage(TestPackage * testPackage)164 void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage)
165 {
166 	DE_UNREF(testPackage);
167 	m_caseExecutor->deinitTestPackage(m_testCtx);
168 	// If m_caseExecutor uses local status then it may perform some tests in deinitTestPackage(). We have to update TestSessionExecutor::m_status
169 	if (m_caseExecutor->usesLocalStatus())
170 		m_caseExecutor->updateGlobalStatus(m_status);
171 
172 	const deInt64 duration	= deGetMicroseconds() - m_packageStartTime;
173 	m_packageStartTime		= 0;
174 
175 	if (!std::string(m_testCtx.getCommandLine().getServerAddress()).empty())
176 		m_caseExecutor->reportDurations(m_testCtx, std::string(testPackage->getName()), duration, m_groupsDurationTime);
177 
178 	m_caseExecutor.clear();
179 
180 	if (!std::string(m_testCtx.getCommandLine().getServerAddress()).empty())
181 	{
182 		m_testCtx.getLog().startTestsCasesTime();
183 
184 		m_testCtx.getLog() << TestLog::Integer(testPackage->getName(), "Total tests case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
185 
186 		for (std::map<std::string, deUint64>::iterator it = m_groupsDurationTime.begin(); it != m_groupsDurationTime.end(); ++it)
187 			m_testCtx.getLog() << TestLog::Integer(it->first, "The test group case duration in microseconds", "us", QP_KEY_TAG_TIME, it->second);
188 
189 		m_testCtx.getLog().endTestsCasesTime();
190 	}
191 }
192 
enterTestGroup(const std::string & casePath)193 void TestSessionExecutor::enterTestGroup (const std::string& casePath)
194 {
195 	m_groupsDurationTime[casePath] = deGetMicroseconds();
196 }
197 
leaveTestGroup(const std::string & casePath)198 void TestSessionExecutor::leaveTestGroup (const std::string& casePath)
199 {
200 	m_groupsDurationTime[casePath] = deGetMicroseconds() - m_groupsDurationTime[casePath];
201 }
202 
enterTestCase(TestCase * testCase,const std::string & casePath)203 bool TestSessionExecutor::enterTestCase (TestCase* testCase, const std::string& casePath)
204 {
205 	TestLog&				log			= m_testCtx.getLog();
206 	const qpTestCaseType	caseType	= nodeTypeToTestCaseType(testCase->getNodeType());
207 	bool					initOk		= false;
208 
209 	print("\nTest case '%s'..\n", casePath.c_str());
210 
211 #if (DE_OS == DE_OS_WIN32)
212 	fflush(stdout);
213 #endif
214 
215 	m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
216 	m_testCtx.setTerminateAfter(false);
217 	log.startCase(casePath.c_str(), caseType);
218 
219 	m_isInTestCase	= true;
220 	m_testStartTime	= deGetMicroseconds();
221 
222 	try
223 	{
224 		m_caseExecutor->init(testCase, casePath);
225 		initOk = true;
226 	}
227 	catch (const std::bad_alloc&)
228 	{
229 		DE_ASSERT(!initOk);
230 		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init");
231 		m_testCtx.setTerminateAfter(true);
232 	}
233 	catch (const tcu::TestException& e)
234 	{
235 		DE_ASSERT(!initOk);
236 		DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
237 		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
238 		m_testCtx.setTerminateAfter(e.isFatal());
239 		log << e;
240 	}
241 	catch (const tcu::Exception& e)
242 	{
243 		DE_ASSERT(!initOk);
244 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
245 		log << e;
246 	}
247 
248 	DE_ASSERT(initOk || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
249 
250 	return initOk;
251 }
252 
leaveTestCase(TestCase * testCase)253 void TestSessionExecutor::leaveTestCase (TestCase* testCase)
254 {
255 	TestLog&	log		= m_testCtx.getLog();
256 
257 	// De-init case.
258 	try
259 	{
260 		m_caseExecutor->deinit(testCase);
261 	}
262 	catch (const tcu::Exception& e)
263 	{
264 		const bool suppressLogging = m_testCtx.getLog().isSupressLogging();
265 
266 		if (suppressLogging)
267 			m_testCtx.getLog().supressLogging(false);
268 
269 		log << e << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage;
270 		m_testCtx.setTerminateAfter(true);
271 
272 		m_testCtx.getLog().supressLogging(suppressLogging);
273 	}
274 
275 	{
276 		const deInt64 duration = deGetMicroseconds()-m_testStartTime;
277 		m_testStartTime = 0;
278 		m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
279 	}
280 
281 	{
282 		const qpTestResult	testResult		= m_testCtx.getTestResult();
283 		const char* const	testResultDesc	= m_testCtx.getTestResultDesc();
284 		const bool			terminateAfter	= m_testCtx.getTerminateAfter();
285 		DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
286 
287 		m_isInTestCase = false;
288 		m_testCtx.getLog().endCase(testResult, testResultDesc);
289 
290 		// Update statistics.
291 		print("  %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
292 
293 #if (DE_OS == DE_OS_WIN32)
294 		fflush(stdout);
295 #endif
296 		if(!m_caseExecutor->usesLocalStatus())
297 		{
298 			m_status.numExecuted += 1;
299 			switch (testResult)
300 			{
301 				case QP_TEST_RESULT_PASS:					m_status.numPassed			+= 1;	break;
302 				case QP_TEST_RESULT_NOT_SUPPORTED:			m_status.numNotSupported	+= 1;	break;
303 				case QP_TEST_RESULT_QUALITY_WARNING:		m_status.numWarnings		+= 1;	break;
304 				case QP_TEST_RESULT_COMPATIBILITY_WARNING:	m_status.numWarnings		+= 1;	break;
305 				case QP_TEST_RESULT_WAIVER:					m_status.numWaived			+= 1;	break;
306 				default:									m_status.numFailed			+= 1;	break;
307 			}
308 		}
309 		else
310 		{
311 			m_caseExecutor->updateGlobalStatus(m_status);
312 		}
313 
314 		// terminateAfter, Resource error or any error in deinit means that execution should end
315 		if (terminateAfter || testResult == QP_TEST_RESULT_RESOURCE_ERROR ||
316 			(m_status.numFailed > 0 && m_testCtx.getCommandLine().isTerminateOnFailEnabled()))
317 
318 			m_abortSession = true;
319 	}
320 
321 	if (m_testCtx.getWatchDog())
322 		qpWatchDog_reset(m_testCtx.getWatchDog());
323 }
324 
iterateTestCase(TestCase * testCase)325 TestCase::IterateResult TestSessionExecutor::iterateTestCase (TestCase* testCase)
326 {
327 	TestLog&				log				= m_testCtx.getLog();
328 	TestCase::IterateResult	iterateResult	= TestCase::STOP;
329 
330 	m_testCtx.touchWatchdog();
331 
332 	try
333 	{
334 		iterateResult = m_caseExecutor->iterate(testCase);
335 	}
336 	catch (const std::bad_alloc&)
337 	{
338 		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution");
339 		m_testCtx.setTerminateAfter(true);
340 	}
341 	catch (const tcu::TestException& e)
342 	{
343 		log << e;
344 		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
345 		m_testCtx.setTerminateAfter(e.isFatal());
346 	}
347 	catch (const tcu::Exception& e)
348 	{
349 		log << e;
350 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
351 	}
352 
353 	return iterateResult;
354 }
355 
356 } // tcu
357