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(<ime);
238 struct tm *timeinfo = gmtime(<ime);
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 × = obj["times"];
728 if (!times.IsArray())
729 {
730 return false;
731 }
732
733 const js::Value::ConstArray ×Array = 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