1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // TestSuite:
7 //   Basic implementation of a test harness in ANGLE.
8 
9 #include "TestSuite.h"
10 
11 #include "common/debug.h"
12 #include "common/platform.h"
13 #include "common/string_utils.h"
14 #include "common/system_utils.h"
15 #include "util/Timer.h"
16 
17 #include <stdlib.h>
18 #include <time.h>
19 
20 #include <fstream>
21 #include <unordered_map>
22 
23 #include <gtest/gtest.h>
24 #include <rapidjson/document.h>
25 #include <rapidjson/filewritestream.h>
26 #include <rapidjson/istreamwrapper.h>
27 #include <rapidjson/prettywriter.h>
28 
29 // We directly call into a function to register the parameterized tests. This saves spinning up
30 // a subprocess with a new gtest filter.
31 #include <gtest/../../src/gtest-internal-inl.h>
32 
33 namespace js = rapidjson;
34 
35 namespace angle
36 {
37 namespace
38 {
39 constexpr char kBatchId[]              = "--batch-id=";
40 constexpr char kFilterFileArg[]        = "--filter-file=";
41 constexpr char kFlakyRetries[]         = "--flaky-retries=";
42 constexpr char kGTestListTests[]       = "--gtest_list_tests";
43 constexpr char kHistogramJsonFileArg[] = "--histogram-json-file=";
44 constexpr char kListTests[]            = "--list-tests";
45 constexpr char kPrintTestStdout[]      = "--print-test-stdout";
46 constexpr char kResultFileArg[]        = "--results-file=";
47 constexpr char kTestTimeoutArg[]       = "--test-timeout=";
48 constexpr char kDisableCrashHandler[]  = "--disable-crash-handler";
49 
50 constexpr char kStartedTestString[] = "[ RUN      ] ";
51 constexpr char kPassedTestString[]  = "[       OK ] ";
52 constexpr char kFailedTestString[]  = "[  FAILED  ] ";
53 
54 #if defined(NDEBUG)
55 constexpr int kDefaultTestTimeout = 20;
56 #else
57 constexpr int kDefaultTestTimeout  = 60;
58 #endif
59 #if defined(NDEBUG)
60 constexpr int kDefaultBatchTimeout = 240;
61 #else
62 constexpr int kDefaultBatchTimeout = 600;
63 #endif
64 constexpr int kDefaultBatchSize      = 256;
65 constexpr double kIdleMessageTimeout = 15.0;
66 constexpr int kDefaultMaxProcesses   = 16;
67 
ParseFlagValue(const char * flag,const char * argument)68 const char *ParseFlagValue(const char *flag, const char *argument)
69 {
70     if (strstr(argument, flag) == argument)
71     {
72         return argument + strlen(flag);
73     }
74 
75     return nullptr;
76 }
77 
ParseIntArg(const char * flag,const char * argument,int * valueOut)78 bool ParseIntArg(const char *flag, const char *argument, int *valueOut)
79 {
80     const char *value = ParseFlagValue(flag, argument);
81     if (!value)
82     {
83         return false;
84     }
85 
86     char *end            = nullptr;
87     const long longValue = strtol(value, &end, 10);
88 
89     if (*end != '\0')
90     {
91         printf("Error parsing integer flag value.\n");
92         exit(EXIT_FAILURE);
93     }
94 
95     if (longValue == LONG_MAX || longValue == LONG_MIN || static_cast<int>(longValue) != longValue)
96     {
97         printf("Overflow when parsing integer flag value.\n");
98         exit(EXIT_FAILURE);
99     }
100 
101     *valueOut = static_cast<int>(longValue);
102     return true;
103 }
104 
ParseIntArgNoDelete(const char * flag,const char * argument,int * valueOut)105 bool ParseIntArgNoDelete(const char *flag, const char *argument, int *valueOut)
106 {
107     ParseIntArg(flag, argument, valueOut);
108     return false;
109 }
110 
ParseFlag(const char * expected,const char * actual,bool * flagOut)111 bool ParseFlag(const char *expected, const char *actual, bool *flagOut)
112 {
113     if (strcmp(expected, actual) == 0)
114     {
115         *flagOut = true;
116         return true;
117     }
118     return false;
119 }
120 
ParseStringArg(const char * flag,const char * argument,std::string * valueOut)121 bool ParseStringArg(const char *flag, const char *argument, std::string *valueOut)
122 {
123     const char *value = ParseFlagValue(flag, argument);
124     if (!value)
125     {
126         return false;
127     }
128 
129     *valueOut = value;
130     return true;
131 }
132 
DeleteArg(int * argc,char ** argv,int argIndex)133 void DeleteArg(int *argc, char **argv, int argIndex)
134 {
135     // Shift the remainder of the argv list left by one.  Note that argv has (*argc + 1) elements,
136     // the last one always being NULL.  The following loop moves the trailing NULL element as well.
137     for (int index = argIndex; index < *argc; ++index)
138     {
139         argv[index] = argv[index + 1];
140     }
141     (*argc)--;
142 }
143 
AddArg(int * argc,char ** argv,const char * arg)144 void AddArg(int *argc, char **argv, const char *arg)
145 {
146     // This unsafe const_cast is necessary to work around gtest limitations.
147     argv[*argc]     = const_cast<char *>(arg);
148     argv[*argc + 1] = nullptr;
149     (*argc)++;
150 }
151 
ResultTypeToString(TestResultType type)152 const char *ResultTypeToString(TestResultType type)
153 {
154     switch (type)
155     {
156         case TestResultType::Crash:
157             return "CRASH";
158         case TestResultType::Fail:
159             return "FAIL";
160         case TestResultType::Pass:
161             return "PASS";
162         case TestResultType::NoResult:
163             return "SKIP";
164         case TestResultType::Timeout:
165             return "TIMEOUT";
166         case TestResultType::Unknown:
167             return "UNKNOWN";
168     }
169 }
170 
GetResultTypeFromString(const std::string & str)171 TestResultType GetResultTypeFromString(const std::string &str)
172 {
173     if (str == "CRASH")
174         return TestResultType::Crash;
175     if (str == "FAIL")
176         return TestResultType::Fail;
177     if (str == "PASS")
178         return TestResultType::Pass;
179     if (str == "SKIP")
180         return TestResultType::NoResult;
181     if (str == "TIMEOUT")
182         return TestResultType::Timeout;
183     return TestResultType::Unknown;
184 }
185 
ResultTypeToJSString(TestResultType type,js::Document::AllocatorType * allocator)186 js::Value ResultTypeToJSString(TestResultType type, js::Document::AllocatorType *allocator)
187 {
188     js::Value jsName;
189     jsName.SetString(ResultTypeToString(type), *allocator);
190     return jsName;
191 }
192 
WriteJsonFile(const std::string & outputFile,js::Document * doc)193 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
194 {
195     FILE *fp = fopen(outputFile.c_str(), "w");
196     if (!fp)
197     {
198         return false;
199     }
200 
201     constexpr size_t kBufferSize = 0xFFFF;
202     std::vector<char> writeBuffer(kBufferSize);
203     js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
204     js::PrettyWriter<js::FileWriteStream> writer(os);
205     if (!doc->Accept(writer))
206     {
207         fclose(fp);
208         return false;
209     }
210     fclose(fp);
211     return true;
212 }
213 
214 // Writes out a TestResults to the Chromium JSON Test Results format.
215 // https://chromium.googlesource.com/chromium/src.git/+/master/docs/testing/json_test_results_format.md
WriteResultsFile(bool interrupted,const TestResults & testResults,const std::string & outputFile,const char * testSuiteName)216 void WriteResultsFile(bool interrupted,
217                       const TestResults &testResults,
218                       const std::string &outputFile,
219                       const char *testSuiteName)
220 {
221     time_t ltime;
222     time(&ltime);
223     struct tm *timeinfo = gmtime(&ltime);
224     ltime               = mktime(timeinfo);
225 
226     uint64_t secondsSinceEpoch = static_cast<uint64_t>(ltime);
227 
228     js::Document doc;
229     doc.SetObject();
230 
231     js::Document::AllocatorType &allocator = doc.GetAllocator();
232 
233     doc.AddMember("interrupted", interrupted, allocator);
234     doc.AddMember("path_delimiter", ".", allocator);
235     doc.AddMember("version", 3, allocator);
236     doc.AddMember("seconds_since_epoch", secondsSinceEpoch, allocator);
237 
238     js::Value tests;
239     tests.SetObject();
240 
241     std::map<TestResultType, uint32_t> counts;
242 
243     for (const auto &resultIter : testResults.results)
244     {
245         const TestIdentifier &id = resultIter.first;
246         const TestResult &result = resultIter.second;
247 
248         js::Value jsResult;
249         jsResult.SetObject();
250 
251         counts[result.type]++;
252 
253         std::string actualResult;
254         for (uint32_t fail = 0; fail < result.flakyFailures; ++fail)
255         {
256             actualResult += "FAIL ";
257         }
258 
259         actualResult += ResultTypeToString(result.type);
260 
261         std::string expectedResult;
262         if (result.flakyFailures > 0)
263         {
264             expectedResult = "FAIL PASS";
265             jsResult.AddMember("is_flaky", true, allocator);
266         }
267         else
268         {
269             expectedResult = "PASS";
270         }
271 
272         jsResult.AddMember("actual", actualResult, allocator);
273         jsResult.AddMember("expected", expectedResult, allocator);
274 
275         if (result.type != TestResultType::Pass)
276         {
277             jsResult.AddMember("is_unexpected", true, allocator);
278         }
279 
280         js::Value times;
281         times.SetArray();
282         times.PushBack(result.elapsedTimeSeconds, allocator);
283 
284         jsResult.AddMember("times", times, allocator);
285 
286         char testName[500];
287         id.sprintfName(testName);
288         js::Value jsName;
289         jsName.SetString(testName, allocator);
290 
291         tests.AddMember(jsName, jsResult, allocator);
292     }
293 
294     js::Value numFailuresByType;
295     numFailuresByType.SetObject();
296 
297     for (const auto &countIter : counts)
298     {
299         TestResultType type = countIter.first;
300         uint32_t count      = countIter.second;
301 
302         js::Value jsCount(count);
303         numFailuresByType.AddMember(ResultTypeToJSString(type, &allocator), jsCount, allocator);
304     }
305 
306     doc.AddMember("num_failures_by_type", numFailuresByType, allocator);
307 
308     doc.AddMember("tests", tests, allocator);
309 
310     printf("Writing test results to %s\n", outputFile.c_str());
311 
312     if (!WriteJsonFile(outputFile, &doc))
313     {
314         printf("Error writing test results file.\n");
315     }
316 }
317 
WriteHistogramJson(const HistogramWriter & histogramWriter,const std::string & outputFile,const char * testSuiteName)318 void WriteHistogramJson(const HistogramWriter &histogramWriter,
319                         const std::string &outputFile,
320                         const char *testSuiteName)
321 {
322     js::Document doc;
323     doc.SetArray();
324 
325     histogramWriter.getAsJSON(&doc);
326 
327     printf("Writing histogram json to %s\n", outputFile.c_str());
328 
329     if (!WriteJsonFile(outputFile, &doc))
330     {
331         printf("Error writing histogram json file.\n");
332     }
333 }
334 
WriteOutputFiles(bool interrupted,const TestResults & testResults,const std::string & resultsFile,const HistogramWriter & histogramWriter,const std::string & histogramJsonOutputFile,const char * testSuiteName)335 void WriteOutputFiles(bool interrupted,
336                       const TestResults &testResults,
337                       const std::string &resultsFile,
338                       const HistogramWriter &histogramWriter,
339                       const std::string &histogramJsonOutputFile,
340                       const char *testSuiteName)
341 {
342     if (!resultsFile.empty())
343     {
344         WriteResultsFile(interrupted, testResults, resultsFile, testSuiteName);
345     }
346 
347     if (!histogramJsonOutputFile.empty())
348     {
349         WriteHistogramJson(histogramWriter, histogramJsonOutputFile, testSuiteName);
350     }
351 }
352 
UpdateCurrentTestResult(const testing::TestResult & resultIn,TestResults * resultsOut)353 void UpdateCurrentTestResult(const testing::TestResult &resultIn, TestResults *resultsOut)
354 {
355     TestResult &resultOut = resultsOut->results[resultsOut->currentTest];
356 
357     // Note: Crashes and Timeouts are detected by the crash handler and a watchdog thread.
358     if (resultIn.Skipped())
359     {
360         resultOut.type = TestResultType::NoResult;
361     }
362     else if (resultIn.Failed())
363     {
364         resultOut.type = TestResultType::Fail;
365     }
366     else
367     {
368         resultOut.type = TestResultType::Pass;
369     }
370 
371     resultOut.elapsedTimeSeconds = resultsOut->currentTestTimer.getElapsedTime();
372 }
373 
GetTestIdentifier(const testing::TestInfo & testInfo)374 TestIdentifier GetTestIdentifier(const testing::TestInfo &testInfo)
375 {
376     return {testInfo.test_suite_name(), testInfo.name()};
377 }
378 
379 class TestEventListener : public testing::EmptyTestEventListener
380 {
381   public:
382     // Note: TestResults is owned by the TestSuite. It should outlive TestEventListener.
TestEventListener(const std::string & resultsFile,const std::string & histogramJsonFile,const char * testSuiteName,TestResults * testResults,HistogramWriter * histogramWriter)383     TestEventListener(const std::string &resultsFile,
384                       const std::string &histogramJsonFile,
385                       const char *testSuiteName,
386                       TestResults *testResults,
387                       HistogramWriter *histogramWriter)
388         : mResultsFile(resultsFile),
389           mHistogramJsonFile(histogramJsonFile),
390           mTestSuiteName(testSuiteName),
391           mTestResults(testResults),
392           mHistogramWriter(histogramWriter)
393     {}
394 
OnTestStart(const testing::TestInfo & testInfo)395     void OnTestStart(const testing::TestInfo &testInfo) override
396     {
397         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
398         mTestResults->currentTest = GetTestIdentifier(testInfo);
399         mTestResults->currentTestTimer.start();
400     }
401 
OnTestEnd(const testing::TestInfo & testInfo)402     void OnTestEnd(const testing::TestInfo &testInfo) override
403     {
404         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
405         mTestResults->currentTestTimer.stop();
406         const testing::TestResult &resultIn = *testInfo.result();
407         UpdateCurrentTestResult(resultIn, mTestResults);
408         mTestResults->currentTest = TestIdentifier();
409     }
410 
OnTestProgramEnd(const testing::UnitTest & testProgramInfo)411     void OnTestProgramEnd(const testing::UnitTest &testProgramInfo) override
412     {
413         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
414         mTestResults->allDone = true;
415         WriteOutputFiles(false, *mTestResults, mResultsFile, *mHistogramWriter, mHistogramJsonFile,
416                          mTestSuiteName);
417     }
418 
419   private:
420     std::string mResultsFile;
421     std::string mHistogramJsonFile;
422     const char *mTestSuiteName;
423     TestResults *mTestResults;
424     HistogramWriter *mHistogramWriter;
425 };
426 
IsTestDisabled(const testing::TestInfo & testInfo)427 bool IsTestDisabled(const testing::TestInfo &testInfo)
428 {
429     return ::strstr(testInfo.name(), "DISABLED_") == testInfo.name();
430 }
431 
432 using TestIdentifierFilter = std::function<bool(const TestIdentifier &id)>;
433 
FilterTests(std::map<TestIdentifier,FileLine> * fileLinesOut,TestIdentifierFilter filter,bool alsoRunDisabledTests)434 std::vector<TestIdentifier> FilterTests(std::map<TestIdentifier, FileLine> *fileLinesOut,
435                                         TestIdentifierFilter filter,
436                                         bool alsoRunDisabledTests)
437 {
438     std::vector<TestIdentifier> tests;
439 
440     const testing::UnitTest &testProgramInfo = *testing::UnitTest::GetInstance();
441     for (int suiteIndex = 0; suiteIndex < testProgramInfo.total_test_suite_count(); ++suiteIndex)
442     {
443         const testing::TestSuite &testSuite = *testProgramInfo.GetTestSuite(suiteIndex);
444         for (int testIndex = 0; testIndex < testSuite.total_test_count(); ++testIndex)
445         {
446             const testing::TestInfo &testInfo = *testSuite.GetTestInfo(testIndex);
447             TestIdentifier id                 = GetTestIdentifier(testInfo);
448             if (filter(id) && (!IsTestDisabled(testInfo) || alsoRunDisabledTests))
449             {
450                 tests.emplace_back(id);
451 
452                 if (fileLinesOut)
453                 {
454                     (*fileLinesOut)[id] = {testInfo.file(), testInfo.line()};
455                 }
456             }
457         }
458     }
459 
460     return tests;
461 }
462 
GetFilteredTests(std::map<TestIdentifier,FileLine> * fileLinesOut,bool alsoRunDisabledTests)463 std::vector<TestIdentifier> GetFilteredTests(std::map<TestIdentifier, FileLine> *fileLinesOut,
464                                              bool alsoRunDisabledTests)
465 {
466     TestIdentifierFilter gtestIDFilter = [](const TestIdentifier &id) {
467         return testing::internal::UnitTestOptions::FilterMatchesTest(id.testSuiteName, id.testName);
468     };
469 
470     return FilterTests(fileLinesOut, gtestIDFilter, alsoRunDisabledTests);
471 }
472 
GetShardTests(const std::vector<TestIdentifier> & allTests,int shardIndex,int shardCount,std::map<TestIdentifier,FileLine> * fileLinesOut,bool alsoRunDisabledTests)473 std::vector<TestIdentifier> GetShardTests(const std::vector<TestIdentifier> &allTests,
474                                           int shardIndex,
475                                           int shardCount,
476                                           std::map<TestIdentifier, FileLine> *fileLinesOut,
477                                           bool alsoRunDisabledTests)
478 {
479     std::vector<TestIdentifier> shardTests;
480 
481     for (int testIndex = shardIndex; testIndex < static_cast<int>(allTests.size());
482          testIndex += shardCount)
483     {
484         shardTests.emplace_back(allTests[testIndex]);
485     }
486 
487     return shardTests;
488 }
489 
GetTestFilter(const std::vector<TestIdentifier> & tests)490 std::string GetTestFilter(const std::vector<TestIdentifier> &tests)
491 {
492     std::stringstream filterStream;
493 
494     filterStream << "--gtest_filter=";
495 
496     for (size_t testIndex = 0; testIndex < tests.size(); ++testIndex)
497     {
498         if (testIndex != 0)
499         {
500             filterStream << ":";
501         }
502 
503         filterStream << tests[testIndex];
504     }
505 
506     return filterStream.str();
507 }
508 
ParseTestSuiteName(const char * executable)509 std::string ParseTestSuiteName(const char *executable)
510 {
511     const char *baseNameStart = strrchr(executable, GetPathSeparator());
512     if (!baseNameStart)
513     {
514         baseNameStart = executable;
515     }
516     else
517     {
518         baseNameStart++;
519     }
520 
521     const char *suffix = GetExecutableExtension();
522     size_t suffixLen   = strlen(suffix);
523     if (suffixLen == 0)
524     {
525         return baseNameStart;
526     }
527 
528     if (!EndsWith(baseNameStart, suffix))
529     {
530         return baseNameStart;
531     }
532 
533     return std::string(baseNameStart, baseNameStart + strlen(baseNameStart) - suffixLen);
534 }
535 
GetTestResultsFromJSON(const js::Document & document,TestResults * resultsOut)536 bool GetTestResultsFromJSON(const js::Document &document, TestResults *resultsOut)
537 {
538     if (!document.HasMember("tests") || !document["tests"].IsObject())
539     {
540         return false;
541     }
542 
543     const js::Value::ConstObject &tests = document["tests"].GetObject();
544     for (auto iter = tests.MemberBegin(); iter != tests.MemberEnd(); ++iter)
545     {
546         // Get test identifier.
547         const js::Value &name = iter->name;
548         if (!name.IsString())
549         {
550             return false;
551         }
552 
553         TestIdentifier id;
554         if (!TestIdentifier::ParseFromString(name.GetString(), &id))
555         {
556             return false;
557         }
558 
559         // Get test result.
560         const js::Value &value = iter->value;
561         if (!value.IsObject())
562         {
563             return false;
564         }
565 
566         const js::Value::ConstObject &obj = value.GetObject();
567         if (!obj.HasMember("expected") || !obj.HasMember("actual"))
568         {
569             return false;
570         }
571 
572         const js::Value &expected = obj["expected"];
573         const js::Value &actual   = obj["actual"];
574 
575         if (!expected.IsString() || !actual.IsString())
576         {
577             return false;
578         }
579 
580         const std::string actualStr = actual.GetString();
581 
582         TestResultType resultType = TestResultType::Unknown;
583         int flakyFailures         = 0;
584         if (actualStr.find(' '))
585         {
586             std::istringstream strstr(actualStr);
587             std::string token;
588             while (std::getline(strstr, token, ' '))
589             {
590                 resultType = GetResultTypeFromString(token);
591                 if (resultType == TestResultType::Unknown)
592                 {
593                     printf("Failed to parse result type.\n");
594                     return false;
595                 }
596                 if (resultType != TestResultType::Pass)
597                 {
598                     flakyFailures++;
599                 }
600             }
601         }
602         else
603         {
604             resultType = GetResultTypeFromString(actualStr);
605             if (resultType == TestResultType::Unknown)
606             {
607                 printf("Failed to parse result type.\n");
608                 return false;
609             }
610         }
611 
612         double elapsedTimeSeconds = 0.0;
613         if (obj.HasMember("times"))
614         {
615             const js::Value &times = obj["times"];
616             if (!times.IsArray())
617             {
618                 return false;
619             }
620 
621             const js::Value::ConstArray &timesArray = times.GetArray();
622             if (timesArray.Size() != 1 || !timesArray[0].IsDouble())
623             {
624                 return false;
625             }
626 
627             elapsedTimeSeconds = timesArray[0].GetDouble();
628         }
629 
630         TestResult &result        = resultsOut->results[id];
631         result.elapsedTimeSeconds = elapsedTimeSeconds;
632         result.type               = resultType;
633         result.flakyFailures      = flakyFailures;
634     }
635 
636     return true;
637 }
638 
MergeTestResults(TestResults * input,TestResults * output,int flakyRetries)639 bool MergeTestResults(TestResults *input, TestResults *output, int flakyRetries)
640 {
641     for (auto &resultsIter : input->results)
642     {
643         const TestIdentifier &id = resultsIter.first;
644         TestResult &inputResult  = resultsIter.second;
645         TestResult &outputResult = output->results[id];
646 
647         if (inputResult.type != TestResultType::NoResult)
648         {
649             if (outputResult.type != TestResultType::NoResult)
650             {
651                 printf("Warning: duplicate entry for %s.%s.\n", id.testSuiteName.c_str(),
652                        id.testName.c_str());
653                 return false;
654             }
655 
656             // Mark the tests that haven't exhausted their retries as 'SKIP'. This makes ANGLE
657             // attempt the test again.
658             uint32_t runCount = outputResult.flakyFailures + 1;
659             if (inputResult.type != TestResultType::Pass &&
660                 runCount < static_cast<uint32_t>(flakyRetries))
661             {
662                 inputResult.type = TestResultType::NoResult;
663                 outputResult.flakyFailures++;
664             }
665             else
666             {
667                 outputResult.elapsedTimeSeconds = inputResult.elapsedTimeSeconds;
668                 outputResult.type               = inputResult.type;
669             }
670         }
671     }
672 
673     return true;
674 }
675 
PrintTestOutputSnippet(const TestIdentifier & id,const TestResult & result,const std::string & fullOutput)676 void PrintTestOutputSnippet(const TestIdentifier &id,
677                             const TestResult &result,
678                             const std::string &fullOutput)
679 {
680     std::stringstream nameStream;
681     nameStream << id;
682     std::string fullName = nameStream.str();
683 
684     size_t runPos = fullOutput.find(std::string(kStartedTestString) + fullName);
685     if (runPos == std::string::npos)
686     {
687         printf("Cannot locate test output snippet.\n");
688         return;
689     }
690 
691     size_t endPos = fullOutput.find(std::string(kFailedTestString) + fullName, runPos);
692     // Only clip the snippet to the "OK" message if the test really
693     // succeeded. It still might have e.g. crashed after printing it.
694     if (endPos == std::string::npos && result.type == TestResultType::Pass)
695     {
696         endPos = fullOutput.find(std::string(kPassedTestString) + fullName, runPos);
697     }
698     if (endPos != std::string::npos)
699     {
700         size_t newline_pos = fullOutput.find("\n", endPos);
701         if (newline_pos != std::string::npos)
702             endPos = newline_pos + 1;
703     }
704 
705     std::cout << "\n";
706     if (endPos != std::string::npos)
707     {
708         std::cout << fullOutput.substr(runPos, endPos - runPos);
709     }
710     else
711     {
712         std::cout << fullOutput.substr(runPos);
713     }
714 }
715 
GetConfigNameFromTestIdentifier(const TestIdentifier & id)716 std::string GetConfigNameFromTestIdentifier(const TestIdentifier &id)
717 {
718     size_t slashPos = id.testName.find('/');
719     if (slashPos == std::string::npos)
720     {
721         return "default";
722     }
723 
724     size_t doubleUnderscorePos = id.testName.find("__");
725     if (doubleUnderscorePos == std::string::npos)
726     {
727         std::string configName = id.testName.substr(slashPos + 1);
728 
729         if (!BeginsWith(configName, "ES"))
730         {
731             return "default";
732         }
733 
734         return configName;
735     }
736     else
737     {
738         return id.testName.substr(slashPos + 1, doubleUnderscorePos - slashPos - 1);
739     }
740 }
741 
BatchTests(const std::vector<TestIdentifier> & tests,int batchSize)742 TestQueue BatchTests(const std::vector<TestIdentifier> &tests, int batchSize)
743 {
744     // First sort tests by configuration.
745     std::unordered_map<std::string, std::vector<TestIdentifier>> testsSortedByConfig;
746     for (const TestIdentifier &id : tests)
747     {
748         std::string config = GetConfigNameFromTestIdentifier(id);
749         testsSortedByConfig[config].push_back(id);
750     }
751 
752     // Then group into batches by 'batchSize'.
753     TestQueue testQueue;
754     for (const auto &configAndIds : testsSortedByConfig)
755     {
756         const std::vector<TestIdentifier> &configTests = configAndIds.second;
757 
758         // Count the number of batches needed for this config.
759         int batchesForConfig = static_cast<int>(configTests.size() + batchSize - 1) / batchSize;
760 
761         // Create batches with striping to split up slow tests.
762         for (int batchIndex = 0; batchIndex < batchesForConfig; ++batchIndex)
763         {
764             std::vector<TestIdentifier> batchTests;
765             for (size_t testIndex = batchIndex; testIndex < configTests.size();
766                  testIndex += batchesForConfig)
767             {
768                 batchTests.push_back(configTests[testIndex]);
769             }
770             testQueue.emplace(std::move(batchTests));
771             ASSERT(batchTests.empty());
772         }
773     }
774 
775     return testQueue;
776 }
777 
ListTests(const std::map<TestIdentifier,TestResult> & resultsMap)778 void ListTests(const std::map<TestIdentifier, TestResult> &resultsMap)
779 {
780     std::map<std::string, std::vector<std::string>> suites;
781 
782     std::cout << "Tests list:\n";
783 
784     for (const auto &resultIt : resultsMap)
785     {
786         const TestIdentifier &id = resultIt.first;
787         std::cout << id << "\n";
788     }
789 }
790 
791 // Prints the names of the tests matching the user-specified filter flag.
792 // This matches the output from googletest/src/gtest.cc but is much much faster for large filters.
793 // See http://anglebug.com/5164
GTestListTests(const std::map<TestIdentifier,TestResult> & resultsMap)794 void GTestListTests(const std::map<TestIdentifier, TestResult> &resultsMap)
795 {
796     std::map<std::string, std::vector<std::string>> suites;
797 
798     for (const auto &resultIt : resultsMap)
799     {
800         const TestIdentifier &id = resultIt.first;
801         suites[id.testSuiteName].push_back(id.testName);
802     }
803 
804     for (const auto &testSuiteIt : suites)
805     {
806         bool printedTestSuiteName = false;
807 
808         const std::string &suiteName              = testSuiteIt.first;
809         const std::vector<std::string> &testNames = testSuiteIt.second;
810 
811         for (const std::string &testName : testNames)
812         {
813             if (!printedTestSuiteName)
814             {
815                 printedTestSuiteName = true;
816                 printf("%s.\n", suiteName.c_str());
817             }
818             printf("  %s\n", testName.c_str());
819         }
820     }
821 }
822 }  // namespace
823 
824 // static
825 TestSuite *TestSuite::mInstance = nullptr;
826 
827 TestIdentifier::TestIdentifier() = default;
828 
TestIdentifier(const std::string & suiteNameIn,const std::string & nameIn)829 TestIdentifier::TestIdentifier(const std::string &suiteNameIn, const std::string &nameIn)
830     : testSuiteName(suiteNameIn), testName(nameIn)
831 {}
832 
833 TestIdentifier::TestIdentifier(const TestIdentifier &other) = default;
834 
835 TestIdentifier::~TestIdentifier() = default;
836 
837 TestIdentifier &TestIdentifier::operator=(const TestIdentifier &other) = default;
838 
sprintfName(char * outBuffer) const839 void TestIdentifier::sprintfName(char *outBuffer) const
840 {
841     sprintf(outBuffer, "%s.%s", testSuiteName.c_str(), testName.c_str());
842 }
843 
844 // static
ParseFromString(const std::string & str,TestIdentifier * idOut)845 bool TestIdentifier::ParseFromString(const std::string &str, TestIdentifier *idOut)
846 {
847     size_t separator = str.find(".");
848     if (separator == std::string::npos)
849     {
850         return false;
851     }
852 
853     idOut->testSuiteName = str.substr(0, separator);
854     idOut->testName      = str.substr(separator + 1, str.length() - separator - 1);
855     return true;
856 }
857 
858 TestResults::TestResults() = default;
859 
860 TestResults::~TestResults() = default;
861 
862 ProcessInfo::ProcessInfo() = default;
863 
operator =(ProcessInfo && rhs)864 ProcessInfo &ProcessInfo::operator=(ProcessInfo &&rhs)
865 {
866     process         = std::move(rhs.process);
867     testsInBatch    = std::move(rhs.testsInBatch);
868     resultsFileName = std::move(rhs.resultsFileName);
869     filterFileName  = std::move(rhs.filterFileName);
870     commandLine     = std::move(rhs.commandLine);
871     filterString    = std::move(rhs.filterString);
872     return *this;
873 }
874 
875 ProcessInfo::~ProcessInfo() = default;
876 
ProcessInfo(ProcessInfo && other)877 ProcessInfo::ProcessInfo(ProcessInfo &&other)
878 {
879     *this = std::move(other);
880 }
881 
TestSuite(int * argc,char ** argv)882 TestSuite::TestSuite(int *argc, char **argv)
883     : mShardCount(-1),
884       mShardIndex(-1),
885       mBotMode(false),
886       mDebugTestGroups(false),
887       mGTestListTests(false),
888       mListTests(false),
889       mPrintTestStdout(false),
890       mDisableCrashHandler(false),
891       mBatchSize(kDefaultBatchSize),
892       mCurrentResultCount(0),
893       mTotalResultCount(0),
894       mMaxProcesses(std::min(NumberOfProcessors(), kDefaultMaxProcesses)),
895       mTestTimeout(kDefaultTestTimeout),
896       mBatchTimeout(kDefaultBatchTimeout),
897       mBatchId(-1),
898       mFlakyRetries(0)
899 {
900     ASSERT(mInstance == nullptr);
901     mInstance = this;
902 
903     Optional<int> filterArgIndex;
904     bool alsoRunDisabledTests = false;
905 
906 #if defined(ANGLE_PLATFORM_WINDOWS)
907     testing::GTEST_FLAG(catch_exceptions) = false;
908 #endif
909 
910     if (*argc <= 0)
911     {
912         printf("Missing test arguments.\n");
913         exit(EXIT_FAILURE);
914     }
915 
916     mTestExecutableName = argv[0];
917     mTestSuiteName      = ParseTestSuiteName(mTestExecutableName.c_str());
918 
919     for (int argIndex = 1; argIndex < *argc;)
920     {
921         if (parseSingleArg(argv[argIndex]))
922         {
923             DeleteArg(argc, argv, argIndex);
924             continue;
925         }
926 
927         if (ParseFlagValue("--gtest_filter=", argv[argIndex]))
928         {
929             filterArgIndex = argIndex;
930         }
931         else
932         {
933             // Don't include disabled tests in test lists unless the user asks for them.
934             if (strcmp("--gtest_also_run_disabled_tests", argv[argIndex]) == 0)
935             {
936                 alsoRunDisabledTests = true;
937             }
938 
939             mChildProcessArgs.push_back(argv[argIndex]);
940         }
941         ++argIndex;
942     }
943 
944     if (!mDisableCrashHandler)
945     {
946         // Note that the crash callback must be owned and not use global constructors.
947         mCrashCallback = [this]() { onCrashOrTimeout(TestResultType::Crash); };
948         InitCrashHandler(&mCrashCallback);
949     }
950 
951     std::string envShardIndex = angle::GetEnvironmentVar("GTEST_SHARD_INDEX");
952     if (!envShardIndex.empty())
953     {
954         angle::UnsetEnvironmentVar("GTEST_SHARD_INDEX");
955         if (mShardIndex == -1)
956         {
957             std::stringstream shardIndexStream(envShardIndex);
958             shardIndexStream >> mShardIndex;
959         }
960     }
961 
962     std::string envTotalShards = angle::GetEnvironmentVar("GTEST_TOTAL_SHARDS");
963     if (!envTotalShards.empty())
964     {
965         angle::UnsetEnvironmentVar("GTEST_TOTAL_SHARDS");
966         if (mShardCount == -1)
967         {
968             std::stringstream shardCountStream(envTotalShards);
969             shardCountStream >> mShardCount;
970         }
971     }
972 
973     if ((mShardIndex == -1) != (mShardCount == -1))
974     {
975         printf("Shard index and shard count must be specified together.\n");
976         exit(EXIT_FAILURE);
977     }
978 
979     if (!mFilterFile.empty())
980     {
981         if (filterArgIndex.valid())
982         {
983             printf("Cannot use gtest_filter in conjunction with a filter file.\n");
984             exit(EXIT_FAILURE);
985         }
986 
987         uint32_t fileSize = 0;
988         if (!GetFileSize(mFilterFile.c_str(), &fileSize))
989         {
990             printf("Error getting filter file size: %s\n", mFilterFile.c_str());
991             exit(EXIT_FAILURE);
992         }
993 
994         std::vector<char> fileContents(fileSize + 1, 0);
995         if (!ReadEntireFileToString(mFilterFile.c_str(), fileContents.data(), fileSize))
996         {
997             printf("Error loading filter file: %s\n", mFilterFile.c_str());
998             exit(EXIT_FAILURE);
999         }
1000         mFilterString.assign(fileContents.data());
1001 
1002         if (mFilterString.substr(0, strlen("--gtest_filter=")) != std::string("--gtest_filter="))
1003         {
1004             printf("Filter file must start with \"--gtest_filter=\".\n");
1005             exit(EXIT_FAILURE);
1006         }
1007 
1008         // Note that we only add a filter string if we previously deleted a shader filter file
1009         // argument. So we will have space for the new filter string in argv.
1010         AddArg(argc, argv, mFilterString.c_str());
1011     }
1012 
1013     // Call into gtest internals to force parameterized test name registration.
1014     testing::internal::UnitTestImpl *impl = testing::internal::GetUnitTestImpl();
1015     impl->RegisterParameterizedTests();
1016 
1017     // Initialize internal GoogleTest filter arguments so we can call "FilterMatchesTest".
1018     testing::internal::ParseGoogleTestFlagsOnly(argc, argv);
1019 
1020     std::vector<TestIdentifier> testSet = GetFilteredTests(&mTestFileLines, alsoRunDisabledTests);
1021 
1022     if (mShardCount == 0)
1023     {
1024         printf("Shard count must be > 0.\n");
1025         exit(EXIT_FAILURE);
1026     }
1027     else if (mShardCount > 0)
1028     {
1029         if (mShardIndex >= mShardCount)
1030         {
1031             printf("Shard index must be less than shard count.\n");
1032             exit(EXIT_FAILURE);
1033         }
1034 
1035         // If there's only one shard, we can use the testSet as defined above.
1036         if (mShardCount > 1)
1037         {
1038             testSet = GetShardTests(testSet, mShardIndex, mShardCount, &mTestFileLines,
1039                                     alsoRunDisabledTests);
1040 
1041             if (!mBotMode)
1042             {
1043                 mFilterString = GetTestFilter(testSet);
1044 
1045                 if (filterArgIndex.valid())
1046                 {
1047                     argv[filterArgIndex.value()] = const_cast<char *>(mFilterString.c_str());
1048                 }
1049                 else
1050                 {
1051                     // Note that we only add a filter string if we previously deleted a shard
1052                     // index/count argument. So we will have space for the new filter string in
1053                     // argv.
1054                     AddArg(argc, argv, mFilterString.c_str());
1055                 }
1056 
1057                 // Force-re-initialize GoogleTest flags to load the shard filter.
1058                 testing::internal::ParseGoogleTestFlagsOnly(argc, argv);
1059             }
1060         }
1061     }
1062 
1063     if (mBotMode)
1064     {
1065         // Split up test batches.
1066         mTestQueue = BatchTests(testSet, mBatchSize);
1067 
1068         if (mDebugTestGroups)
1069         {
1070             std::cout << "Test Groups:\n";
1071 
1072             while (!mTestQueue.empty())
1073             {
1074                 const std::vector<TestIdentifier> &tests = mTestQueue.front();
1075                 std::cout << GetConfigNameFromTestIdentifier(tests[0]) << " ("
1076                           << static_cast<int>(tests.size()) << ")\n";
1077                 mTestQueue.pop();
1078             }
1079 
1080             exit(EXIT_SUCCESS);
1081         }
1082     }
1083 
1084     testing::InitGoogleTest(argc, argv);
1085 
1086     mTotalResultCount = testSet.size();
1087 
1088     if ((mBotMode || !mResultsDirectory.empty()) && mResultsFile.empty())
1089     {
1090         // Create a default output file in bot mode.
1091         mResultsFile = "output.json";
1092     }
1093 
1094     if (!mResultsDirectory.empty())
1095     {
1096         std::stringstream resultFileName;
1097         resultFileName << mResultsDirectory << GetPathSeparator() << mResultsFile;
1098         mResultsFile = resultFileName.str();
1099     }
1100 
1101     if (!mBotMode)
1102     {
1103         testing::TestEventListeners &listeners = testing::UnitTest::GetInstance()->listeners();
1104         listeners.Append(new TestEventListener(mResultsFile, mHistogramJsonFile,
1105                                                mTestSuiteName.c_str(), &mTestResults,
1106                                                &mHistogramWriter));
1107 
1108         for (const TestIdentifier &id : testSet)
1109         {
1110             mTestResults.results[id].type = TestResultType::NoResult;
1111         }
1112     }
1113 }
1114 
~TestSuite()1115 TestSuite::~TestSuite()
1116 {
1117     if (mWatchdogThread.joinable())
1118     {
1119         mWatchdogThread.detach();
1120     }
1121     TerminateCrashHandler();
1122 }
1123 
parseSingleArg(const char * argument)1124 bool TestSuite::parseSingleArg(const char *argument)
1125 {
1126     // Note: Flags should be documented in README.md.
1127     return (ParseIntArg("--shard-count=", argument, &mShardCount) ||
1128             ParseIntArg("--shard-index=", argument, &mShardIndex) ||
1129             ParseIntArg("--batch-size=", argument, &mBatchSize) ||
1130             ParseIntArg("--max-processes=", argument, &mMaxProcesses) ||
1131             ParseIntArg(kTestTimeoutArg, argument, &mTestTimeout) ||
1132             ParseIntArg("--batch-timeout=", argument, &mBatchTimeout) ||
1133             ParseIntArg(kFlakyRetries, argument, &mFlakyRetries) ||
1134             // Other test functions consume the batch ID, so keep it in the list.
1135             ParseIntArgNoDelete(kBatchId, argument, &mBatchId) ||
1136             ParseStringArg("--results-directory=", argument, &mResultsDirectory) ||
1137             ParseStringArg(kResultFileArg, argument, &mResultsFile) ||
1138             ParseStringArg("--isolated-script-test-output=", argument, &mResultsFile) ||
1139             ParseStringArg(kFilterFileArg, argument, &mFilterFile) ||
1140             ParseStringArg(kHistogramJsonFileArg, argument, &mHistogramJsonFile) ||
1141             ParseStringArg("--isolated-script-test-perf-output=", argument, &mHistogramJsonFile) ||
1142             ParseFlag("--bot-mode", argument, &mBotMode) ||
1143             ParseFlag("--debug-test-groups", argument, &mDebugTestGroups) ||
1144             ParseFlag(kGTestListTests, argument, &mGTestListTests) ||
1145             ParseFlag(kListTests, argument, &mListTests) ||
1146             ParseFlag(kPrintTestStdout, argument, &mPrintTestStdout) ||
1147             ParseFlag(kDisableCrashHandler, argument, &mDisableCrashHandler));
1148 }
1149 
onCrashOrTimeout(TestResultType crashOrTimeout)1150 void TestSuite::onCrashOrTimeout(TestResultType crashOrTimeout)
1151 {
1152     std::lock_guard<std::mutex> guard(mTestResults.currentTestMutex);
1153     if (mTestResults.currentTest.valid())
1154     {
1155         TestResult &result        = mTestResults.results[mTestResults.currentTest];
1156         result.type               = crashOrTimeout;
1157         result.elapsedTimeSeconds = mTestResults.currentTestTimer.getElapsedTime();
1158     }
1159 
1160     if (mResultsFile.empty())
1161     {
1162         printf("No results file specified.\n");
1163         return;
1164     }
1165 
1166     WriteOutputFiles(true, mTestResults, mResultsFile, mHistogramWriter, mHistogramJsonFile,
1167                      mTestSuiteName.c_str());
1168 }
1169 
launchChildTestProcess(uint32_t batchId,const std::vector<TestIdentifier> & testsInBatch)1170 bool TestSuite::launchChildTestProcess(uint32_t batchId,
1171                                        const std::vector<TestIdentifier> &testsInBatch)
1172 {
1173     constexpr uint32_t kMaxPath = 1000;
1174 
1175     // Create a temporary file to store the test list
1176     ProcessInfo processInfo;
1177 
1178     char filterBuffer[kMaxPath] = {};
1179     if (!CreateTemporaryFile(filterBuffer, kMaxPath))
1180     {
1181         std::cerr << "Error creating temporary file for test list.\n";
1182         return false;
1183     }
1184     processInfo.filterFileName.assign(filterBuffer);
1185 
1186     std::string filterString = GetTestFilter(testsInBatch);
1187 
1188     FILE *fp = fopen(processInfo.filterFileName.c_str(), "w");
1189     if (!fp)
1190     {
1191         std::cerr << "Error opening temporary file for test list.\n";
1192         return false;
1193     }
1194     fprintf(fp, "%s", filterString.c_str());
1195     fclose(fp);
1196 
1197     processInfo.filterString = filterString;
1198 
1199     std::string filterFileArg = kFilterFileArg + processInfo.filterFileName;
1200 
1201     // Create a temporary file to store the test output.
1202     char resultsBuffer[kMaxPath] = {};
1203     if (!CreateTemporaryFile(resultsBuffer, kMaxPath))
1204     {
1205         std::cerr << "Error creating temporary file for test list.\n";
1206         return false;
1207     }
1208     processInfo.resultsFileName.assign(resultsBuffer);
1209 
1210     std::string resultsFileArg = kResultFileArg + processInfo.resultsFileName;
1211 
1212     // Construct commandline for child process.
1213     std::vector<const char *> args;
1214 
1215     args.push_back(mTestExecutableName.c_str());
1216     args.push_back(filterFileArg.c_str());
1217     args.push_back(resultsFileArg.c_str());
1218 
1219     std::stringstream batchIdStream;
1220     batchIdStream << kBatchId << batchId;
1221     std::string batchIdString = batchIdStream.str();
1222     args.push_back(batchIdString.c_str());
1223 
1224     for (const std::string &arg : mChildProcessArgs)
1225     {
1226         args.push_back(arg.c_str());
1227     }
1228 
1229     if (mDisableCrashHandler)
1230     {
1231         args.push_back(kDisableCrashHandler);
1232     }
1233 
1234     std::string timeoutStr;
1235     if (mTestTimeout != kDefaultTestTimeout)
1236     {
1237         std::stringstream timeoutStream;
1238         timeoutStream << kTestTimeoutArg << mTestTimeout;
1239         timeoutStr = timeoutStream.str();
1240         args.push_back(timeoutStr.c_str());
1241     }
1242 
1243     // Launch child process and wait for completion.
1244     processInfo.process = LaunchProcess(args, true, true);
1245 
1246     if (!processInfo.process->started())
1247     {
1248         std::cerr << "Error launching child process.\n";
1249         return false;
1250     }
1251 
1252     std::stringstream commandLineStr;
1253     for (const char *arg : args)
1254     {
1255         commandLineStr << arg << " ";
1256     }
1257 
1258     processInfo.commandLine  = commandLineStr.str();
1259     processInfo.testsInBatch = testsInBatch;
1260     mCurrentProcesses.emplace_back(std::move(processInfo));
1261     return true;
1262 }
1263 
ParseTestIdentifierAndSetResult(const std::string & testName,TestResultType result,TestResults * results)1264 void ParseTestIdentifierAndSetResult(const std::string &testName,
1265                                      TestResultType result,
1266                                      TestResults *results)
1267 {
1268     // Trim off any whitespace + extra stuff at the end of the string.
1269     std::string modifiedTestName = testName.substr(0, testName.find(' '));
1270     modifiedTestName             = modifiedTestName.substr(0, testName.find('\r'));
1271     TestIdentifier id;
1272     bool ok = TestIdentifier::ParseFromString(modifiedTestName, &id);
1273     ASSERT(ok);
1274     results->results[id] = {result};
1275 }
1276 
finishProcess(ProcessInfo * processInfo)1277 bool TestSuite::finishProcess(ProcessInfo *processInfo)
1278 {
1279     // Get test results and merge into master list.
1280     TestResults batchResults;
1281 
1282     if (!GetTestResultsFromFile(processInfo->resultsFileName.c_str(), &batchResults))
1283     {
1284         std::cerr << "Warning: could not find test results file from child process.\n";
1285 
1286         // First assume all tests get skipped.
1287         for (const TestIdentifier &id : processInfo->testsInBatch)
1288         {
1289             batchResults.results[id] = {TestResultType::NoResult};
1290         }
1291 
1292         // Attempt to reconstruct passing list from stdout snippets.
1293         const std::string &batchStdout = processInfo->process->getStdout();
1294         std::istringstream linesStream(batchStdout);
1295 
1296         std::string line;
1297         while (std::getline(linesStream, line))
1298         {
1299             size_t startPos = line.find(kStartedTestString);
1300             size_t failPos  = line.find(kFailedTestString);
1301             size_t passPos  = line.find(kPassedTestString);
1302 
1303             if (startPos != std::string::npos)
1304             {
1305                 // Assume a test that's started crashed until we see it completed.
1306                 std::string testName = line.substr(strlen(kStartedTestString));
1307                 ParseTestIdentifierAndSetResult(testName, TestResultType::Crash, &batchResults);
1308             }
1309             else if (failPos != std::string::npos)
1310             {
1311                 std::string testName = line.substr(strlen(kFailedTestString));
1312                 ParseTestIdentifierAndSetResult(testName, TestResultType::Fail, &batchResults);
1313             }
1314             else if (passPos != std::string::npos)
1315             {
1316                 std::string testName = line.substr(strlen(kPassedTestString));
1317                 ParseTestIdentifierAndSetResult(testName, TestResultType::Pass, &batchResults);
1318             }
1319         }
1320     }
1321 
1322     if (!MergeTestResults(&batchResults, &mTestResults, mFlakyRetries))
1323     {
1324         std::cerr << "Error merging batch test results.\n";
1325         return false;
1326     }
1327 
1328     if (!batchResults.results.empty())
1329     {
1330         const TestIdentifier &id = batchResults.results.begin()->first;
1331         std::string config       = GetConfigNameFromTestIdentifier(id);
1332         printf("Completed batch with config: %s\n", config.c_str());
1333 
1334         for (const auto &resultIter : batchResults.results)
1335         {
1336             const TestResult &result = resultIter.second;
1337             if (result.type != TestResultType::NoResult && result.type != TestResultType::Pass)
1338             {
1339                 printf("To reproduce the batch, use filter:\n%s\n",
1340                        processInfo->filterString.c_str());
1341                 break;
1342             }
1343         }
1344     }
1345 
1346     // Process results and print unexpected errors.
1347     for (const auto &resultIter : batchResults.results)
1348     {
1349         const TestIdentifier &id = resultIter.first;
1350         const TestResult &result = resultIter.second;
1351 
1352         // Skip results aren't procesed since they're added back to the test queue below.
1353         if (result.type == TestResultType::NoResult)
1354         {
1355             continue;
1356         }
1357 
1358         mCurrentResultCount++;
1359 
1360         printf("[%d/%d] %s.%s", mCurrentResultCount, mTotalResultCount, id.testSuiteName.c_str(),
1361                id.testName.c_str());
1362 
1363         if (mPrintTestStdout)
1364         {
1365             const std::string &batchStdout = processInfo->process->getStdout();
1366             PrintTestOutputSnippet(id, result, batchStdout);
1367         }
1368         else if (result.type == TestResultType::Pass)
1369         {
1370             printf(" (%0.1lf ms)\n", result.elapsedTimeSeconds * 1000.0);
1371         }
1372         else if (result.type == TestResultType::Timeout)
1373         {
1374             printf(" (TIMEOUT in %0.1lf s)\n", result.elapsedTimeSeconds);
1375         }
1376         else
1377         {
1378             printf(" (%s)\n", ResultTypeToString(result.type));
1379 
1380             const std::string &batchStdout = processInfo->process->getStdout();
1381             PrintTestOutputSnippet(id, result, batchStdout);
1382         }
1383     }
1384 
1385     // On unexpected exit, re-queue any unfinished tests.
1386     std::vector<TestIdentifier> unfinishedTests;
1387     for (const auto &resultIter : batchResults.results)
1388     {
1389         const TestIdentifier &id = resultIter.first;
1390         const TestResult &result = resultIter.second;
1391 
1392         if (result.type == TestResultType::NoResult)
1393         {
1394             unfinishedTests.push_back(id);
1395         }
1396     }
1397 
1398     if (!unfinishedTests.empty())
1399     {
1400         mTestQueue.emplace(std::move(unfinishedTests));
1401     }
1402 
1403     // Clean up any dirty temporary files.
1404     for (const std::string &tempFile : {processInfo->filterFileName, processInfo->resultsFileName})
1405     {
1406         // Note: we should be aware that this cleanup won't happen if the harness itself
1407         // crashes. If this situation comes up in the future we should add crash cleanup to the
1408         // harness.
1409         if (!angle::DeleteFile(tempFile.c_str()))
1410         {
1411             std::cerr << "Warning: Error cleaning up temp file: " << tempFile << "\n";
1412         }
1413     }
1414 
1415     processInfo->process.reset();
1416     return true;
1417 }
1418 
run()1419 int TestSuite::run()
1420 {
1421     if (mListTests)
1422     {
1423         ListTests(mTestResults.results);
1424         return EXIT_SUCCESS;
1425     }
1426 
1427     if (mGTestListTests)
1428     {
1429         GTestListTests(mTestResults.results);
1430         return EXIT_SUCCESS;
1431     }
1432 
1433     // Run tests serially.
1434     if (!mBotMode)
1435     {
1436         // Only start the watchdog if the debugger is not attached and we're a child process.
1437         if (!angle::IsDebuggerAttached() && mBatchId != -1)
1438         {
1439             startWatchdog();
1440         }
1441         int retVal = RUN_ALL_TESTS();
1442 
1443         {
1444             std::lock_guard<std::mutex> guard(mTestResults.currentTestMutex);
1445             mTestResults.allDone = true;
1446         }
1447 
1448         if (mWatchdogThread.joinable())
1449         {
1450             mWatchdogThread.join();
1451         }
1452         return retVal;
1453     }
1454 
1455     Timer totalRunTime;
1456     totalRunTime.start();
1457 
1458     Timer messageTimer;
1459     messageTimer.start();
1460 
1461     uint32_t batchId = 0;
1462 
1463     while (!mTestQueue.empty() || !mCurrentProcesses.empty())
1464     {
1465         bool progress = false;
1466 
1467         // Spawn a process if needed and possible.
1468         if (static_cast<int>(mCurrentProcesses.size()) < mMaxProcesses && !mTestQueue.empty())
1469         {
1470             std::vector<TestIdentifier> testsInBatch = mTestQueue.front();
1471             mTestQueue.pop();
1472 
1473             if (!launchChildTestProcess(++batchId, testsInBatch))
1474             {
1475                 return 1;
1476             }
1477 
1478             progress = true;
1479         }
1480 
1481         // Check for process completion.
1482         uint32_t totalTestCount = 0;
1483         for (auto processIter = mCurrentProcesses.begin(); processIter != mCurrentProcesses.end();)
1484         {
1485             ProcessInfo &processInfo = *processIter;
1486             if (processInfo.process->finished())
1487             {
1488                 if (!finishProcess(&processInfo))
1489                 {
1490                     return 1;
1491                 }
1492                 processIter = mCurrentProcesses.erase(processIter);
1493                 progress    = true;
1494             }
1495             else if (processInfo.process->getElapsedTimeSeconds() > mBatchTimeout)
1496             {
1497                 // Terminate the process and record timeouts for the batch.
1498                 // Because we can't determine which sub-test caused a timeout, record the whole
1499                 // batch as a timeout failure. Can be improved by using socket message passing.
1500                 if (!processInfo.process->kill())
1501                 {
1502                     return 1;
1503                 }
1504                 for (const TestIdentifier &testIdentifier : processInfo.testsInBatch)
1505                 {
1506                     // Because the whole batch failed we can't know how long each test took.
1507                     mTestResults.results[testIdentifier].type = TestResultType::Timeout;
1508                 }
1509 
1510                 processIter = mCurrentProcesses.erase(processIter);
1511                 progress    = true;
1512             }
1513             else
1514             {
1515                 totalTestCount += static_cast<uint32_t>(processInfo.testsInBatch.size());
1516                 processIter++;
1517             }
1518         }
1519 
1520         if (progress)
1521         {
1522             messageTimer.start();
1523         }
1524         else if (messageTimer.getElapsedTime() > kIdleMessageTimeout)
1525         {
1526             const ProcessInfo &processInfo = mCurrentProcesses[0];
1527             double processTime             = processInfo.process->getElapsedTimeSeconds();
1528             printf("Running %d tests in %d processes, longest for %d seconds.\n", totalTestCount,
1529                    static_cast<int>(mCurrentProcesses.size()), static_cast<int>(processTime));
1530             messageTimer.start();
1531         }
1532 
1533         // Sleep briefly and continue.
1534         angle::Sleep(100);
1535     }
1536 
1537     // Dump combined results.
1538     WriteOutputFiles(false, mTestResults, mResultsFile, mHistogramWriter, mHistogramJsonFile,
1539                      mTestSuiteName.c_str());
1540 
1541     totalRunTime.stop();
1542     printf("Tests completed in %lf seconds\n", totalRunTime.getElapsedTime());
1543 
1544     return printFailuresAndReturnCount() == 0 ? 0 : 1;
1545 }
1546 
printFailuresAndReturnCount() const1547 int TestSuite::printFailuresAndReturnCount() const
1548 {
1549     std::vector<std::string> failures;
1550 
1551     for (const auto &resultIter : mTestResults.results)
1552     {
1553         const TestIdentifier &id = resultIter.first;
1554         const TestResult &result = resultIter.second;
1555 
1556         if (result.type != TestResultType::Pass)
1557         {
1558             const FileLine &fileLine = mTestFileLines.find(id)->second;
1559 
1560             std::stringstream failureMessage;
1561             failureMessage << id << " (" << fileLine.file << ":" << fileLine.line << ") ("
1562                            << ResultTypeToString(result.type) << ")";
1563             failures.emplace_back(failureMessage.str());
1564         }
1565     }
1566 
1567     if (failures.empty())
1568         return 0;
1569 
1570     printf("%zu test%s failed:\n", failures.size(), failures.size() > 1 ? "s" : "");
1571     for (const std::string &failure : failures)
1572     {
1573         printf("    %s\n", failure.c_str());
1574     }
1575 
1576     return static_cast<int>(failures.size());
1577 }
1578 
startWatchdog()1579 void TestSuite::startWatchdog()
1580 {
1581     auto watchdogMain = [this]() {
1582         do
1583         {
1584             {
1585                 std::lock_guard<std::mutex> guard(mTestResults.currentTestMutex);
1586                 if (mTestResults.currentTestTimer.getElapsedTime() >
1587                     static_cast<double>(mTestTimeout))
1588                 {
1589                     break;
1590                 }
1591 
1592                 if (mTestResults.allDone)
1593                     return;
1594             }
1595 
1596             angle::Sleep(500);
1597         } while (true);
1598         onCrashOrTimeout(TestResultType::Timeout);
1599         ::_Exit(EXIT_FAILURE);
1600     };
1601     mWatchdogThread = std::thread(watchdogMain);
1602 }
1603 
addHistogramSample(const std::string & measurement,const std::string & story,double value,const std::string & units)1604 void TestSuite::addHistogramSample(const std::string &measurement,
1605                                    const std::string &story,
1606                                    double value,
1607                                    const std::string &units)
1608 {
1609     mHistogramWriter.addSample(measurement, story, value, units);
1610 }
1611 
GetTestResultsFromFile(const char * fileName,TestResults * resultsOut)1612 bool GetTestResultsFromFile(const char *fileName, TestResults *resultsOut)
1613 {
1614     std::ifstream ifs(fileName);
1615     if (!ifs.is_open())
1616     {
1617         std::cerr << "Error opening " << fileName << "\n";
1618         return false;
1619     }
1620 
1621     js::IStreamWrapper ifsWrapper(ifs);
1622     js::Document document;
1623     document.ParseStream(ifsWrapper);
1624 
1625     if (document.HasParseError())
1626     {
1627         std::cerr << "Parse error reading JSON document: " << document.GetParseError() << "\n";
1628         return false;
1629     }
1630 
1631     if (!GetTestResultsFromJSON(document, resultsOut))
1632     {
1633         std::cerr << "Error getting test results from JSON.\n";
1634         return false;
1635     }
1636 
1637     return true;
1638 }
1639 
TestResultTypeToString(TestResultType type)1640 const char *TestResultTypeToString(TestResultType type)
1641 {
1642     switch (type)
1643     {
1644         case TestResultType::Crash:
1645             return "Crash";
1646         case TestResultType::Fail:
1647             return "Fail";
1648         case TestResultType::NoResult:
1649             return "NoResult";
1650         case TestResultType::Pass:
1651             return "Pass";
1652         case TestResultType::Timeout:
1653             return "Timeout";
1654         case TestResultType::Unknown:
1655             return "Unknown";
1656     }
1657 }
1658 }  // namespace angle
1659