Vulkan: Add wide-color tests
[platform/upstream/VK-GL-CTS.git] / executor / tools / xeTestLogCompare.cpp
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 }