• 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 <time.h>
18 #include <fstream>
19 
20 #include <gtest/gtest.h>
21 #include <rapidjson/document.h>
22 #include <rapidjson/filewritestream.h>
23 #include <rapidjson/istreamwrapper.h>
24 #include <rapidjson/prettywriter.h>
25 
26 // We directly call into a function to register the parameterized tests. This saves spinning up
27 // a subprocess with a new gtest filter.
28 #include <gtest/../../src/gtest-internal-inl.h>
29 
30 namespace js = rapidjson;
31 
32 namespace angle
33 {
34 namespace
35 {
36 constexpr char kTestTimeoutArg[] = "--test-timeout=";
37 constexpr char kFilterFileArg[]  = "--filter-file=";
38 constexpr char kResultFileArg[]  = "--results-file=";
39 #if defined(NDEBUG)
40 constexpr int kDefaultTestTimeout = 20;
41 #else
42 constexpr int kDefaultTestTimeout  = 60;
43 #endif
44 #if defined(NDEBUG)
45 constexpr int kDefaultBatchTimeout = 240;
46 #else
47 constexpr int kDefaultBatchTimeout = 600;
48 #endif
49 constexpr int kDefaultBatchSize = 1000;
50 
ParseFlagValue(const char * flag,const char * argument)51 const char *ParseFlagValue(const char *flag, const char *argument)
52 {
53     if (strstr(argument, flag) == argument)
54     {
55         return argument + strlen(flag);
56     }
57 
58     return nullptr;
59 }
60 
ParseIntArg(const char * flag,const char * argument,int * valueOut)61 bool ParseIntArg(const char *flag, const char *argument, int *valueOut)
62 {
63     const char *value = ParseFlagValue(flag, argument);
64     if (!value)
65     {
66         return false;
67     }
68 
69     char *end            = nullptr;
70     const long longValue = strtol(value, &end, 10);
71 
72     if (*end != '\0')
73     {
74         printf("Error parsing integer flag value.\n");
75         exit(1);
76     }
77 
78     if (longValue == LONG_MAX || longValue == LONG_MIN || static_cast<int>(longValue) != longValue)
79     {
80         printf("Overflow when parsing integer flag value.\n");
81         exit(1);
82     }
83 
84     *valueOut = static_cast<int>(longValue);
85     return true;
86 }
87 
ParseFlag(const char * expected,const char * actual,bool * flagOut)88 bool ParseFlag(const char *expected, const char *actual, bool *flagOut)
89 {
90     if (strcmp(expected, actual) == 0)
91     {
92         *flagOut = true;
93         return true;
94     }
95     return false;
96 }
97 
ParseStringArg(const char * flag,const char * argument,std::string * valueOut)98 bool ParseStringArg(const char *flag, const char *argument, std::string *valueOut)
99 {
100     const char *value = ParseFlagValue(flag, argument);
101     if (!value)
102     {
103         return false;
104     }
105 
106     *valueOut = value;
107     return true;
108 }
109 
DeleteArg(int * argc,char ** argv,int argIndex)110 void DeleteArg(int *argc, char **argv, int argIndex)
111 {
112     // Shift the remainder of the argv list left by one.  Note that argv has (*argc + 1) elements,
113     // the last one always being NULL.  The following loop moves the trailing NULL element as well.
114     for (int index = argIndex; index < *argc; ++index)
115     {
116         argv[index] = argv[index + 1];
117     }
118     (*argc)--;
119 }
120 
AddArg(int * argc,char ** argv,const char * arg)121 void AddArg(int *argc, char **argv, const char *arg)
122 {
123     // This unsafe const_cast is necessary to work around gtest limitations.
124     argv[*argc]     = const_cast<char *>(arg);
125     argv[*argc + 1] = nullptr;
126     (*argc)++;
127 }
128 
ResultTypeToString(TestResultType type)129 const char *ResultTypeToString(TestResultType type)
130 {
131     switch (type)
132     {
133         case TestResultType::Crash:
134             return "CRASH";
135         case TestResultType::Fail:
136             return "FAIL";
137         case TestResultType::Pass:
138             return "PASS";
139         case TestResultType::Skip:
140             return "SKIP";
141         case TestResultType::Timeout:
142             return "TIMEOUT";
143         case TestResultType::Unknown:
144             return "UNKNOWN";
145     }
146 }
147 
GetResultTypeFromString(const std::string & str)148 TestResultType GetResultTypeFromString(const std::string &str)
149 {
150     if (str == "CRASH")
151         return TestResultType::Crash;
152     if (str == "FAIL")
153         return TestResultType::Fail;
154     if (str == "PASS")
155         return TestResultType::Pass;
156     if (str == "SKIP")
157         return TestResultType::Skip;
158     if (str == "TIMEOUT")
159         return TestResultType::Timeout;
160     return TestResultType::Unknown;
161 }
162 
ResultTypeToJSString(TestResultType type,js::Document::AllocatorType * allocator)163 js::Value ResultTypeToJSString(TestResultType type, js::Document::AllocatorType *allocator)
164 {
165     js::Value jsName;
166     jsName.SetString(ResultTypeToString(type), *allocator);
167     return jsName;
168 }
169 
170 // Writes out a TestResults to the Chromium JSON Test Results format.
171 // https://chromium.googlesource.com/chromium/src.git/+/master/docs/testing/json_test_results_format.md
WriteTestResults(bool interrupted,const TestResults & testResults,const std::string & outputFile,const char * testSuiteName)172 void WriteTestResults(bool interrupted,
173                       const TestResults &testResults,
174                       const std::string &outputFile,
175                       const char *testSuiteName)
176 {
177     time_t ltime;
178     time(&ltime);
179     struct tm *timeinfo = gmtime(&ltime);
180     ltime               = mktime(timeinfo);
181 
182     uint64_t secondsSinceEpoch = static_cast<uint64_t>(ltime);
183 
184     js::Document doc;
185     doc.SetObject();
186 
187     js::Document::AllocatorType &allocator = doc.GetAllocator();
188 
189     doc.AddMember("interrupted", interrupted, allocator);
190     doc.AddMember("path_delimiter", ".", allocator);
191     doc.AddMember("version", 3, allocator);
192     doc.AddMember("seconds_since_epoch", secondsSinceEpoch, allocator);
193 
194     js::Value testSuite;
195     testSuite.SetObject();
196 
197     std::map<TestResultType, uint32_t> counts;
198 
199     for (const auto &resultIter : testResults.results)
200     {
201         const TestIdentifier &id = resultIter.first;
202         const TestResult &result = resultIter.second;
203 
204         js::Value jsResult;
205         jsResult.SetObject();
206 
207         counts[result.type]++;
208 
209         jsResult.AddMember("expected", "PASS", allocator);
210         jsResult.AddMember("actual", ResultTypeToJSString(result.type, &allocator), allocator);
211 
212         js::Value times;
213         times.SetArray();
214         times.PushBack(result.elapsedTimeSeconds, allocator);
215 
216         jsResult.AddMember("times", times, allocator);
217 
218         char testName[500];
219         id.sprintfName(testName);
220         js::Value jsName;
221         jsName.SetString(testName, allocator);
222 
223         testSuite.AddMember(jsName, jsResult, allocator);
224     }
225 
226     js::Value numFailuresByType;
227     numFailuresByType.SetObject();
228 
229     for (const auto &countIter : counts)
230     {
231         TestResultType type = countIter.first;
232         uint32_t count      = countIter.second;
233 
234         js::Value jsCount(count);
235         numFailuresByType.AddMember(ResultTypeToJSString(type, &allocator), jsCount, allocator);
236     }
237 
238     doc.AddMember("num_failures_by_type", numFailuresByType, allocator);
239 
240     js::Value tests;
241     tests.SetObject();
242     tests.AddMember(js::StringRef(testSuiteName), testSuite, allocator);
243 
244     doc.AddMember("tests", tests, allocator);
245 
246     printf("Writing test results to %s\n", outputFile.c_str());
247 
248     FILE *fp = fopen(outputFile.c_str(), "w");
249 
250     constexpr size_t kBufferSize = 0xFFFF;
251     std::vector<char> writeBuffer(kBufferSize);
252     js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
253     js::PrettyWriter<js::FileWriteStream> writer(os);
254     doc.Accept(writer);
255 
256     fclose(fp);
257 }
258 
UpdateCurrentTestResult(const testing::TestResult & resultIn,TestResults * resultsOut)259 void UpdateCurrentTestResult(const testing::TestResult &resultIn, TestResults *resultsOut)
260 {
261     TestResult &resultOut = resultsOut->results[resultsOut->currentTest];
262 
263     // Note: Crashes and Timeouts are detected by the crash handler and a watchdog thread.
264     if (resultIn.Skipped())
265     {
266         resultOut.type = TestResultType::Skip;
267     }
268     else if (resultIn.Failed())
269     {
270         resultOut.type = TestResultType::Fail;
271     }
272     else
273     {
274         resultOut.type = TestResultType::Pass;
275     }
276 
277     resultOut.elapsedTimeSeconds = resultsOut->currentTestTimer.getElapsedTime();
278 }
279 
GetTestIdentifier(const testing::TestInfo & testInfo)280 TestIdentifier GetTestIdentifier(const testing::TestInfo &testInfo)
281 {
282     return {testInfo.test_suite_name(), testInfo.name()};
283 }
284 
285 class TestEventListener : public testing::EmptyTestEventListener
286 {
287   public:
288     // Note: TestResults is owned by the TestSuite. It should outlive TestEventListener.
TestEventListener(const std::string & outputFile,const char * testSuiteName,TestResults * testResults)289     TestEventListener(const std::string &outputFile,
290                       const char *testSuiteName,
291                       TestResults *testResults)
292         : mResultsFile(outputFile), mTestSuiteName(testSuiteName), mTestResults(testResults)
293     {}
294 
OnTestStart(const testing::TestInfo & testInfo)295     void OnTestStart(const testing::TestInfo &testInfo) override
296     {
297         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
298         mTestResults->currentTest = GetTestIdentifier(testInfo);
299         mTestResults->currentTestTimer.start();
300     }
301 
OnTestEnd(const testing::TestInfo & testInfo)302     void OnTestEnd(const testing::TestInfo &testInfo) override
303     {
304         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
305         mTestResults->currentTestTimer.stop();
306         const testing::TestResult &resultIn = *testInfo.result();
307         UpdateCurrentTestResult(resultIn, mTestResults);
308         mTestResults->currentTest = TestIdentifier();
309     }
310 
OnTestProgramEnd(const testing::UnitTest & testProgramInfo)311     void OnTestProgramEnd(const testing::UnitTest &testProgramInfo) override
312     {
313         std::lock_guard<std::mutex> guard(mTestResults->currentTestMutex);
314         mTestResults->allDone = true;
315         WriteTestResults(false, *mTestResults, mResultsFile, mTestSuiteName);
316     }
317 
318   private:
319     std::string mResultsFile;
320     const char *mTestSuiteName;
321     TestResults *mTestResults;
322 };
323 
IsTestDisabled(const testing::TestInfo & testInfo)324 bool IsTestDisabled(const testing::TestInfo &testInfo)
325 {
326     return ::strstr(testInfo.name(), "DISABLED_") == testInfo.name();
327 }
328 
329 using TestIdentifierFilter = std::function<bool(const TestIdentifier &id)>;
330 
FilterTests(std::map<TestIdentifier,FileLine> * fileLinesOut,TestIdentifierFilter filter,bool alsoRunDisabledTests)331 std::vector<TestIdentifier> FilterTests(std::map<TestIdentifier, FileLine> *fileLinesOut,
332                                         TestIdentifierFilter filter,
333                                         bool alsoRunDisabledTests)
334 {
335     std::vector<TestIdentifier> tests;
336 
337     const testing::UnitTest &testProgramInfo = *testing::UnitTest::GetInstance();
338     for (int suiteIndex = 0; suiteIndex < testProgramInfo.total_test_suite_count(); ++suiteIndex)
339     {
340         const testing::TestSuite &testSuite = *testProgramInfo.GetTestSuite(suiteIndex);
341         for (int testIndex = 0; testIndex < testSuite.total_test_count(); ++testIndex)
342         {
343             const testing::TestInfo &testInfo = *testSuite.GetTestInfo(testIndex);
344             TestIdentifier id                 = GetTestIdentifier(testInfo);
345             if (filter(id) && (!IsTestDisabled(testInfo) || alsoRunDisabledTests))
346             {
347                 tests.emplace_back(id);
348 
349                 if (fileLinesOut)
350                 {
351                     (*fileLinesOut)[id] = {testInfo.file(), testInfo.line()};
352                 }
353             }
354         }
355     }
356 
357     return tests;
358 }
359 
GetFilteredTests(std::map<TestIdentifier,FileLine> * fileLinesOut,bool alsoRunDisabledTests)360 std::vector<TestIdentifier> GetFilteredTests(std::map<TestIdentifier, FileLine> *fileLinesOut,
361                                              bool alsoRunDisabledTests)
362 {
363     TestIdentifierFilter gtestIDFilter = [](const TestIdentifier &id) {
364         return testing::internal::UnitTestOptions::FilterMatchesTest(id.testSuiteName, id.testName);
365     };
366 
367     return FilterTests(fileLinesOut, gtestIDFilter, alsoRunDisabledTests);
368 }
369 
GetShardTests(int shardIndex,int shardCount,std::map<TestIdentifier,FileLine> * fileLinesOut,bool alsoRunDisabledTests)370 std::vector<TestIdentifier> GetShardTests(int shardIndex,
371                                           int shardCount,
372                                           std::map<TestIdentifier, FileLine> *fileLinesOut,
373                                           bool alsoRunDisabledTests)
374 {
375     std::vector<TestIdentifier> allTests = GetFilteredTests(fileLinesOut, alsoRunDisabledTests);
376     std::vector<TestIdentifier> shardTests;
377 
378     for (int testIndex = shardIndex; testIndex < static_cast<int>(allTests.size());
379          testIndex += shardCount)
380     {
381         shardTests.emplace_back(allTests[testIndex]);
382     }
383 
384     return shardTests;
385 }
386 
GetTestFilter(const std::vector<TestIdentifier> & tests)387 std::string GetTestFilter(const std::vector<TestIdentifier> &tests)
388 {
389     std::stringstream filterStream;
390 
391     filterStream << "--gtest_filter=";
392 
393     for (size_t testIndex = 0; testIndex < tests.size(); ++testIndex)
394     {
395         if (testIndex != 0)
396         {
397             filterStream << ":";
398         }
399 
400         filterStream << tests[testIndex];
401     }
402 
403     return filterStream.str();
404 }
405 
ParseTestSuiteName(const char * executable)406 std::string ParseTestSuiteName(const char *executable)
407 {
408     const char *baseNameStart = strrchr(executable, GetPathSeparator());
409     if (!baseNameStart)
410     {
411         baseNameStart = executable;
412     }
413     else
414     {
415         baseNameStart++;
416     }
417 
418     const char *suffix = GetExecutableExtension();
419     size_t suffixLen   = strlen(suffix);
420     if (suffixLen == 0)
421     {
422         return baseNameStart;
423     }
424 
425     if (!EndsWith(baseNameStart, suffix))
426     {
427         return baseNameStart;
428     }
429 
430     return std::string(baseNameStart, baseNameStart + strlen(baseNameStart) - suffixLen);
431 }
432 
GetTestResultsFromJSON(const js::Document & document,TestResults * resultsOut)433 bool GetTestResultsFromJSON(const js::Document &document, TestResults *resultsOut)
434 {
435     if (!document.HasMember("tests") || !document["tests"].IsObject())
436     {
437         return false;
438     }
439 
440     const js::Value::ConstObject &tests = document["tests"].GetObject();
441     if (tests.MemberCount() != 1)
442     {
443         return false;
444     }
445 
446     const js::Value::Member &suite = *tests.MemberBegin();
447     if (!suite.value.IsObject())
448     {
449         return false;
450     }
451 
452     const js::Value::ConstObject &actual = suite.value.GetObject();
453 
454     for (auto iter = actual.MemberBegin(); iter != actual.MemberEnd(); ++iter)
455     {
456         // Get test identifier.
457         const js::Value &name = iter->name;
458         if (!name.IsString())
459         {
460             return false;
461         }
462 
463         TestIdentifier id;
464         if (!TestIdentifier::ParseFromString(name.GetString(), &id))
465         {
466             return false;
467         }
468 
469         // Get test result.
470         const js::Value &value = iter->value;
471         if (!value.IsObject())
472         {
473             return false;
474         }
475 
476         const js::Value::ConstObject &obj = value.GetObject();
477         if (!obj.HasMember("expected") || !obj.HasMember("actual"))
478         {
479             return false;
480         }
481 
482         const js::Value &expected = obj["expected"];
483         const js::Value &actual   = obj["actual"];
484 
485         if (!expected.IsString() || !actual.IsString())
486         {
487             return false;
488         }
489 
490         const std::string expectedStr = expected.GetString();
491         const std::string actualStr   = actual.GetString();
492 
493         if (expectedStr != "PASS")
494         {
495             return false;
496         }
497 
498         TestResultType resultType = GetResultTypeFromString(actualStr);
499         if (resultType == TestResultType::Unknown)
500         {
501             return false;
502         }
503 
504         double elapsedTimeSeconds = 0.0;
505         if (obj.HasMember("times"))
506         {
507             const js::Value &times = obj["times"];
508             if (!times.IsArray())
509             {
510                 return false;
511             }
512 
513             const js::Value::ConstArray &timesArray = times.GetArray();
514             if (timesArray.Size() != 1 || !timesArray[0].IsDouble())
515             {
516                 return false;
517             }
518 
519             elapsedTimeSeconds = timesArray[0].GetDouble();
520         }
521 
522         TestResult &result        = resultsOut->results[id];
523         result.elapsedTimeSeconds = elapsedTimeSeconds;
524         result.type               = resultType;
525     }
526 
527     return true;
528 }
529 
MergeTestResults(const TestResults & input,TestResults * output)530 bool MergeTestResults(const TestResults &input, TestResults *output)
531 {
532     for (const auto &resultsIter : input.results)
533     {
534         const TestIdentifier &id      = resultsIter.first;
535         const TestResult &inputResult = resultsIter.second;
536         TestResult &outputResult      = output->results[id];
537 
538         // This should probably handle situations where a test is run more than once.
539         if (inputResult.type != TestResultType::Skip)
540         {
541             if (outputResult.type != TestResultType::Skip)
542             {
543                 printf("Warning: duplicate entry for %s.%s.\n", id.testSuiteName.c_str(),
544                        id.testName.c_str());
545                 return false;
546             }
547 
548             outputResult.elapsedTimeSeconds = inputResult.elapsedTimeSeconds;
549             outputResult.type               = inputResult.type;
550         }
551     }
552 
553     return true;
554 }
555 
PrintTestOutputSnippet(const TestIdentifier & id,const TestResult & result,const std::string & fullOutput)556 void PrintTestOutputSnippet(const TestIdentifier &id,
557                             const TestResult &result,
558                             const std::string &fullOutput)
559 {
560     std::stringstream nameStream;
561     nameStream << id;
562     std::string fullName = nameStream.str();
563 
564     size_t runPos = fullOutput.find(std::string("[ RUN      ] ") + fullName);
565     if (runPos == std::string::npos)
566     {
567         printf("Cannot locate test output snippet.\n");
568         return;
569     }
570 
571     size_t endPos = fullOutput.find(std::string("[  FAILED  ] ") + fullName, runPos);
572     // Only clip the snippet to the "OK" message if the test really
573     // succeeded. It still might have e.g. crashed after printing it.
574     if (endPos == std::string::npos && result.type == TestResultType::Pass)
575     {
576         endPos = fullOutput.find(std::string("[       OK ] ") + fullName, runPos);
577     }
578     if (endPos != std::string::npos)
579     {
580         size_t newline_pos = fullOutput.find("\n", endPos);
581         if (newline_pos != std::string::npos)
582             endPos = newline_pos + 1;
583     }
584 
585     std::cout << "\n";
586     if (endPos != std::string::npos)
587     {
588         std::cout << fullOutput.substr(runPos, endPos - runPos);
589     }
590     else
591     {
592         std::cout << fullOutput.substr(runPos);
593     }
594     std::cout << "\n";
595 }
596 }  // namespace
597 
598 TestIdentifier::TestIdentifier() = default;
599 
TestIdentifier(const std::string & suiteNameIn,const std::string & nameIn)600 TestIdentifier::TestIdentifier(const std::string &suiteNameIn, const std::string &nameIn)
601     : testSuiteName(suiteNameIn), testName(nameIn)
602 {}
603 
604 TestIdentifier::TestIdentifier(const TestIdentifier &other) = default;
605 
606 TestIdentifier::~TestIdentifier() = default;
607 
608 TestIdentifier &TestIdentifier::operator=(const TestIdentifier &other) = default;
609 
sprintfName(char * outBuffer) const610 void TestIdentifier::sprintfName(char *outBuffer) const
611 {
612     sprintf(outBuffer, "%s.%s", testSuiteName.c_str(), testName.c_str());
613 }
614 
615 // static
ParseFromString(const std::string & str,TestIdentifier * idOut)616 bool TestIdentifier::ParseFromString(const std::string &str, TestIdentifier *idOut)
617 {
618     size_t separator = str.find(".");
619     if (separator == std::string::npos)
620     {
621         return false;
622     }
623 
624     idOut->testSuiteName = str.substr(0, separator);
625     idOut->testName      = str.substr(separator + 1, str.length() - separator - 1);
626     return true;
627 }
628 
629 TestResults::TestResults() = default;
630 
631 TestResults::~TestResults() = default;
632 
633 ProcessInfo::ProcessInfo() = default;
634 
operator =(ProcessInfo && rhs)635 ProcessInfo &ProcessInfo::operator=(ProcessInfo &&rhs)
636 {
637     process         = std::move(rhs.process);
638     testsInBatch    = std::move(rhs.testsInBatch);
639     resultsFileName = std::move(rhs.resultsFileName);
640     filterFileName  = std::move(rhs.filterFileName);
641     commandLine     = std::move(rhs.commandLine);
642     return *this;
643 }
644 
645 ProcessInfo::~ProcessInfo() = default;
646 
ProcessInfo(ProcessInfo && other)647 ProcessInfo::ProcessInfo(ProcessInfo &&other)
648 {
649     *this = std::move(other);
650 }
651 
TestSuite(int * argc,char ** argv)652 TestSuite::TestSuite(int *argc, char **argv)
653     : mShardCount(-1),
654       mShardIndex(-1),
655       mBotMode(false),
656       mBatchSize(kDefaultBatchSize),
657       mCurrentResultCount(0),
658       mTotalResultCount(0),
659       mMaxProcesses(NumberOfProcessors()),
660       mTestTimeout(kDefaultTestTimeout),
661       mBatchTimeout(kDefaultBatchTimeout)
662 {
663     bool hasFilter            = false;
664     bool alsoRunDisabledTests = false;
665 
666 #if defined(ANGLE_PLATFORM_WINDOWS)
667     testing::GTEST_FLAG(catch_exceptions) = false;
668 #endif
669 
670     // Note that the crash callback must be owned and not use global constructors.
671     mCrashCallback = [this]() { onCrashOrTimeout(TestResultType::Crash); };
672     InitCrashHandler(&mCrashCallback);
673 
674     if (*argc <= 0)
675     {
676         printf("Missing test arguments.\n");
677         exit(1);
678     }
679 
680     mTestExecutableName = argv[0];
681     mTestSuiteName      = ParseTestSuiteName(mTestExecutableName.c_str());
682 
683     for (int argIndex = 1; argIndex < *argc;)
684     {
685         if (parseSingleArg(argv[argIndex]))
686         {
687             DeleteArg(argc, argv, argIndex);
688             continue;
689         }
690 
691         if (ParseFlagValue("--gtest_filter=", argv[argIndex]))
692         {
693             hasFilter = true;
694         }
695         else
696         {
697             // Don't include disabled tests in test lists unless the user asks for them.
698             if (strcmp("--gtest_also_run_disabled_tests", argv[argIndex]) == 0)
699             {
700                 alsoRunDisabledTests = true;
701             }
702 
703             mGoogleTestCommandLineArgs.push_back(argv[argIndex]);
704         }
705         ++argIndex;
706     }
707 
708     if ((mShardIndex >= 0) != (mShardCount > 1))
709     {
710         printf("Shard index and shard count must be specified together.\n");
711         exit(1);
712     }
713 
714     if (!mFilterFile.empty())
715     {
716         if (hasFilter)
717         {
718             printf("Cannot use gtest_filter in conjunction with a filter file.\n");
719             exit(1);
720         }
721 
722         uint32_t fileSize = 0;
723         if (!GetFileSize(mFilterFile.c_str(), &fileSize))
724         {
725             printf("Error getting filter file size: %s\n", mFilterFile.c_str());
726             exit(1);
727         }
728 
729         std::vector<char> fileContents(fileSize + 1, 0);
730         if (!ReadEntireFileToString(mFilterFile.c_str(), fileContents.data(), fileSize))
731         {
732             printf("Error loading filter file: %s\n", mFilterFile.c_str());
733             exit(1);
734         }
735         mFilterString.assign(fileContents.data());
736 
737         if (mFilterString.substr(0, strlen("--gtest_filter=")) != std::string("--gtest_filter="))
738         {
739             printf("Filter file must start with \"--gtest_filter=\".");
740             exit(1);
741         }
742 
743         // Note that we only add a filter string if we previously deleted a shader filter file
744         // argument. So we will have space for the new filter string in argv.
745         AddArg(argc, argv, mFilterString.c_str());
746     }
747 
748     if (mShardCount > 0)
749     {
750         // Call into gtest internals to force parameterized test name registration.
751         testing::internal::UnitTestImpl *impl = testing::internal::GetUnitTestImpl();
752         impl->RegisterParameterizedTests();
753 
754         // Initialize internal GoogleTest filter arguments so we can call "FilterMatchesTest".
755         testing::internal::ParseGoogleTestFlagsOnly(argc, argv);
756 
757         mTestQueue = GetShardTests(mShardIndex, mShardCount, &mTestFileLines, alsoRunDisabledTests);
758         mFilterString = GetTestFilter(mTestQueue);
759 
760         // Note that we only add a filter string if we previously deleted a shader index/count
761         // argument. So we will have space for the new filter string in argv.
762         AddArg(argc, argv, mFilterString.c_str());
763 
764         // Force-re-initialize GoogleTest flags to load the shard filter.
765         testing::internal::ParseGoogleTestFlagsOnly(argc, argv);
766     }
767 
768     testing::InitGoogleTest(argc, argv);
769 
770     if (mShardCount <= 0)
771     {
772         mTestQueue = GetFilteredTests(&mTestFileLines, alsoRunDisabledTests);
773     }
774 
775     mTotalResultCount = mTestQueue.size();
776 
777     if ((mBotMode || !mResultsDirectory.empty()) && mResultsFile.empty())
778     {
779         // Create a default output file in bot mode.
780         mResultsFile = "output.json";
781     }
782 
783     if (!mResultsDirectory.empty())
784     {
785         std::stringstream resultFileName;
786         resultFileName << mResultsDirectory << GetPathSeparator() << mResultsFile;
787         mResultsFile = resultFileName.str();
788     }
789 
790     if (!mResultsFile.empty())
791     {
792         testing::TestEventListeners &listeners = testing::UnitTest::GetInstance()->listeners();
793         listeners.Append(
794             new TestEventListener(mResultsFile, mTestSuiteName.c_str(), &mTestResults));
795 
796         std::vector<TestIdentifier> testList = GetFilteredTests(nullptr, alsoRunDisabledTests);
797 
798         for (const TestIdentifier &id : testList)
799         {
800             mTestResults.results[id].type = TestResultType::Skip;
801         }
802     }
803 }
804 
~TestSuite()805 TestSuite::~TestSuite()
806 {
807     if (mWatchdogThread.joinable())
808     {
809         mWatchdogThread.detach();
810     }
811     TerminateCrashHandler();
812 }
813 
parseSingleArg(const char * argument)814 bool TestSuite::parseSingleArg(const char *argument)
815 {
816     return (ParseIntArg("--shard-count=", argument, &mShardCount) ||
817             ParseIntArg("--shard-index=", argument, &mShardIndex) ||
818             ParseIntArg("--batch-size=", argument, &mBatchSize) ||
819             ParseIntArg("--max-processes=", argument, &mMaxProcesses) ||
820             ParseIntArg(kTestTimeoutArg, argument, &mTestTimeout) ||
821             ParseIntArg("--batch-timeout=", argument, &mBatchTimeout) ||
822             ParseStringArg("--results-directory=", argument, &mResultsDirectory) ||
823             ParseStringArg(kResultFileArg, argument, &mResultsFile) ||
824             ParseStringArg(kFilterFileArg, argument, &mFilterFile) ||
825             ParseFlag("--bot-mode", argument, &mBotMode));
826 }
827 
onCrashOrTimeout(TestResultType crashOrTimeout)828 void TestSuite::onCrashOrTimeout(TestResultType crashOrTimeout)
829 {
830     if (mTestResults.currentTest.valid())
831     {
832         TestResult &result        = mTestResults.results[mTestResults.currentTest];
833         result.type               = crashOrTimeout;
834         result.elapsedTimeSeconds = mTestResults.currentTestTimer.getElapsedTime();
835     }
836 
837     if (mResultsFile.empty())
838     {
839         printf("No results file specified.\n");
840         return;
841     }
842 
843     WriteTestResults(true, mTestResults, mResultsFile, mTestSuiteName.c_str());
844 }
845 
launchChildTestProcess(const std::vector<TestIdentifier> & testsInBatch)846 bool TestSuite::launchChildTestProcess(const std::vector<TestIdentifier> &testsInBatch)
847 {
848     constexpr uint32_t kMaxPath = 1000;
849 
850     // Create a temporary file to store the test list
851     ProcessInfo processInfo;
852 
853     char filterBuffer[kMaxPath] = {};
854     if (!CreateTemporaryFile(filterBuffer, kMaxPath))
855     {
856         std::cerr << "Error creating temporary file for test list.\n";
857         return false;
858     }
859     processInfo.filterFileName.assign(filterBuffer);
860 
861     std::string filterString = GetTestFilter(testsInBatch);
862 
863     FILE *fp = fopen(processInfo.filterFileName.c_str(), "w");
864     if (!fp)
865     {
866         std::cerr << "Error opening temporary file for test list.\n";
867         return false;
868     }
869     fprintf(fp, "%s", filterString.c_str());
870     fclose(fp);
871 
872     std::string filterFileArg = kFilterFileArg + processInfo.filterFileName;
873 
874     // Create a temporary file to store the test output.
875     char resultsBuffer[kMaxPath] = {};
876     if (!CreateTemporaryFile(resultsBuffer, kMaxPath))
877     {
878         std::cerr << "Error creating temporary file for test list.\n";
879         return false;
880     }
881     processInfo.resultsFileName.assign(resultsBuffer);
882 
883     std::string resultsFileArg = kResultFileArg + processInfo.resultsFileName;
884 
885     // Construct commandline for child process.
886     std::vector<const char *> args;
887 
888     args.push_back(mTestExecutableName.c_str());
889     args.push_back(filterFileArg.c_str());
890     args.push_back(resultsFileArg.c_str());
891 
892     for (const std::string &arg : mGoogleTestCommandLineArgs)
893     {
894         args.push_back(arg.c_str());
895     }
896 
897     std::string timeoutStr;
898     if (mTestTimeout != kDefaultTestTimeout)
899     {
900         std::stringstream timeoutStream;
901         timeoutStream << kTestTimeoutArg << mTestTimeout;
902         timeoutStr = timeoutStream.str();
903         args.push_back(timeoutStr.c_str());
904     }
905 
906     // Launch child process and wait for completion.
907     processInfo.process = LaunchProcess(args, true, true);
908 
909     if (!processInfo.process->started())
910     {
911         std::cerr << "Error launching child process.\n";
912         return false;
913     }
914 
915     std::stringstream commandLineStr;
916     for (const char *arg : args)
917     {
918         commandLineStr << arg << " ";
919     }
920 
921     processInfo.commandLine  = commandLineStr.str();
922     processInfo.testsInBatch = testsInBatch;
923     mCurrentProcesses.emplace_back(std::move(processInfo));
924     return true;
925 }
926 
finishProcess(ProcessInfo * processInfo)927 bool TestSuite::finishProcess(ProcessInfo *processInfo)
928 {
929     // Get test results and merge into master list.
930     TestResults batchResults;
931 
932     if (!GetTestResultsFromFile(processInfo->resultsFileName.c_str(), &batchResults))
933     {
934         std::cerr << "Error reading test results from child process.\n";
935         return false;
936     }
937 
938     if (!MergeTestResults(batchResults, &mTestResults))
939     {
940         std::cerr << "Error merging batch test results.\n";
941         return false;
942     }
943 
944     // Process results and print unexpected errors.
945     for (const auto &resultIter : batchResults.results)
946     {
947         const TestIdentifier &id = resultIter.first;
948         const TestResult &result = resultIter.second;
949 
950         // Skip results aren't procesed since they're added back to the test queue below.
951         if (result.type == TestResultType::Skip)
952         {
953             continue;
954         }
955 
956         mCurrentResultCount++;
957         printf("[%d/%d] %s.%s", mCurrentResultCount, mTotalResultCount, id.testSuiteName.c_str(),
958                id.testName.c_str());
959 
960         if (result.type == TestResultType::Pass)
961         {
962             printf(" (%g ms)\n", result.elapsedTimeSeconds * 1000.0);
963         }
964         else
965         {
966             printf(" (%s)\n", ResultTypeToString(result.type));
967 
968             const std::string &batchStdout = processInfo->process->getStdout();
969             PrintTestOutputSnippet(id, result, batchStdout);
970         }
971     }
972 
973     // On unexpected exit, re-queue any unfinished tests.
974     if (processInfo->process->getExitCode() != 0)
975     {
976         for (const auto &resultIter : batchResults.results)
977         {
978             const TestIdentifier &id = resultIter.first;
979             const TestResult &result = resultIter.second;
980 
981             if (result.type == TestResultType::Skip)
982             {
983                 mTestQueue.emplace_back(id);
984             }
985         }
986     }
987 
988     // Clean up any dirty temporary files.
989     for (const std::string &tempFile : {processInfo->filterFileName, processInfo->resultsFileName})
990     {
991         // Note: we should be aware that this cleanup won't happen if the harness itself crashes.
992         // If this situation comes up in the future we should add crash cleanup to the harness.
993         if (!angle::DeleteFile(tempFile.c_str()))
994         {
995             std::cerr << "Warning: Error cleaning up temp file: " << tempFile << "\n";
996         }
997     }
998 
999     processInfo->process.reset();
1000     return true;
1001 }
1002 
run()1003 int TestSuite::run()
1004 {
1005     // Run tests serially.
1006     if (!mBotMode)
1007     {
1008         startWatchdog();
1009         return RUN_ALL_TESTS();
1010     }
1011 
1012     constexpr double kIdleMessageTimeout = 5.0;
1013 
1014     Timer messageTimer;
1015     messageTimer.start();
1016 
1017     while (!mTestQueue.empty() || !mCurrentProcesses.empty())
1018     {
1019         bool progress = false;
1020 
1021         // Spawn a process if needed and possible.
1022         while (static_cast<int>(mCurrentProcesses.size()) < mMaxProcesses && !mTestQueue.empty())
1023         {
1024             int numTests = std::min<int>(mTestQueue.size(), mBatchSize);
1025 
1026             std::vector<TestIdentifier> testsInBatch;
1027             testsInBatch.assign(mTestQueue.begin(), mTestQueue.begin() + numTests);
1028             mTestQueue.erase(mTestQueue.begin(), mTestQueue.begin() + numTests);
1029 
1030             if (!launchChildTestProcess(testsInBatch))
1031             {
1032                 return 1;
1033             }
1034 
1035             progress = true;
1036         }
1037 
1038         // Check for process completion.
1039         for (auto processIter = mCurrentProcesses.begin(); processIter != mCurrentProcesses.end();)
1040         {
1041             ProcessInfo &processInfo = *processIter;
1042             if (processInfo.process->finished())
1043             {
1044                 if (!finishProcess(&processInfo))
1045                 {
1046                     return 1;
1047                 }
1048                 processIter = mCurrentProcesses.erase(processIter);
1049                 progress    = true;
1050             }
1051             else if (processInfo.process->getElapsedTimeSeconds() > mBatchTimeout)
1052             {
1053                 // Terminate the process and record timeouts for the batch.
1054                 // Because we can't determine which sub-test caused a timeout, record the whole
1055                 // batch as a timeout failure. Can be improved by using socket message passing.
1056                 if (!processInfo.process->kill())
1057                 {
1058                     return 1;
1059                 }
1060                 for (const TestIdentifier &testIdentifier : processInfo.testsInBatch)
1061                 {
1062                     // Because the whole batch failed we can't know how long each test took.
1063                     mTestResults.results[testIdentifier].type = TestResultType::Timeout;
1064                 }
1065 
1066                 processIter = mCurrentProcesses.erase(processIter);
1067                 progress    = true;
1068             }
1069             else
1070             {
1071                 processIter++;
1072             }
1073         }
1074 
1075         if (!progress && messageTimer.getElapsedTime() > kIdleMessageTimeout)
1076         {
1077             for (const ProcessInfo &processInfo : mCurrentProcesses)
1078             {
1079                 double processTime = processInfo.process->getElapsedTimeSeconds();
1080                 if (processTime > kIdleMessageTimeout)
1081                 {
1082                     printf("Running for %d seconds: %s\n", static_cast<int>(processTime),
1083                            processInfo.commandLine.c_str());
1084                 }
1085             }
1086 
1087             messageTimer.start();
1088         }
1089 
1090         // Sleep briefly and continue.
1091         angle::Sleep(10);
1092     }
1093 
1094     // Dump combined results.
1095     WriteTestResults(true, mTestResults, mResultsFile, mTestSuiteName.c_str());
1096 
1097     return printFailuresAndReturnCount() == 0;
1098 }
1099 
printFailuresAndReturnCount() const1100 int TestSuite::printFailuresAndReturnCount() const
1101 {
1102     std::vector<std::string> failures;
1103 
1104     for (const auto &resultIter : mTestResults.results)
1105     {
1106         const TestIdentifier &id = resultIter.first;
1107         const TestResult &result = resultIter.second;
1108 
1109         if (result.type != TestResultType::Pass)
1110         {
1111             const FileLine &fileLine = mTestFileLines.find(id)->second;
1112 
1113             std::stringstream failureMessage;
1114             failureMessage << id << " (" << fileLine.file << ":" << fileLine.line << ") ("
1115                            << ResultTypeToString(result.type) << ")";
1116             failures.emplace_back(failureMessage.str());
1117         }
1118     }
1119 
1120     if (failures.empty())
1121         return 0;
1122 
1123     printf("%zu test%s failed:\n", failures.size(), failures.size() > 1 ? "s" : "");
1124     for (const std::string &failure : failures)
1125     {
1126         printf("    %s\n", failure.c_str());
1127     }
1128 
1129     return static_cast<int>(failures.size());
1130 }
1131 
startWatchdog()1132 void TestSuite::startWatchdog()
1133 {
1134     auto watchdogMain = [this]() {
1135         do
1136         {
1137             {
1138                 std::lock_guard<std::mutex> guard(mTestResults.currentTestMutex);
1139                 if (mTestResults.currentTestTimer.getElapsedTime() >
1140                     static_cast<double>(mTestTimeout))
1141                 {
1142                     onCrashOrTimeout(TestResultType::Timeout);
1143                     exit(2);
1144                 }
1145 
1146                 if (mTestResults.allDone)
1147                     return;
1148             }
1149 
1150             angle::Sleep(1000);
1151         } while (true);
1152     };
1153     mWatchdogThread = std::thread(watchdogMain);
1154 }
1155 
GetTestResultsFromFile(const char * fileName,TestResults * resultsOut)1156 bool GetTestResultsFromFile(const char *fileName, TestResults *resultsOut)
1157 {
1158     std::ifstream ifs(fileName);
1159     if (!ifs.is_open())
1160     {
1161         std::cerr << "Error opening " << fileName << "\n";
1162         return false;
1163     }
1164 
1165     js::IStreamWrapper ifsWrapper(ifs);
1166     js::Document document;
1167     document.ParseStream(ifsWrapper);
1168 
1169     if (document.HasParseError())
1170     {
1171         std::cerr << "Parse error reading JSON document: " << document.GetParseError() << "\n";
1172         return false;
1173     }
1174 
1175     if (!GetTestResultsFromJSON(document, resultsOut))
1176     {
1177         std::cerr << "Error getting test results from JSON.\n";
1178         return false;
1179     }
1180 
1181     return true;
1182 }
1183 
TestResultTypeToString(TestResultType type)1184 const char *TestResultTypeToString(TestResultType type)
1185 {
1186     switch (type)
1187     {
1188         case TestResultType::Crash:
1189             return "Crash";
1190         case TestResultType::Fail:
1191             return "Fail";
1192         case TestResultType::Skip:
1193             return "Skip";
1194         case TestResultType::Pass:
1195             return "Pass";
1196         case TestResultType::Timeout:
1197             return "Timeout";
1198         case TestResultType::Unknown:
1199             return "Unknown";
1200     }
1201 }
1202 }  // namespace angle
1203