You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
9.0 KiB
323 lines
9.0 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Tester Core
|
|
* ----------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Test executor.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "tcuTestSessionExecutor.hpp"
|
|
#include "tcuCommandLine.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
|
|
#include "deClock.h"
|
|
|
|
namespace tcu
|
|
{
|
|
|
|
using std::vector;
|
|
|
|
static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType)
|
|
{
|
|
switch (nodeType)
|
|
{
|
|
case NODETYPE_SELF_VALIDATE: return QP_TEST_CASE_TYPE_SELF_VALIDATE;
|
|
case NODETYPE_PERFORMANCE: return QP_TEST_CASE_TYPE_PERFORMANCE;
|
|
case NODETYPE_CAPABILITY: return QP_TEST_CASE_TYPE_CAPABILITY;
|
|
case NODETYPE_ACCURACY: return QP_TEST_CASE_TYPE_ACCURACY;
|
|
default:
|
|
DE_ASSERT(false);
|
|
return QP_TEST_CASE_TYPE_LAST;
|
|
}
|
|
}
|
|
|
|
TestSessionExecutor::TestSessionExecutor (TestPackageRoot& root, TestContext& testCtx)
|
|
: m_testCtx (testCtx)
|
|
, m_inflater (testCtx)
|
|
, m_caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()))
|
|
, m_iterator (root, m_inflater, *m_caseListFilter)
|
|
, m_state (STATE_TRAVERSE_HIERARCHY)
|
|
, m_abortSession (false)
|
|
, m_isInTestCase (false)
|
|
, m_testStartTime (0)
|
|
, m_packageStartTime (0)
|
|
{
|
|
}
|
|
|
|
TestSessionExecutor::~TestSessionExecutor (void)
|
|
{
|
|
}
|
|
|
|
bool TestSessionExecutor::iterate (void)
|
|
{
|
|
while (!m_abortSession)
|
|
{
|
|
switch (m_state)
|
|
{
|
|
case STATE_TRAVERSE_HIERARCHY:
|
|
{
|
|
const TestHierarchyIterator::State hierIterState = m_iterator.getState();
|
|
|
|
if (hierIterState == TestHierarchyIterator::STATE_ENTER_NODE ||
|
|
hierIterState == TestHierarchyIterator::STATE_LEAVE_NODE)
|
|
{
|
|
TestNode* const curNode = m_iterator.getNode();
|
|
const TestNodeType nodeType = curNode->getNodeType();
|
|
const bool isEnter = hierIterState == TestHierarchyIterator::STATE_ENTER_NODE;
|
|
|
|
switch (nodeType)
|
|
{
|
|
case NODETYPE_PACKAGE:
|
|
{
|
|
TestPackage* const testPackage = static_cast<TestPackage*>(curNode);
|
|
isEnter ? enterTestPackage(testPackage) : leaveTestPackage(testPackage);
|
|
break;
|
|
}
|
|
|
|
case NODETYPE_GROUP:
|
|
{
|
|
isEnter ? enterTestGroup(m_iterator.getNodePath()) : leaveTestGroup(m_iterator.getNodePath());
|
|
break; // nada
|
|
}
|
|
|
|
case NODETYPE_SELF_VALIDATE:
|
|
case NODETYPE_PERFORMANCE:
|
|
case NODETYPE_CAPABILITY:
|
|
case NODETYPE_ACCURACY:
|
|
{
|
|
TestCase* const testCase = static_cast<TestCase*>(curNode);
|
|
|
|
if (isEnter)
|
|
{
|
|
if (enterTestCase(testCase, m_iterator.getNodePath()))
|
|
m_state = STATE_EXECUTE_TEST_CASE;
|
|
// else remain in TRAVERSING_HIERARCHY => node will be exited from in the next iteration
|
|
}
|
|
else
|
|
leaveTestCase(testCase);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
m_iterator.next();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(hierIterState == TestHierarchyIterator::STATE_FINISHED);
|
|
m_status.isComplete = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
case STATE_EXECUTE_TEST_CASE:
|
|
{
|
|
DE_ASSERT(m_iterator.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
|
|
isTestNodeTypeExecutable(m_iterator.getNode()->getNodeType()));
|
|
|
|
TestCase* const testCase = static_cast<TestCase*>(m_iterator.getNode());
|
|
const TestCase::IterateResult iterResult = iterateTestCase(testCase);
|
|
|
|
if (iterResult == TestCase::STOP)
|
|
m_state = STATE_TRAVERSE_HIERARCHY;
|
|
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TestSessionExecutor::enterTestPackage (TestPackage* testPackage)
|
|
{
|
|
// Create test case wrapper
|
|
DE_ASSERT(!m_caseExecutor);
|
|
m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
|
|
m_packageStartTime = deGetMicroseconds();
|
|
}
|
|
|
|
void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage)
|
|
{
|
|
DE_UNREF(testPackage);
|
|
m_caseExecutor.clear();
|
|
m_testCtx.getLog().startTestsCasesTime();
|
|
|
|
{
|
|
const deInt64 duration = deGetMicroseconds() - m_packageStartTime;
|
|
m_packageStartTime = 0;
|
|
m_testCtx.getLog() << TestLog::Integer(testPackage->getName(), "Total tests case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
|
|
}
|
|
|
|
for(std::map<std::string, deUint64>::iterator it=m_groupsDurationTime.begin(); it != m_groupsDurationTime.end(); ++it)
|
|
m_testCtx.getLog() << TestLog::Integer(it->first, "The test group case duration in microseconds", "us", QP_KEY_TAG_TIME, it->second);
|
|
|
|
m_testCtx.getLog().endTestsCasesTime();
|
|
}
|
|
|
|
void TestSessionExecutor::enterTestGroup (const std::string& casePath)
|
|
{
|
|
m_groupsDurationTime[casePath] = deGetMicroseconds();
|
|
}
|
|
|
|
void TestSessionExecutor::leaveTestGroup (const std::string& casePath)
|
|
{
|
|
m_groupsDurationTime[casePath] = deGetMicroseconds() - m_groupsDurationTime[casePath];
|
|
}
|
|
|
|
bool TestSessionExecutor::enterTestCase (TestCase* testCase, const std::string& casePath)
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType());
|
|
bool initOk = false;
|
|
|
|
print("\nTest case '%s'..\n", casePath.c_str());
|
|
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
|
|
m_testCtx.setTerminateAfter(false);
|
|
log.startCase(casePath.c_str(), caseType);
|
|
|
|
m_isInTestCase = true;
|
|
m_testStartTime = deGetMicroseconds();
|
|
|
|
try
|
|
{
|
|
m_caseExecutor->init(testCase, casePath);
|
|
initOk = true;
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
DE_ASSERT(!initOk);
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init");
|
|
m_testCtx.setTerminateAfter(true);
|
|
}
|
|
catch (const tcu::TestException& e)
|
|
{
|
|
DE_ASSERT(!initOk);
|
|
DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
|
|
m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
|
|
m_testCtx.setTerminateAfter(e.isFatal());
|
|
log << e;
|
|
}
|
|
catch (const tcu::Exception& e)
|
|
{
|
|
DE_ASSERT(!initOk);
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
|
|
log << e;
|
|
}
|
|
|
|
DE_ASSERT(initOk || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
|
|
|
|
return initOk;
|
|
}
|
|
|
|
void TestSessionExecutor::leaveTestCase (TestCase* testCase)
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
|
|
// De-init case.
|
|
try
|
|
{
|
|
m_caseExecutor->deinit(testCase);
|
|
}
|
|
catch (const tcu::Exception& e)
|
|
{
|
|
log << e << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage;
|
|
m_testCtx.setTerminateAfter(true);
|
|
}
|
|
|
|
{
|
|
const deInt64 duration = deGetMicroseconds()-m_testStartTime;
|
|
m_testStartTime = 0;
|
|
m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
|
|
}
|
|
|
|
{
|
|
const qpTestResult testResult = m_testCtx.getTestResult();
|
|
const char* const testResultDesc = m_testCtx.getTestResultDesc();
|
|
const bool terminateAfter = m_testCtx.getTerminateAfter();
|
|
DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
|
|
|
|
m_isInTestCase = false;
|
|
m_testCtx.getLog().endCase(testResult, testResultDesc);
|
|
|
|
// Update statistics.
|
|
print(" %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
|
|
|
|
m_status.numExecuted += 1;
|
|
switch (testResult)
|
|
{
|
|
case QP_TEST_RESULT_PASS: m_status.numPassed += 1; break;
|
|
case QP_TEST_RESULT_NOT_SUPPORTED: m_status.numNotSupported += 1; break;
|
|
case QP_TEST_RESULT_QUALITY_WARNING: m_status.numWarnings += 1; break;
|
|
case QP_TEST_RESULT_COMPATIBILITY_WARNING: m_status.numWarnings += 1; break;
|
|
case QP_TEST_RESULT_WAIVER: m_status.numWaived += 1; break;
|
|
default: m_status.numFailed += 1; break;
|
|
}
|
|
|
|
// terminateAfter, Resource error or any error in deinit means that execution should end
|
|
if (terminateAfter || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
|
|
m_abortSession = true;
|
|
}
|
|
|
|
if (m_testCtx.getWatchDog())
|
|
qpWatchDog_reset(m_testCtx.getWatchDog());
|
|
}
|
|
|
|
TestCase::IterateResult TestSessionExecutor::iterateTestCase (TestCase* testCase)
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
TestCase::IterateResult iterateResult = TestCase::STOP;
|
|
|
|
m_testCtx.touchWatchdog();
|
|
|
|
try
|
|
{
|
|
iterateResult = m_caseExecutor->iterate(testCase);
|
|
}
|
|
catch (const std::bad_alloc&)
|
|
{
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution");
|
|
m_testCtx.setTerminateAfter(true);
|
|
}
|
|
catch (const tcu::TestException& e)
|
|
{
|
|
log << e;
|
|
m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
|
|
m_testCtx.setTerminateAfter(e.isFatal());
|
|
}
|
|
catch (const tcu::Exception& e)
|
|
{
|
|
log << e;
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
|
|
}
|
|
|
|
return iterateResult;
|
|
}
|
|
|
|
} // tcu
|