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 hierarchy utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuTestHierarchyUtil.hpp"
25#include "tcuStringTemplate.hpp"
26#include "tcuCommandLine.hpp"
27
28#include "qpXmlWriter.h"
29
30#include <fstream>
31
32namespace tcu
33{
34
35using std::string;
36
37static const char* getNodeTypeName (TestNodeType nodeType)
38{
39	switch (nodeType)
40	{
41		case NODETYPE_SELF_VALIDATE:	return "SelfValidate";
42		case NODETYPE_CAPABILITY:		return "Capability";
43		case NODETYPE_ACCURACY:			return "Accuracy";
44		case NODETYPE_PERFORMANCE:		return "Performance";
45		case NODETYPE_GROUP:			return "TestGroup";
46		default:
47			DE_ASSERT(false);
48			return DE_NULL;
49	}
50}
51
52// Utilities
53
54static std::string makePackageFilename (const std::string& pattern, const std::string& packageName, const std::string& typeExtension)
55{
56	std::map<string, string> args;
57	args["packageName"]		= packageName;
58	args["typeExtension"]	= typeExtension;
59	return StringTemplate(pattern).specialize(args);
60}
61
62static void writeXmlCaselist (TestHierarchyIterator& iter, qpXmlWriter* writer)
63{
64	DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
65			  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
66
67	{
68		const TestNode* node		= iter.getNode();
69		qpXmlAttribute	attribs[1];
70		int				numAttribs	= 0;
71		attribs[numAttribs++] = qpSetStringAttrib("PackageName", node->getName());
72		DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
73
74		if (!qpXmlWriter_startDocument(writer, true) ||
75			!qpXmlWriter_startElement(writer, "TestCaseList", numAttribs, attribs))
76			throw Exception("Failed to start XML document");
77	}
78
79	iter.next();
80
81	while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
82	{
83		const TestNode* const	node		= iter.getNode();
84		const TestNodeType		nodeType	= node->getNodeType();
85		const bool				isEnter		= iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE;
86
87		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE ||
88				  iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE);
89		{
90			if (isEnter)
91			{
92				const string	caseName	= node->getName();
93				qpXmlAttribute	attribs[2];
94				int				numAttribs = 0;
95
96				attribs[numAttribs++] = qpSetStringAttrib("Name",			caseName.c_str());
97				attribs[numAttribs++] = qpSetStringAttrib("CaseType",		getNodeTypeName(nodeType));
98				DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
99
100				if (!qpXmlWriter_startElement(writer, "TestCase", numAttribs, attribs))
101					throw Exception("Writing to case list file failed");
102			}
103			else
104			{
105				if (!qpXmlWriter_endElement(writer, "TestCase"))
106					throw tcu::Exception("Writing to case list file failed");
107			}
108		}
109
110		iter.next();
111	}
112
113	// This could be done in catch, but the file is corrupt at that point anyways.
114	if (!qpXmlWriter_endElement(writer, "TestCaseList") ||
115		!qpXmlWriter_endDocument(writer))
116		throw Exception("Failed to terminate XML document");
117}
118
119/*--------------------------------------------------------------------*//*!
120 * \brief Export the test list of each package into a separate XML file.
121 *//*--------------------------------------------------------------------*/
122void writeXmlCaselistsToFiles (TestPackageRoot& root, TestContext& testCtx, const CommandLine& cmdLine)
123{
124	DefaultHierarchyInflater			inflater		(testCtx);
125	de::MovePtr<const CaseListFilter>	caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
126
127	TestHierarchyIterator				iter			(root, inflater, *caseListFilter);
128	const char* const					filenamePattern = cmdLine.getCaseListExportFile();
129
130	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
131	{
132		const TestNode* node		= iter.getNode();
133		const char*		pkgName		= node->getName();
134		const string	filename	= makePackageFilename(filenamePattern, pkgName, "xml");
135
136		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
137				  node->getNodeType() == NODETYPE_PACKAGE);
138
139		FILE*			file	= DE_NULL;
140		qpXmlWriter*	writer	= DE_NULL;
141
142		try
143		{
144			file = fopen(filename.c_str(), "wb");
145			if (!file)
146				throw Exception("Failed to open " + filename);
147
148			writer = qpXmlWriter_createFileWriter(file, DE_FALSE, DE_FALSE);
149			if (!writer)
150				throw Exception("XML writer creation failed");
151
152			print("Writing test cases from '%s' to file '%s'..\n", pkgName, filename.c_str());
153
154			writeXmlCaselist(iter, writer);
155
156			qpXmlWriter_destroy(writer);
157			writer = DE_NULL;
158
159			fclose(file);
160			file = DE_NULL;
161		}
162		catch (...)
163		{
164			if (writer)
165				qpXmlWriter_destroy(writer);
166			if (file)
167				fclose(file);
168			throw;
169		}
170
171		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
172				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
173		iter.next();
174	}
175}
176
177/*--------------------------------------------------------------------*//*!
178 * \brief Export the test list of each package into a separate ascii file.
179 *//*--------------------------------------------------------------------*/
180void writeTxtCaselistsToFiles (TestPackageRoot& root, TestContext& testCtx, const CommandLine& cmdLine)
181{
182	DefaultHierarchyInflater			inflater		(testCtx);
183	de::MovePtr<const CaseListFilter>	caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
184
185	TestHierarchyIterator				iter			(root, inflater, *caseListFilter);
186	const char* const					filenamePattern = cmdLine.getCaseListExportFile();
187
188	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
189	{
190		const TestNode* node		= iter.getNode();
191		const char*		pkgName		= node->getName();
192		const string	filename	= makePackageFilename(filenamePattern, pkgName, "txt");
193
194		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
195				  node->getNodeType() == NODETYPE_PACKAGE);
196
197		std::ofstream out(filename.c_str(), std::ios_base::binary);
198		if (!out.is_open() || !out.good())
199			throw Exception("Failed to open " + filename);
200
201		print("Writing test cases from '%s' to file '%s'..\n", pkgName, filename.c_str());
202
203		try
204		{
205			iter.next();
206		}
207		catch (const tcu::NotSupportedError&)
208		{
209			return;
210		}
211
212		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
213		{
214			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE)
215				out << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n";
216			iter.next();
217		}
218
219		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
220				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
221		iter.next();
222	}
223}
224
225} // tcu
226