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
29namespace tcu
30{
31
32using std::vector;
33
34static 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
48TestSessionExecutor::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
59TestSessionExecutor::~TestSessionExecutor (void)
60{
61}
62
63bool 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
150void 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
157void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage)
158{
159	DE_UNREF(testPackage);
160	m_caseExecutor.clear();
161}
162
163bool 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
209void 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
261TestCase::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