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 case hierarchy iterator.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuTestHierarchyIterator.hpp"
25#include "tcuCommandLine.hpp"
26
27namespace tcu
28{
29
30using std::string;
31using std::vector;
32
33// TestHierarchyInflater
34
35TestHierarchyInflater::TestHierarchyInflater (void)
36{
37}
38
39TestHierarchyInflater::~TestHierarchyInflater (void)
40{
41}
42
43// DefaultHierarchyInflater
44
45DefaultHierarchyInflater::DefaultHierarchyInflater (TestContext& testCtx)
46	: m_testCtx(testCtx)
47{
48}
49
50DefaultHierarchyInflater::~DefaultHierarchyInflater (void)
51{
52}
53
54void DefaultHierarchyInflater::enterTestPackage (TestPackage* testPackage, vector<TestNode*>& children)
55{
56	{
57		Archive* const	pkgArchive	= testPackage->getArchive();
58
59		if (pkgArchive)
60			m_testCtx.setCurrentArchive(*pkgArchive);
61		else
62			m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
63	}
64
65	testPackage->init();
66	testPackage->getChildren(children);
67
68	// write default session info if it was not done by package
69	m_testCtx.writeSessionInfo();
70}
71
72void DefaultHierarchyInflater::leaveTestPackage (TestPackage* testPackage)
73{
74	m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
75	testPackage->deinit();
76}
77
78void DefaultHierarchyInflater::enterGroupNode (TestCaseGroup* testGroup, vector<TestNode*>& children)
79{
80	testGroup->init();
81	testGroup->getChildren(children);
82}
83
84void DefaultHierarchyInflater::leaveGroupNode (TestCaseGroup* testGroup)
85{
86	testGroup->deinit();
87}
88
89// TestHierarchyIterator
90
91TestHierarchyIterator::TestHierarchyIterator (TestPackageRoot&			rootNode,
92											  TestHierarchyInflater&	inflater,
93											  const CaseListFilter&		caseListFilter)
94	: m_inflater		(inflater)
95	, m_caseListFilter	(caseListFilter)
96	, m_groupNumber		(0)
97{
98	// Init traverse state and "seek" to first reportable node.
99	NodeIter iter(&rootNode);
100	iter.setState(NodeIter::NISTATE_ENTER); // Root is never reported
101	m_sessionStack.push_back(iter);
102	next();
103}
104
105TestHierarchyIterator::~TestHierarchyIterator (void)
106{
107	// Tear down inflated nodes in m_sessionStack
108	for (vector<NodeIter>::reverse_iterator iter = m_sessionStack.rbegin(); iter != m_sessionStack.rend(); ++iter)
109	{
110		TestNode* const		node		= iter->node;
111		const TestNodeType	nodeType	= node->getNodeType();
112
113		switch (nodeType)
114		{
115			case NODETYPE_ROOT:		/* root is not de-initialized */								break;
116			case NODETYPE_PACKAGE:	m_inflater.leaveTestPackage(static_cast<TestPackage*>(node));	break;
117			case NODETYPE_GROUP:	m_inflater.leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
118			default:
119				break;
120		}
121	}
122}
123
124TestHierarchyIterator::State TestHierarchyIterator::getState (void) const
125{
126	if (!m_sessionStack.empty())
127	{
128		const NodeIter&	iter	= m_sessionStack.back();
129
130		DE_ASSERT(iter.getState() == NodeIter::NISTATE_ENTER ||
131				  iter.getState() == NodeIter::NISTATE_LEAVE);
132
133		return iter.getState() == NodeIter::NISTATE_ENTER ? STATE_ENTER_NODE : STATE_LEAVE_NODE;
134	}
135	else
136		return STATE_FINISHED;
137}
138
139TestNode* TestHierarchyIterator::getNode (void) const
140{
141	DE_ASSERT(getState() != STATE_FINISHED);
142	return m_sessionStack.back().node;
143}
144
145const std::string& TestHierarchyIterator::getNodePath (void) const
146{
147	DE_ASSERT(getState() != STATE_FINISHED);
148	return m_nodePath;
149}
150
151std::string TestHierarchyIterator::buildNodePath (const vector<NodeIter>& nodeStack)
152{
153	string nodePath;
154	for (size_t ndx = 1; ndx < nodeStack.size(); ndx++)
155	{
156		const NodeIter& iter = nodeStack[ndx];
157		if (ndx > 1) // ignore root package
158			nodePath += ".";
159		nodePath += iter.node->getName();
160	}
161	return nodePath;
162}
163
164void TestHierarchyIterator::next (void)
165{
166	while (!m_sessionStack.empty())
167	{
168		NodeIter&			iter		= m_sessionStack.back();
169		TestNode* const		node		= iter.node;
170		const bool			isLeaf		= isTestNodeTypeExecutable(node->getNodeType());
171
172		switch (iter.getState())
173		{
174			case NodeIter::NISTATE_INIT:
175			{
176				const std::string nodePath = buildNodePath(m_sessionStack);
177
178				// Return to parent if name or runner type doesn't match filter.
179				if (!(isLeaf ? (m_caseListFilter.checkRunnerType(node->getRunnerType()) && m_caseListFilter.checkTestCaseName(nodePath.c_str()))
180							 : m_caseListFilter.checkTestGroupName(nodePath.c_str())))
181				{
182					m_sessionStack.pop_back();
183					break;
184				}
185
186				m_nodePath = nodePath;
187				iter.setState(NodeIter::NISTATE_ENTER);
188				return; // Yield enter event
189			}
190
191			case NodeIter::NISTATE_ENTER:
192			{
193				if (isLeaf)
194				{
195					iter.setState(NodeIter::NISTATE_LEAVE);
196					return; // Yield leave event
197				}
198				else
199				{
200					iter.setState(NodeIter::NISTATE_TRAVERSE_CHILDREN);
201					iter.children.clear();
202
203					switch (node->getNodeType())
204					{
205						case NODETYPE_ROOT:		static_cast<TestPackageRoot*>(node)->getChildren(iter.children);				break;
206						case NODETYPE_PACKAGE:	m_inflater.enterTestPackage(static_cast<TestPackage*>(node), iter.children);	break;
207						case NODETYPE_GROUP:	m_inflater.enterGroupNode(static_cast<TestCaseGroup*>(node), iter.children);	break;
208						default:
209							DE_ASSERT(false);
210					}
211				}
212
213				break;
214			}
215
216			case NodeIter::NISTATE_TRAVERSE_CHILDREN:
217			{
218				int numChildren = (int)iter.children.size();
219				if (++iter.curChildNdx < numChildren)
220				{
221					// Push child to stack.
222					TestNode* childNode = iter.children[iter.curChildNdx];
223
224					// Check whether this is a bottom-level group (child is executable)
225					// and whether that group should be filtered out.
226					if ( isTestNodeTypeExecutable(childNode->getNodeType()) )
227					{
228						const std::string testName = m_nodePath + "." + childNode->getName();
229						if(!m_caseListFilter.checkCaseFraction(m_groupNumber, testName))
230							break;
231					}
232					m_sessionStack.push_back(NodeIter(childNode));
233				}
234				else
235				{
236					iter.setState(NodeIter::NISTATE_LEAVE);
237					if (node->getNodeType() != NODETYPE_ROOT)
238						return; // Yield leave event
239				}
240
241				break;
242			}
243
244			case NodeIter::NISTATE_LEAVE:
245			{
246				// Leave node.
247				if (!isLeaf)
248				{
249					switch (node->getNodeType())
250					{
251						case NODETYPE_ROOT:		/* root is not de-initialized */								break;
252						case NODETYPE_PACKAGE:	m_inflater.leaveTestPackage(static_cast<TestPackage*>(node));	break;
253						case NODETYPE_GROUP:	m_inflater.leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
254						default:
255							DE_ASSERT(false);
256					}
257					m_groupNumber++;
258				}
259
260				m_sessionStack.pop_back();
261				m_nodePath = buildNodePath(m_sessionStack);
262				break;
263			}
264
265			default:
266				DE_ASSERT(false);
267				return;
268		}
269	}
270
271	DE_ASSERT(m_sessionStack.empty() && getState() == STATE_FINISHED);
272}
273
274} // tcu
275