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 Render target info.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuApp.hpp"
25#include "tcuPlatform.hpp"
26#include "tcuTestContext.hpp"
27#include "tcuTestSessionExecutor.hpp"
28#include "tcuTestHierarchyUtil.hpp"
29#include "tcuCommandLine.hpp"
30#include "tcuTestLog.hpp"
31
32#include "qpInfo.h"
33#include "qpDebugOut.h"
34
35#include "deMath.h"
36
37#include <iostream>
38
39namespace tcu
40{
41
42using std::string;
43
44/*--------------------------------------------------------------------*//*!
45 *  Writes all packages found stdout without any
46 *  separations. Recommended to be used with a single package
47 *  only. It's possible to use test selectors for limiting the export
48 *  to one package in a multipackage binary.
49 *//*--------------------------------------------------------------------*/
50static void writeCaselistsToStdout (TestPackageRoot& root, TestContext& testCtx, const CommandLine& cmdLine)
51{
52	DefaultHierarchyInflater	inflater	(testCtx);
53	TestHierarchyIterator		iter		(root, inflater, cmdLine);
54
55	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
56	{
57		iter.next();
58
59		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
60		{
61			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE)
62				std::cout << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n";
63			iter.next();
64		}
65
66		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
67				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
68		iter.next();
69	}
70}
71
72/*--------------------------------------------------------------------*//*!
73 * \brief Construct test application
74 *
75 * If a fatal error occurs during initialization constructor will call
76 * die() with debug information.
77 *
78 * \param platform Reference to platform implementation.
79 *//*--------------------------------------------------------------------*/
80App::App (Platform& platform, Archive& archive, TestLog& log, const CommandLine& cmdLine)
81	: m_platform		(platform)
82	, m_watchDog		(DE_NULL)
83	, m_crashHandler	(DE_NULL)
84	, m_crashed			(false)
85	, m_testCtx			(DE_NULL)
86	, m_testRoot		(DE_NULL)
87	, m_testExecutor	(DE_NULL)
88{
89	print("dEQP Core %s (0x%08x) starting..\n", qpGetReleaseName(), qpGetReleaseId());
90	print("  target implementation = '%s'\n", qpGetTargetName());
91
92	if (!deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST))
93		qpPrintf("WARNING: Failed to set floating-point rounding mode!\n");
94
95	try
96	{
97		const RunMode	runMode	= cmdLine.getRunMode();
98
99		// Initialize watchdog
100		if (cmdLine.isWatchDogEnabled())
101			TCU_CHECK_INTERNAL(m_watchDog = qpWatchDog_create(onWatchdogTimeout, this, 300, 30));
102
103		// Initialize crash handler.
104		if (cmdLine.isCrashHandlingEnabled())
105			TCU_CHECK_INTERNAL(m_crashHandler = qpCrashHandler_create(onCrash, this));
106
107		// Create test context
108		m_testCtx = new TestContext(m_platform, archive, log, cmdLine, m_watchDog);
109
110		// Create root from registry
111		m_testRoot = new TestPackageRoot(*m_testCtx, TestPackageRegistry::getSingleton());
112
113		// \note No executor is created if runmode is not EXECUTE
114		if (runMode == RUNMODE_EXECUTE)
115			m_testExecutor = new TestSessionExecutor(*m_testRoot, *m_testCtx);
116		else if (runMode == RUNMODE_DUMP_STDOUT_CASELIST)
117			writeCaselistsToStdout(*m_testRoot, *m_testCtx, cmdLine);
118		else if (runMode == RUNMODE_DUMP_XML_CASELIST)
119			writeXmlCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine);
120		else if (runMode == RUNMODE_DUMP_TEXT_CASELIST)
121			writeTxtCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine);
122		else
123			DE_ASSERT(false);
124	}
125	catch (const std::exception& e)
126	{
127		cleanup();
128		die("Failed to initialize dEQP: %s", e.what());
129	}
130}
131
132App::~App (void)
133{
134	cleanup();
135}
136
137void App::cleanup (void)
138{
139	delete m_testExecutor;
140	delete m_testRoot;
141	delete m_testCtx;
142
143	if (m_crashHandler)
144		qpCrashHandler_destroy(m_crashHandler);
145
146	if (m_watchDog)
147		qpWatchDog_destroy(m_watchDog);
148}
149
150/*--------------------------------------------------------------------*//*!
151 * \brief Step forward test execution
152 * \return true if application should call iterate() again and false
153 *         if test execution session is complete.
154 *//*--------------------------------------------------------------------*/
155bool App::iterate (void)
156{
157	if (!m_testExecutor)
158	{
159		DE_ASSERT(m_testCtx->getCommandLine().getRunMode() != RUNMODE_EXECUTE);
160		return false;
161	}
162
163	// Poll platform events
164	const bool platformOk = m_platform.processEvents();
165
166	// Iterate a step.
167	bool testExecOk = false;
168	if (platformOk)
169	{
170		try
171		{
172			testExecOk = m_testExecutor->iterate();
173		}
174		catch (const std::exception& e)
175		{
176			die("%s", e.what());
177		}
178	}
179
180	if (!platformOk || !testExecOk)
181	{
182		if (!platformOk)
183			print("\nABORTED!\n");
184		else
185			print("\nDONE!\n");
186
187		const RunMode runMode = m_testCtx->getCommandLine().getRunMode();
188		if (runMode == RUNMODE_EXECUTE)
189		{
190			const TestRunStatus& result = m_testExecutor->getStatus();
191
192			// Report statistics.
193			print("\nTest run totals:\n");
194			print("  Passed:        %d/%d (%.1f%%)\n", result.numPassed,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numPassed			/ (float)result.numExecuted) : 0.0f));
195			print("  Failed:        %d/%d (%.1f%%)\n", result.numFailed,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numFailed			/ (float)result.numExecuted) : 0.0f));
196			print("  Not supported: %d/%d (%.1f%%)\n", result.numNotSupported,	result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numNotSupported	/ (float)result.numExecuted) : 0.0f));
197			print("  Warnings:      %d/%d (%.1f%%)\n", result.numWarnings,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWarnings		/ (float)result.numExecuted) : 0.0f));
198			if (!result.isComplete)
199				print("Test run was ABORTED!\n");
200		}
201	}
202
203	return platformOk && testExecOk;
204}
205
206void App::onWatchdogTimeout (qpWatchDog* watchDog, void* userPtr)
207{
208	DE_UNREF(watchDog);
209	static_cast<App*>(userPtr)->onWatchdogTimeout();
210}
211
212void App::onCrash (qpCrashHandler* crashHandler, void* userPtr)
213{
214	DE_UNREF(crashHandler);
215	static_cast<App*>(userPtr)->onCrash();
216}
217
218void App::onWatchdogTimeout (void)
219{
220	if (!m_crashLock.tryLock() || m_crashed)
221		return; // In crash handler already.
222
223	m_crashed = true;
224
225	m_testCtx->getLog().terminateCase(QP_TEST_RESULT_TIMEOUT);
226	die("Watchdog timer timeout");
227}
228
229static void writeCrashToLog (void* userPtr, const char* infoString)
230{
231	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
232	TestLog* log = static_cast<TestLog*>(userPtr);
233	log->writeMessage(infoString);
234}
235
236static void writeCrashToConsole (void* userPtr, const char* infoString)
237{
238	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
239	DE_UNREF(userPtr);
240	qpPrint(infoString);
241}
242
243void App::onCrash (void)
244{
245	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
246
247	if (!m_crashLock.tryLock() || m_crashed)
248		return; // In crash handler already.
249
250	m_crashed = true;
251
252	bool isInCase = m_testExecutor ? m_testExecutor->isInTestCase() : false;
253
254	if (isInCase)
255	{
256		qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToLog, &m_testCtx->getLog());
257		m_testCtx->getLog().terminateCase(QP_TEST_RESULT_CRASH);
258	}
259	else
260		qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToConsole, DE_NULL);
261
262	die("Test program crashed");
263}
264
265} // tcu
266