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[2];
70		int				numAttribs	= 0;
71		attribs[numAttribs++] = qpSetStringAttrib("PackageName", node->getName());
72		attribs[numAttribs++] = qpSetStringAttrib("Description", node->getDescription());
73		DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
74
75		if (!qpXmlWriter_startDocument(writer) ||
76			!qpXmlWriter_startElement(writer, "TestCaseList", numAttribs, attribs))
77			throw Exception("Failed to start XML document");
78	}
79
80	iter.next();
81
82	while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
83	{
84		const TestNode* const	node		= iter.getNode();
85		const TestNodeType		nodeType	= node->getNodeType();
86		const bool				isEnter		= iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE;
87
88		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE ||
89				  iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE);
90		{
91			if (isEnter)
92			{
93				const string	caseName	= node->getName();
94				const string	description	= node->getDescription();
95				qpXmlAttribute	attribs[3];
96				int				numAttribs = 0;
97
98				attribs[numAttribs++] = qpSetStringAttrib("Name",			caseName.c_str());
99				attribs[numAttribs++] = qpSetStringAttrib("CaseType",		getNodeTypeName(nodeType));
100				attribs[numAttribs++] = qpSetStringAttrib("Description",	description.c_str());
101				DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
102
103				if (!qpXmlWriter_startElement(writer, "TestCase", numAttribs, attribs))
104					throw Exception("Writing to case list file failed");
105			}
106			else
107			{
108				if (!qpXmlWriter_endElement(writer, "TestCase"))
109					throw tcu::Exception("Writing to case list file failed");
110			}
111		}
112
113		iter.next();
114	}
115
116	// This could be done in catch, but the file is corrupt at that point anyways.
117	if (!qpXmlWriter_endElement(writer, "TestCaseList") ||
118		!qpXmlWriter_endDocument(writer))
119		throw Exception("Failed to terminate XML document");
120}
121
122/*--------------------------------------------------------------------*//*!
123 * \brief Export the test list of each package into a separate XML file.
124 *//*--------------------------------------------------------------------*/
125void writeXmlCaselistsToFiles (TestPackageRoot& root, TestContext& testCtx, const CommandLine& cmdLine)
126{
127	DefaultHierarchyInflater			inflater		(testCtx);
128	de::MovePtr<const CaseListFilter>	caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
129
130	TestHierarchyIterator				iter			(root, inflater, *caseListFilter);
131	const char* const					filenamePattern = cmdLine.getCaseListExportFile();
132
133	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
134	{
135		const TestNode* node		= iter.getNode();
136		const char*		pkgName		= node->getName();
137		const string	filename	= makePackageFilename(filenamePattern, pkgName, "xml");
138
139		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
140				  node->getNodeType() == NODETYPE_PACKAGE);
141
142		FILE*			file	= DE_NULL;
143		qpXmlWriter*	writer	= DE_NULL;
144
145		try
146		{
147			file = fopen(filename.c_str(), "wb");
148			if (!file)
149				throw Exception("Failed to open " + filename);
150
151			writer = qpXmlWriter_createFileWriter(file, DE_FALSE, DE_FALSE);
152			if (!writer)
153				throw Exception("XML writer creation failed");
154
155			print("Writing test cases from '%s' to file '%s'..\n", pkgName, filename.c_str());
156
157			writeXmlCaselist(iter, writer);
158
159			qpXmlWriter_destroy(writer);
160			writer = DE_NULL;
161
162			fclose(file);
163			file = DE_NULL;
164		}
165		catch (...)
166		{
167			if (writer)
168				qpXmlWriter_destroy(writer);
169			if (file)
170				fclose(file);
171			throw;
172		}
173
174		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
175				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
176		iter.next();
177	}
178}
179
180/*--------------------------------------------------------------------*//*!
181 * \brief Export the test list of each package into a separate ascii file.
182 *//*--------------------------------------------------------------------*/
183void writeTxtCaselistsToFiles (TestPackageRoot& root, TestContext& testCtx, const CommandLine& cmdLine)
184{
185	DefaultHierarchyInflater			inflater		(testCtx);
186	de::MovePtr<const CaseListFilter>	caseListFilter	(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive()));
187
188	TestHierarchyIterator				iter			(root, inflater, *caseListFilter);
189	const char* const					filenamePattern = cmdLine.getCaseListExportFile();
190
191	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
192	{
193		const TestNode* node		= iter.getNode();
194		const char*		pkgName		= node->getName();
195		const string	filename	= makePackageFilename(filenamePattern, pkgName, "txt");
196
197		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
198				  node->getNodeType() == NODETYPE_PACKAGE);
199
200		std::ofstream out(filename.c_str(), std::ios_base::binary);
201		if (!out.is_open() || !out.good())
202			throw Exception("Failed to open " + filename);
203
204		print("Writing test cases from '%s' to file '%s'..\n", pkgName, filename.c_str());
205
206		iter.next();
207
208		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
209		{
210			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE)
211				out << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n";
212			iter.next();
213		}
214
215		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
216				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
217		iter.next();
218	}
219}
220
221} // tcu
222