• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Executor which can run randomly accessed tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuRandomOrderExecutor.h"
25 
26 #include "deClock.h"
27 #include "deStringUtil.hpp"
28 #include "tcuCommandLine.hpp"
29 #include "tcuTestLog.hpp"
30 
31 #include <algorithm>
32 #include <cstdio>
33 #include <fstream>
34 
35 using std::string;
36 using std::vector;
37 
38 namespace tcu
39 {
40 
RandomOrderExecutor(TestPackageRoot & root,TestContext & testCtx)41 RandomOrderExecutor::RandomOrderExecutor(TestPackageRoot &root, TestContext &testCtx)
42     : m_testCtx(testCtx), m_inflater(testCtx)
43 {
44     m_nodeStack.push_back(NodeStackEntry(&root));
45     root.getChildren(m_nodeStack[0].children);
46 }
47 
~RandomOrderExecutor(void)48 RandomOrderExecutor::~RandomOrderExecutor(void)
49 {
50     pruneStack(1);
51 }
52 
pruneStack(size_t newStackSize)53 void RandomOrderExecutor::pruneStack(size_t newStackSize)
54 {
55     // \note Root is not managed by us
56     DE_ASSERT(de::inRange(newStackSize, size_t(1), m_nodeStack.size()));
57 
58     while (m_nodeStack.size() > newStackSize)
59     {
60         NodeStackEntry &curEntry = m_nodeStack.back();
61         const bool isPkg         = curEntry.node->getNodeType() == NODETYPE_PACKAGE;
62 
63         DE_ASSERT((m_nodeStack.size() == 2) == isPkg);
64 
65         if (curEntry.node)  // Just in case we are in
66                             // cleanup path after partial
67                             // prune
68         {
69             if (curEntry.node->getNodeType() == NODETYPE_GROUP)
70                 m_inflater.leaveGroupNode(static_cast<TestCaseGroup *>(curEntry.node));
71             else if (curEntry.node->getNodeType() == NODETYPE_PACKAGE)
72                 m_inflater.leaveTestPackage(static_cast<TestPackage *>(curEntry.node));
73             else
74                 DE_ASSERT(curEntry.children.empty());
75 
76             curEntry.node = DE_NULL;
77             curEntry.children.clear();
78         }
79 
80         if (isPkg)
81             m_caseExecutor.clear();
82 
83         m_nodeStack.pop_back();
84     }
85 }
86 
findNodeByName(vector<TestNode * > & nodes,const std::string & name)87 static TestNode *findNodeByName(vector<TestNode *> &nodes, const std::string &name)
88 {
89     for (vector<TestNode *>::const_iterator node = nodes.begin(); node != nodes.end(); ++node)
90     {
91         if (name == (*node)->getName())
92             return *node;
93     }
94 
95     return DE_NULL;
96 }
97 
seekToCase(const string & path)98 TestCase *RandomOrderExecutor::seekToCase(const string &path)
99 {
100     const vector<string> components = de::splitString(path, '.');
101     size_t compNdx;
102 
103     DE_ASSERT(!m_nodeStack.empty() && m_nodeStack.front().node->getNodeType() == NODETYPE_ROOT);
104 
105     for (compNdx = 0; compNdx < components.size(); compNdx++)
106     {
107         const size_t stackPos = compNdx + 1;
108 
109         if (stackPos >= m_nodeStack.size())
110             break;  // Stack end
111         else if (components[compNdx] != m_nodeStack[stackPos].node->getName())
112         {
113             // Current stack doesn't match any more, prune.
114             pruneStack(stackPos);
115             break;
116         }
117     }
118 
119     DE_ASSERT(m_nodeStack.size() == compNdx + 1);
120 
121     for (; compNdx < components.size(); compNdx++)
122     {
123         const size_t parentStackPos = compNdx;
124         TestNode *const curNode =
125             findNodeByName(m_nodeStack[parentStackPos].children, components[compNdx]);
126 
127         if (!curNode)
128             throw Exception(string("Test hierarchy node not found: ") + path);
129 
130         m_nodeStack.push_back(NodeStackEntry(curNode));
131 
132         if (curNode->getNodeType() == NODETYPE_PACKAGE)
133         {
134             TestPackage *const testPackage = static_cast<TestPackage *>(curNode);
135 
136             m_inflater.enterTestPackage(testPackage, m_nodeStack.back().children);
137             DE_ASSERT(!m_caseExecutor);
138             m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
139         }
140         else if (curNode->getNodeType() == NODETYPE_GROUP)
141             m_inflater.enterGroupNode(static_cast<TestCaseGroup *>(curNode),
142                                       m_nodeStack.back().children);
143         else if (compNdx + 1 != components.size())
144             throw Exception(string("Invalid test hierarchy path: ") + path);
145     }
146 
147     DE_ASSERT(m_nodeStack.size() == components.size() + 1);
148 
149     if (isTestNodeTypeExecutable(m_nodeStack.back().node->getNodeType()))
150         return dynamic_cast<TestCase *>(m_nodeStack.back().node);
151     else
152         throw Exception(string("Not a test case: ") + path);
153 }
154 
nodeTypeToTestCaseType(TestNodeType nodeType)155 static qpTestCaseType nodeTypeToTestCaseType(TestNodeType nodeType)
156 {
157     switch (nodeType)
158     {
159         case NODETYPE_SELF_VALIDATE:
160             return QP_TEST_CASE_TYPE_SELF_VALIDATE;
161         case NODETYPE_PERFORMANCE:
162             return QP_TEST_CASE_TYPE_PERFORMANCE;
163         case NODETYPE_CAPABILITY:
164             return QP_TEST_CASE_TYPE_CAPABILITY;
165         case NODETYPE_ACCURACY:
166             return QP_TEST_CASE_TYPE_ACCURACY;
167         default:
168             DE_ASSERT(false);
169             return QP_TEST_CASE_TYPE_LAST;
170     }
171 }
172 
execute(const std::string & casePath)173 TestStatus RandomOrderExecutor::execute(const std::string &casePath)
174 {
175     TestCase *const testCase      = seekToCase(casePath);
176     TestLog &log                  = m_testCtx.getLog();
177     const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType());
178 
179     m_testCtx.setTerminateAfter(false);
180     log.startCase(casePath.c_str(), caseType);
181 
182     {
183         const TestStatus result = executeInner(testCase, casePath);
184         log.endCase(result.getCode(), result.getDescription().c_str());
185         return result;
186     }
187 }
188 
executeInner(TestCase * testCase,const std::string & casePath)189 tcu::TestStatus RandomOrderExecutor::executeInner(TestCase *testCase, const std::string &casePath)
190 {
191     TestLog &log                 = m_testCtx.getLog();
192     const deUint64 testStartTime = deGetMicroseconds();
193 
194     m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
195 
196     // Initialize, will return immediately if fails
197     try
198     {
199         m_caseExecutor->init(testCase, casePath);
200     }
201     catch (const std::bad_alloc &)
202     {
203         m_testCtx.setTerminateAfter(true);
204         return TestStatus(QP_TEST_RESULT_RESOURCE_ERROR,
205                           "Failed to allocate memory in test case init");
206     }
207     catch (const TestException &e)
208     {
209         DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
210         m_testCtx.setTerminateAfter(e.isFatal());
211         log << e;
212         return TestStatus(e.getTestResult(), e.getMessage());
213     }
214     catch (const Exception &e)
215     {
216         log << e;
217         return TestStatus(QP_TEST_RESULT_FAIL, e.getMessage());
218     }
219 
220     // Execute
221     for (;;)
222     {
223         TestCase::IterateResult iterateResult = TestCase::STOP;
224 
225         m_testCtx.touchWatchdog();
226 
227         try
228         {
229             iterateResult = m_caseExecutor->iterate(testCase);
230         }
231         catch (const std::bad_alloc &)
232         {
233             m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR,
234                                     "Failed to allocate memory during test "
235                                     "execution");
236         }
237         catch (const TestException &e)
238         {
239             log << e;
240             m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
241             m_testCtx.setTerminateAfter(e.isFatal());
242         }
243         catch (const Exception &e)
244         {
245             log << e;
246             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
247         }
248 
249         if (iterateResult == TestCase::STOP)
250             break;
251     }
252 
253     DE_ASSERT(m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
254 
255     if (m_testCtx.getTestResult() == QP_TEST_RESULT_RESOURCE_ERROR)
256         m_testCtx.setTerminateAfter(true);
257 
258     // De-initialize
259     try
260     {
261         m_caseExecutor->deinit(testCase);
262     }
263     catch (const tcu::Exception &e)
264     {
265         log << e << TestLog::Message
266             << "Error in test case deinit, test program "
267                "will terminate."
268             << TestLog::EndMessage;
269         m_testCtx.setTerminateAfter(true);
270     }
271 
272     if (m_testCtx.getWatchDog())
273         qpWatchDog_reset(m_testCtx.getWatchDog());
274 
275     {
276         const TestStatus result =
277             TestStatus(m_testCtx.getTestResult(), m_testCtx.getTestResultDesc());
278         m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
279         return result;
280     }
281 }
282 
283 }  // namespace tcu
284