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