Home | History | Annotate | Download | only in tools
      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 Test log compare utility.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xeTestLogParser.hpp"
     25 #include "xeTestResultParser.hpp"
     26 #include "deFilePath.hpp"
     27 #include "deString.h"
     28 #include "deThread.hpp"
     29 #include "deCommandLine.hpp"
     30 
     31 #include <vector>
     32 #include <string>
     33 #include <cstdio>
     34 #include <cstdlib>
     35 #include <fstream>
     36 #include <iostream>
     37 #include <set>
     38 #include <map>
     39 
     40 using std::vector;
     41 using std::string;
     42 using std::set;
     43 using std::map;
     44 
     45 enum OutputMode
     46 {
     47 	OUTPUTMODE_ALL = 0,
     48 	OUTPUTMODE_DIFF,
     49 
     50 	OUTPUTMODE_LAST
     51 };
     52 
     53 enum OutputFormat
     54 {
     55 	OUTPUTFORMAT_TEXT = 0,
     56 	OUTPUTFORMAT_CSV,
     57 
     58 	OUTPUTFORMAT_LAST
     59 };
     60 
     61 enum OutputValue
     62 {
     63 	OUTPUTVALUE_STATUS_CODE = 0,
     64 	OUTPUTVALUE_STATUS_DETAILS,
     65 
     66 	OUTPUTVALUE_LAST
     67 };
     68 
     69 namespace opt
     70 {
     71 
     72 DE_DECLARE_COMMAND_LINE_OPT(OutMode,	OutputMode);
     73 DE_DECLARE_COMMAND_LINE_OPT(OutFormat,	OutputFormat);
     74 DE_DECLARE_COMMAND_LINE_OPT(OutValue,	OutputValue);
     75 
     76 static void registerOptions (de::cmdline::Parser& parser)
     77 {
     78 	using de::cmdline::Option;
     79 	using de::cmdline::NamedValue;
     80 
     81 	static const NamedValue<OutputMode> s_outputModes[] =
     82 	{
     83 		{ "all",	OUTPUTMODE_ALL	},
     84 		{ "diff",	OUTPUTMODE_DIFF	}
     85 	};
     86 	static const NamedValue<OutputFormat> s_outputFormats[] =
     87 	{
     88 		{ "text",	OUTPUTFORMAT_TEXT	},
     89 		{ "csv",	OUTPUTFORMAT_CSV	}
     90 	};
     91 	static const NamedValue<OutputValue> s_outputValues[] =
     92 	{
     93 		{ "code",		OUTPUTVALUE_STATUS_CODE		},
     94 		{ "details",	OUTPUTVALUE_STATUS_DETAILS	}
     95 	};
     96 
     97 	parser << Option<OutFormat>		("f",	"format",		"Output format",	s_outputFormats,	"csv")
     98 		   << Option<OutMode>		("m",	"mode",			"Output mode",		s_outputModes,		"all")
     99 		   << Option<OutValue>		("v",	"value",		"Value to extract",	s_outputValues,		"code");
    100 }
    101 
    102 } // opt
    103 
    104 struct CommandLine
    105 {
    106 	CommandLine (void)
    107 		: outMode	(OUTPUTMODE_ALL)
    108 		, outFormat	(OUTPUTFORMAT_CSV)
    109 		, outValue	(OUTPUTVALUE_STATUS_CODE)
    110 	{
    111 	}
    112 
    113 	OutputMode			outMode;
    114 	OutputFormat		outFormat;
    115 	OutputValue			outValue;
    116 	vector<string>		filenames;
    117 };
    118 
    119 struct ShortBatchResult
    120 {
    121 	vector<xe::TestCaseResultHeader>	resultHeaders;
    122 	map<string, int>					resultMap;
    123 };
    124 
    125 class ShortResultHandler : public xe::TestLogHandler
    126 {
    127 public:
    128 	ShortResultHandler (ShortBatchResult& result)
    129 		: m_result(result)
    130 	{
    131 	}
    132 
    133 	void setSessionInfo (const xe::SessionInfo&)
    134 	{
    135 		// Ignored.
    136 	}
    137 
    138 	xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
    139 	{
    140 		return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
    141 	}
    142 
    143 	void testCaseResultUpdated (const xe::TestCaseResultPtr&)
    144 	{
    145 		// Ignored.
    146 	}
    147 
    148 	void testCaseResultComplete (const xe::TestCaseResultPtr& caseData)
    149 	{
    150 		xe::TestCaseResultHeader	header;
    151 		int							caseNdx	= (int)m_result.resultHeaders.size();
    152 
    153 		header.casePath			= caseData->getTestCasePath();
    154 		header.caseType			= xe::TESTCASETYPE_SELF_VALIDATE;
    155 		header.statusCode		= caseData->getStatusCode();
    156 		header.statusDetails	= caseData->getStatusDetails();
    157 
    158 		if (header.statusCode == xe::TESTSTATUSCODE_LAST)
    159 		{
    160 			xe::TestCaseResult fullResult;
    161 
    162 			xe::parseTestCaseResultFromData(&m_testResultParser, &fullResult, *caseData.get());
    163 
    164 			header = xe::TestCaseResultHeader(fullResult);
    165 		}
    166 
    167 		// Insert into result list & map.
    168 		m_result.resultHeaders.push_back(header);
    169 		m_result.resultMap[header.casePath] = caseNdx;
    170 	}
    171 
    172 private:
    173 	ShortBatchResult&		m_result;
    174 	xe::TestResultParser	m_testResultParser;
    175 };
    176 
    177 static void readLogFile (ShortBatchResult& batchResult, const char* filename)
    178 {
    179 	std::ifstream		in				(filename, std::ifstream::binary|std::ifstream::in);
    180 	ShortResultHandler	resultHandler	(batchResult);
    181 	xe::TestLogParser	parser			(&resultHandler);
    182 	deUint8				buf				[1024];
    183 	int					numRead			= 0;
    184 
    185 	for (;;)
    186 	{
    187 		in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
    188 		numRead = (int)in.gcount();
    189 
    190 		if (numRead <= 0)
    191 			break;
    192 
    193 		parser.parse(&buf[0], numRead);
    194 	}
    195 
    196 	in.close();
    197 }
    198 
    199 class LogFileReader : public de::Thread
    200 {
    201 public:
    202 	LogFileReader (ShortBatchResult& batchResult, const char* filename)
    203 		: m_batchResult	(batchResult)
    204 		, m_filename	(filename)
    205 	{
    206 	}
    207 
    208 	void run (void)
    209 	{
    210 		readLogFile(m_batchResult, m_filename.c_str());
    211 	}
    212 
    213 private:
    214 	ShortBatchResult&	m_batchResult;
    215 	std::string			m_filename;
    216 };
    217 
    218 static void computeCaseList (vector<string>& cases, const vector<ShortBatchResult>& batchResults)
    219 {
    220 	// \todo [2012-07-10 pyry] Do proper case ordering (eg. handle missing cases nicely).
    221 	set<string> addedCases;
    222 
    223 	for (vector<ShortBatchResult>::const_iterator batchIter = batchResults.begin(); batchIter != batchResults.end(); batchIter++)
    224 	{
    225 		for (vector<xe::TestCaseResultHeader>::const_iterator caseIter = batchIter->resultHeaders.begin(); caseIter != batchIter->resultHeaders.end(); caseIter++)
    226 		{
    227 			if (addedCases.find(caseIter->casePath) == addedCases.end())
    228 			{
    229 				cases.push_back(caseIter->casePath);
    230 				addedCases.insert(caseIter->casePath);
    231 			}
    232 		}
    233 	}
    234 }
    235 
    236 static void getTestResultHeaders (vector<xe::TestCaseResultHeader>& headers, const vector<ShortBatchResult>& batchResults, const char* casePath)
    237 {
    238 	headers.resize(batchResults.size());
    239 
    240 	for (int ndx = 0; ndx < (int)batchResults.size(); ndx++)
    241 	{
    242 		const ShortBatchResult&				batchResult	= batchResults[ndx];
    243 		map<string, int>::const_iterator	resultPos	= batchResult.resultMap.find(casePath);
    244 
    245 		if (resultPos != batchResult.resultMap.end())
    246 			headers[ndx] = batchResult.resultHeaders[resultPos->second];
    247 		else
    248 		{
    249 			headers[ndx].casePath	= casePath;
    250 			headers[ndx].caseType	= xe::TESTCASETYPE_SELF_VALIDATE;
    251 			headers[ndx].statusCode	= xe::TESTSTATUSCODE_LAST;
    252 		}
    253 	}
    254 }
    255 
    256 static const char* getStatusCodeName (xe::TestStatusCode code)
    257 {
    258 	if (code == xe::TESTSTATUSCODE_LAST)
    259 		return "Missing";
    260 	else
    261 		return xe::getTestStatusCodeName(code);
    262 }
    263 
    264 static bool runCompare (const CommandLine& cmdLine, std::ostream& dst)
    265 {
    266 	vector<ShortBatchResult>	results;
    267 	vector<string>				batchNames;
    268 	bool						compareOk	= true;
    269 
    270 	XE_CHECK(!cmdLine.filenames.empty());
    271 
    272 	try
    273 	{
    274 		// Read in batch results
    275 		results.resize(cmdLine.filenames.size());
    276 		{
    277 			std::vector<de::SharedPtr<LogFileReader> > readers;
    278 
    279 			for (int ndx = 0; ndx < (int)cmdLine.filenames.size(); ndx++)
    280 			{
    281 				readers.push_back(de::SharedPtr<LogFileReader>(new LogFileReader(results[ndx], cmdLine.filenames[ndx].c_str())));
    282 				readers.back()->start();
    283 			}
    284 
    285 			for (int ndx = 0; ndx < (int)cmdLine.filenames.size(); ndx++)
    286 			{
    287 				readers[ndx]->join();
    288 
    289 				// Use file name as batch name.
    290 				batchNames.push_back(de::FilePath(cmdLine.filenames[ndx].c_str()).getBaseName());
    291 			}
    292 		}
    293 
    294 		// Compute unified case list.
    295 		vector<string> caseList;
    296 		computeCaseList(caseList, results);
    297 
    298 		// Stats.
    299 		int		numCases		= (int)caseList.size();
    300 		int		numEqual		= 0;
    301 
    302 		if (cmdLine.outFormat == OUTPUTFORMAT_CSV)
    303 		{
    304 			dst << "TestCasePath";
    305 			for (vector<string>::const_iterator nameIter = batchNames.begin(); nameIter != batchNames.end(); nameIter++)
    306 				dst << "," << *nameIter;
    307 			dst << "\n";
    308 		}
    309 
    310 		// Compare cases.
    311 		for (vector<string>::const_iterator caseIter = caseList.begin(); caseIter != caseList.end(); caseIter++)
    312 		{
    313 			const string&						caseName	= *caseIter;
    314 			vector<xe::TestCaseResultHeader>	headers;
    315 			bool								allEqual	= true;
    316 
    317 			getTestResultHeaders(headers, results, caseName.c_str());
    318 
    319 			for (vector<xe::TestCaseResultHeader>::const_iterator iter = headers.begin()+1; iter != headers.end(); iter++)
    320 			{
    321 				if (iter->statusCode != headers[0].statusCode)
    322 				{
    323 					allEqual = false;
    324 					break;
    325 				}
    326 			}
    327 
    328 			if (allEqual)
    329 				numEqual += 1;
    330 
    331 			if (cmdLine.outMode == OUTPUTMODE_ALL || !allEqual)
    332 			{
    333 				if (cmdLine.outFormat == OUTPUTFORMAT_TEXT)
    334 				{
    335 					dst << caseName << "\n";
    336 					for (int ndx = 0; ndx < (int)headers.size(); ndx++)
    337 						dst << "  " << batchNames[ndx] << ": " << getStatusCodeName(headers[ndx].statusCode) << " (" << headers[ndx].statusDetails << ")\n";
    338 					dst << "\n";
    339 				}
    340 				else if (cmdLine.outFormat == OUTPUTFORMAT_CSV)
    341 				{
    342 					dst << caseName;
    343 					for (vector<xe::TestCaseResultHeader>::const_iterator iter = headers.begin(); iter != headers.end(); iter++)
    344 						dst << "," << (cmdLine.outValue == OUTPUTVALUE_STATUS_CODE ? getStatusCodeName(iter->statusCode) : iter->statusDetails.c_str());
    345 					dst << "\n";
    346 				}
    347 			}
    348 		}
    349 
    350 		compareOk = numEqual == numCases;
    351 
    352 		if (cmdLine.outFormat == OUTPUTFORMAT_TEXT)
    353 		{
    354 			dst << "  " << numEqual << " / " << numCases << " test case results match.\n";
    355 			dst << "  Comparison " << (compareOk ? "passed" : "FAILED") << "!\n";
    356 		}
    357 	}
    358 	catch (const std::exception& e)
    359 	{
    360 		printf("%s\n", e.what());
    361 		compareOk = false;
    362 	}
    363 
    364 	return compareOk;
    365 }
    366 
    367 static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
    368 {
    369 	de::cmdline::Parser			parser;
    370 	de::cmdline::CommandLine	opts;
    371 
    372 	XE_CHECK(argc >= 1);
    373 
    374 	opt::registerOptions(parser);
    375 
    376 	if (!parser.parse(argc-1, &argv[1], &opts, std::cerr)	||
    377 		opts.getArgs().empty())
    378 	{
    379 		std::cout << argv[0] << ": [options] [filenames]\n";
    380 		parser.help(std::cout);
    381 		return false;
    382 	}
    383 
    384 	cmdLine.outFormat	= opts.getOption<opt::OutFormat>();
    385 	cmdLine.outMode		= opts.getOption<opt::OutMode>();
    386 	cmdLine.outValue	= opts.getOption<opt::OutValue>();
    387 	cmdLine.filenames	= opts.getArgs();
    388 
    389 	return true;
    390 }
    391 
    392 int main (int argc, const char* const* argv)
    393 {
    394 	CommandLine cmdLine;
    395 
    396 	if (!parseCommandLine(cmdLine, argc, argv))
    397 		return -1;
    398 
    399 	try
    400 	{
    401 		bool compareOk = runCompare(cmdLine, std::cout);
    402 		return compareOk ? 0 : -1;
    403 	}
    404 	catch (const std::exception& e)
    405 	{
    406 		printf("FATAL ERROR: %s\n", e.what());
    407 		return -1;
    408 	}
    409 }
    410