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)
51{
52	DefaultHierarchyInflater			inflater		(testCtx);
53	de::MovePtr<const CaseListFilter>	caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
54	TestHierarchyIterator				iter			(root, inflater, *caseListFilter);
55
56	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
57	{
58		iter.next();
59
60		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
61		{
62			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE)
63				std::cout << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n";
64			iter.next();
65		}
66
67		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
68				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
69		iter.next();
70	}
71}
72
73
74/*--------------------------------------------------------------------*//*!
75 * Verifies that amber capability requirements in the .amber files
76 * match with capabilities defined on the CTS C code.
77 *//*--------------------------------------------------------------------*/
78static void verifyAmberCapabilityCoherency (TestPackageRoot& root, TestContext& testCtx)
79{
80	DefaultHierarchyInflater			inflater(testCtx);
81	de::MovePtr<const CaseListFilter>	caseListFilter(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
82	TestHierarchyIterator				iter(root, inflater, *caseListFilter);
83	int									count = 0;
84	int									errorCount = 0;
85
86	bool ok = true;
87
88	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
89	{
90		iter.next();
91
92		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
93		{
94			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
95				isTestNodeTypeExecutable(iter.getNode()->getNodeType()))
96			{
97				std::cout << iter.getNodePath() << "\n";
98				testCtx.getLog() << tcu::TestLog::Message << iter.getNodePath() << tcu::TestLog::EndMessage;
99				if (!iter.getNode()->validateRequirements())
100				{
101					ok = false;
102					errorCount++;
103				}
104				count++;
105			}
106			iter.next();
107		}
108
109		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
110			iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
111		iter.next();
112	}
113	std::cout << count << " amber tests, " << errorCount << " errors.\n";
114	if (!ok)
115		TCU_THROW(InternalError, "One or more CTS and Amber test requirements do not match; check log for details");
116
117}
118
119/*--------------------------------------------------------------------*//*!
120 * \brief Construct test application
121 *
122 * If a fatal error occurs during initialization constructor will call
123 * die() with debug information.
124 *
125 * \param platform Reference to platform implementation.
126 *//*--------------------------------------------------------------------*/
127App::App (Platform& platform, Archive& archive, TestLog& log, const CommandLine& cmdLine)
128	: m_platform		(platform)
129	, m_watchDog		(DE_NULL)
130	, m_crashHandler	(DE_NULL)
131	, m_crashed			(false)
132	, m_testCtx			(DE_NULL)
133	, m_testRoot		(DE_NULL)
134	, m_testExecutor	(DE_NULL)
135{
136	if (!cmdLine.isSubProcess())
137	{
138		print("dEQP Core %s (0x%08x) starting..\n", qpGetReleaseName(), qpGetReleaseId());
139		print("  target implementation = '%s'\n", qpGetTargetName());
140	}
141
142	if (!deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST_EVEN))
143		qpPrintf("WARNING: Failed to set floating-point rounding mode!\n");
144
145	try
146	{
147		const RunMode	runMode	= cmdLine.getRunMode();
148
149		// Initialize watchdog
150		if (cmdLine.isWatchDogEnabled())
151			TCU_CHECK_INTERNAL(m_watchDog = qpWatchDog_create(onWatchdogTimeout, this, WATCHDOG_TOTAL_TIME_LIMIT_SECS, WATCHDOG_INTERVAL_TIME_LIMIT_SECS));
152
153		// Initialize crash handler.
154		if (cmdLine.isCrashHandlingEnabled())
155			TCU_CHECK_INTERNAL(m_crashHandler = qpCrashHandler_create(onCrash, this));
156
157		// Create test context
158		m_testCtx = new TestContext(m_platform, archive, log, cmdLine, m_watchDog);
159
160		// Create root from registry
161		m_testRoot = new TestPackageRoot(*m_testCtx, TestPackageRegistry::getSingleton());
162
163		// \note No executor is created if runmode is not EXECUTE
164		if (runMode == RUNMODE_EXECUTE)
165			m_testExecutor = new TestSessionExecutor(*m_testRoot, *m_testCtx);
166		else if (runMode == RUNMODE_DUMP_STDOUT_CASELIST)
167			writeCaselistsToStdout(*m_testRoot, *m_testCtx);
168		else if (runMode == RUNMODE_DUMP_XML_CASELIST)
169			writeXmlCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine);
170		else if (runMode == RUNMODE_DUMP_TEXT_CASELIST)
171			writeTxtCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine);
172		else if (runMode == RUNMODE_VERIFY_AMBER_COHERENCY)
173			verifyAmberCapabilityCoherency(*m_testRoot, *m_testCtx);
174		else
175			DE_ASSERT(false);
176	}
177	catch (const std::exception& e)
178	{
179		cleanup();
180		die("Failed to initialize dEQP: %s", e.what());
181	}
182}
183
184App::~App (void)
185{
186	cleanup();
187}
188
189void App::cleanup (void)
190{
191	delete m_testExecutor;
192	delete m_testRoot;
193	delete m_testCtx;
194
195	if (m_crashHandler)
196		qpCrashHandler_destroy(m_crashHandler);
197
198	if (m_watchDog)
199		qpWatchDog_destroy(m_watchDog);
200}
201
202/*--------------------------------------------------------------------*//*!
203 * \brief Step forward test execution
204 * \return true if application should call iterate() again and false
205 *         if test execution session is complete.
206 *//*--------------------------------------------------------------------*/
207bool App::iterate (void)
208{
209	if (!m_testExecutor)
210	{
211		DE_ASSERT(m_testCtx->getCommandLine().getRunMode() != RUNMODE_EXECUTE);
212		return false;
213	}
214
215	// Poll platform events
216	const bool platformOk = m_platform.processEvents();
217
218	// Iterate a step.
219	bool testExecOk = false;
220	if (platformOk)
221	{
222		try
223		{
224			testExecOk = m_testExecutor->iterate();
225		}
226		catch (const std::exception& e)
227		{
228			die("%s", e.what());
229		}
230	}
231
232	if ((!platformOk || !testExecOk) )
233	{
234		if (!m_testCtx->getCommandLine().isSubProcess())
235		{
236			if (!platformOk)
237				print("\nABORTED!\n");
238			else
239				print("\nDONE!\n");
240		}
241
242		const RunMode runMode = m_testCtx->getCommandLine().getRunMode();
243		if (runMode == RUNMODE_EXECUTE)
244		{
245			const TestRunStatus& result = m_testExecutor->getStatus();
246			if(!m_testCtx->getCommandLine().isSubProcess())
247			{
248				// Report statistics.
249				print("\nTest run totals:\n");
250				print("  Passed:        %d/%d (%.1f%%)\n", result.numPassed,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numPassed			/ (float)result.numExecuted) : 0.0f));
251				print("  Failed:        %d/%d (%.1f%%)\n", result.numFailed,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numFailed			/ (float)result.numExecuted) : 0.0f));
252				print("  Not supported: %d/%d (%.1f%%)\n", result.numNotSupported,	result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numNotSupported	/ (float)result.numExecuted) : 0.0f));
253				print("  Warnings:      %d/%d (%.1f%%)\n", result.numWarnings,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWarnings		/ (float)result.numExecuted) : 0.0f));
254				print("  Waived:        %d/%d (%.1f%%)\n", result.numWaived,		result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWaived			/ (float)result.numExecuted) : 0.0f));
255				if (!result.isComplete)
256					print("Test run was ABORTED!\n");
257			}
258			else
259			{
260				// subprocess sends test statisticts through qpa file, so that main process may read it
261				// and add to global statistics ( search for #SubProcessStatus to see how it's done )
262				std::ostringstream str;
263				str << "\n#SubProcessStatus " <<
264					result.numExecuted		<< " " <<
265					result.numPassed		<< " " <<
266					result.numFailed		<< " " <<
267					result.numNotSupported	<< " " <<
268					result.numWarnings		<< " " <<
269					result.numWaived		<< "\n";
270				m_testCtx->getLog().writeRaw(str.str().c_str());
271			}
272		}
273	}
274
275	return platformOk && testExecOk;
276}
277
278const TestRunStatus& App::getResult (void) const
279{
280	return m_testExecutor->getStatus();
281}
282
283void App::onWatchdogTimeout (qpWatchDog* watchDog, void* userPtr, qpTimeoutReason reason)
284{
285	DE_UNREF(watchDog);
286	static_cast<App*>(userPtr)->onWatchdogTimeout(reason);
287}
288
289void App::onCrash (qpCrashHandler* crashHandler, void* userPtr)
290{
291	DE_UNREF(crashHandler);
292	static_cast<App*>(userPtr)->onCrash();
293}
294
295void App::onWatchdogTimeout (qpTimeoutReason reason)
296{
297	if (!m_crashLock.tryLock() || m_crashed)
298		return; // In crash handler already.
299
300	m_crashed = true;
301
302	m_testCtx->getLog().terminateCase(QP_TEST_RESULT_TIMEOUT);
303	die("Watchdog timer timeout for %s", (reason == QP_TIMEOUT_REASON_INTERVAL_LIMIT ? "touch interval" : "total time"));
304}
305
306static void writeCrashToLog (void* userPtr, const char* infoString)
307{
308	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
309	TestLog* log = static_cast<TestLog*>(userPtr);
310	log->writeMessage(infoString);
311}
312
313static void writeCrashToConsole (void* userPtr, const char* infoString)
314{
315	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
316	DE_UNREF(userPtr);
317	qpPrint(infoString);
318}
319
320void App::onCrash (void)
321{
322	// \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED!
323
324	if (!m_crashLock.tryLock() || m_crashed)
325		return; // In crash handler already.
326
327	m_crashed = true;
328
329	bool isInCase = m_testExecutor ? m_testExecutor->isInTestCase() : false;
330
331	if (isInCase)
332	{
333		qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToLog, &m_testCtx->getLog());
334		m_testCtx->getLog().terminateCase(QP_TEST_RESULT_CRASH);
335	}
336	else
337		qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToConsole, DE_NULL);
338
339	die("Test program crashed");
340}
341
342} // tcu
343