1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Test Executor
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 Merge two test logs.
22 *
23 * \todo [2013-11-08 pyry] Write variant that can operate with less memory.
24 *//*--------------------------------------------------------------------*/
25
26#include "xeTestLogParser.hpp"
27#include "xeTestResultParser.hpp"
28#include "xeTestLogWriter.hpp"
29#include "deString.h"
30
31#include <vector>
32#include <string>
33#include <cstdio>
34#include <cstdlib>
35#include <fstream>
36#include <iostream>
37#include <stdexcept>
38
39using std::vector;
40using std::string;
41using std::set;
42using std::map;
43
44enum Flags
45{
46	FLAG_USE_LAST_INFO = (1<<0)
47};
48
49struct CommandLine
50{
51	CommandLine (void)
52		: flags(0)
53	{
54	}
55
56	vector<string>	srcFilenames;
57	string			dstFilename;
58	deUint32		flags;
59};
60
61class LogHandler : public xe::TestLogHandler
62{
63public:
64	LogHandler (xe::BatchResult* batchResult, deUint32 flags)
65		: m_batchResult	(batchResult)
66		, m_flags		(flags)
67	{
68	}
69
70	void setSessionInfo (const xe::SessionInfo& info)
71	{
72		xe::SessionInfo& combinedInfo = m_batchResult->getSessionInfo();
73
74		if (m_flags & FLAG_USE_LAST_INFO)
75		{
76			if (!info.targetName.empty())		combinedInfo.targetName			= info.targetName;
77			if (!info.releaseId.empty())		combinedInfo.releaseId			= info.releaseId;
78			if (!info.releaseName.empty())		combinedInfo.releaseName		= info.releaseName;
79			if (!info.candyTargetName.empty())	combinedInfo.candyTargetName	= info.candyTargetName;
80			if (!info.configName.empty())		combinedInfo.configName			= info.configName;
81			if (!info.resultName.empty())		combinedInfo.resultName			= info.resultName;
82			if (!info.timestamp.empty())		combinedInfo.timestamp			= info.timestamp;
83		}
84		else
85		{
86			if (combinedInfo.targetName.empty())		combinedInfo.targetName			= info.targetName;
87			if (combinedInfo.releaseId.empty())			combinedInfo.releaseId			= info.releaseId;
88			if (combinedInfo.releaseName.empty())		combinedInfo.releaseName		= info.releaseName;
89			if (combinedInfo.candyTargetName.empty())	combinedInfo.candyTargetName	= info.candyTargetName;
90			if (combinedInfo.configName.empty())		combinedInfo.configName			= info.configName;
91			if (combinedInfo.resultName.empty())		combinedInfo.resultName			= info.resultName;
92			if (combinedInfo.timestamp.empty())			combinedInfo.timestamp			= info.timestamp;
93		}
94	}
95
96	xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
97	{
98		if (m_batchResult->hasTestCaseResult(casePath))
99		{
100			xe::TestCaseResultPtr existingResult = m_batchResult->getTestCaseResult(casePath);
101			existingResult->clear();
102			return existingResult;
103		}
104		else
105			return m_batchResult->createTestCaseResult(casePath);
106	}
107
108	void testCaseResultUpdated (const xe::TestCaseResultPtr&)
109	{
110		// Ignored.
111	}
112
113	void testCaseResultComplete (const xe::TestCaseResultPtr&)
114	{
115		// Ignored.
116	}
117
118private:
119	xe::BatchResult* const	m_batchResult;
120	const deUint32			m_flags;
121};
122
123static void readLogFile (xe::BatchResult* dstResult, const char* filename, deUint32 flags)
124{
125	std::ifstream		in				(filename, std::ifstream::binary|std::ifstream::in);
126	LogHandler			resultHandler	(dstResult, flags);
127	xe::TestLogParser	parser			(&resultHandler);
128	deUint8				buf				[2048];
129	int					numRead			= 0;
130
131	if (!in.good())
132		throw std::runtime_error(string("Failed to open '") + filename + "'");
133
134	for (;;)
135	{
136		in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
137		numRead = (int)in.gcount();
138
139		if (numRead <= 0)
140			break;
141
142		parser.parse(&buf[0], numRead);
143	}
144
145	in.close();
146}
147
148static void mergeTestLogs (const CommandLine& cmdLine)
149{
150	xe::BatchResult batchResult;
151
152	for (vector<string>::const_iterator filename = cmdLine.srcFilenames.begin(); filename != cmdLine.srcFilenames.end(); ++filename)
153		readLogFile(&batchResult, filename->c_str(), cmdLine.flags);
154
155	if (!cmdLine.dstFilename.empty())
156		xe::writeBatchResultToFile(batchResult, cmdLine.dstFilename.c_str());
157	else
158		xe::writeTestLog(batchResult, std::cout);
159}
160
161static void printHelp (const char* binName)
162{
163	printf("%s: [filename] [[filename 2] ...]\n", binName);
164	printf("  --dst=[filename]    Write final log to file, otherwise written to stdout.\n");
165	printf("  --info=[first|last] Select which session info to use (default: first).\n");
166}
167
168static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
169{
170	for (int argNdx = 1; argNdx < argc; argNdx++)
171	{
172		const char* arg = argv[argNdx];
173
174		if (!deStringBeginsWith(arg, "--"))
175			cmdLine.srcFilenames.push_back(arg);
176		else if (deStringBeginsWith(arg, "--dst="))
177		{
178			if (!cmdLine.dstFilename.empty())
179				return false;
180			cmdLine.dstFilename = arg+6;
181		}
182		else if (deStringEqual(arg, "--info=first"))
183			cmdLine.flags &= ~FLAG_USE_LAST_INFO;
184		else if (deStringEqual(arg, "--info=last"))
185			cmdLine.flags |= FLAG_USE_LAST_INFO;
186		else
187			return false;
188	}
189
190	if (cmdLine.srcFilenames.empty())
191		return false;
192
193	return true;
194}
195
196int main (int argc, const char* const* argv)
197{
198	try
199	{
200		CommandLine cmdLine;
201
202		if (!parseCommandLine(cmdLine, argc, argv))
203		{
204			printHelp(argv[0]);
205			return -1;
206		}
207
208		mergeTestLogs(cmdLine);
209	}
210	catch (const std::exception& e)
211	{
212		printf("FATAL ERROR: %s\n", e.what());
213		return -1;
214	}
215
216	return 0;
217}
218