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