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