Commit 59fbb989 by Jamie Madill Committed by Commit Bot

Test Runner: Add ability to retry flaky tests.

Bug: angleproject:5273 Change-Id: Ie89559bb0897a04213981aa8fe4e2f2bfe78959a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2513287 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 8e878d95
...@@ -24,6 +24,7 @@ following additional command-line arguments: ...@@ -24,6 +24,7 @@ following additional command-line arguments:
* `--results-file` specifies a location for the JSON test result output * `--results-file` specifies a location for the JSON test result output
* `--shard-count` and `--shard-index` control the test sharding * `--shard-count` and `--shard-index` control the test sharding
* `--test-timeout` limits the amount of time spent in each test * `--test-timeout` limits the amount of time spent in each test
* `--flaky-retries` allows for tests to fail a fixed number of times and still pass
`--isolated-script-test-output` and `--isolated-script-perf-test-output` mirror `--results-file` `--isolated-script-test-output` and `--isolated-script-perf-test-output` mirror `--results-file`
and `--histogram-json-file` respectively. and `--histogram-json-file` respectively.
......
...@@ -54,7 +54,7 @@ enum class TestResultType ...@@ -54,7 +54,7 @@ enum class TestResultType
{ {
Crash, Crash,
Fail, Fail,
Skip, NoResult,
Pass, Pass,
Timeout, Timeout,
Unknown, Unknown,
...@@ -64,8 +64,9 @@ const char *TestResultTypeToString(TestResultType type); ...@@ -64,8 +64,9 @@ const char *TestResultTypeToString(TestResultType type);
struct TestResult struct TestResult
{ {
TestResultType type = TestResultType::Skip; TestResultType type = TestResultType::NoResult;
double elapsedTimeSeconds = 0.0; double elapsedTimeSeconds = 0.0;
uint32_t flakyFailures = 0;
}; };
inline bool operator==(const TestResult &a, const TestResult &b) inline bool operator==(const TestResult &a, const TestResult &b)
...@@ -153,6 +154,7 @@ class TestSuite ...@@ -153,6 +154,7 @@ class TestSuite
int mTestTimeout; int mTestTimeout;
int mBatchTimeout; int mBatchTimeout;
int mBatchId; int mBatchId;
int mFlakyRetries;
std::vector<std::string> mChildProcessArgs; std::vector<std::string> mChildProcessArgs;
std::map<TestIdentifier, FileLine> mTestFileLines; std::map<TestIdentifier, FileLine> mTestFileLines;
std::vector<ProcessInfo> mCurrentProcesses; std::vector<ProcessInfo> mCurrentProcesses;
......
...@@ -24,6 +24,10 @@ namespace js = rapidjson; ...@@ -24,6 +24,10 @@ namespace js = rapidjson;
namespace namespace
{ {
constexpr char kTestHelperExecutable[] = "test_utils_unittest_helper"; constexpr char kTestHelperExecutable[] = "test_utils_unittest_helper";
constexpr int kFlakyRetries = 3;
// Enable this for debugging.
constexpr bool kDebugOutput = false;
class TestSuiteTest : public testing::Test class TestSuiteTest : public testing::Test
{ {
...@@ -36,48 +40,70 @@ class TestSuiteTest : public testing::Test ...@@ -36,48 +40,70 @@ class TestSuiteTest : public testing::Test
} }
} }
std::string mTempFileName; bool runTestSuite(const std::vector<std::string> &extraArgs, TestResults *actualResults)
}; {
std::string executablePath = GetExecutableDirectory();
EXPECT_NE(executablePath, "");
executablePath += std::string("/") + kTestHelperExecutable + GetExecutableExtension();
// Tests the ANGLE standalone testing harness. Runs four tests with different ending conditions. constexpr uint32_t kMaxTempDirLen = 100;
// Verifies that Pass, Fail, Crash and Timeout are all handled correctly. char tempDirName[kMaxTempDirLen * 2];
TEST_F(TestSuiteTest, RunMockTests)
{
std::string executablePath = GetExecutableDirectory();
EXPECT_NE(executablePath, "");
executablePath += std::string("/") + kTestHelperExecutable + GetExecutableExtension();
constexpr uint32_t kMaxTempDirLen = 100; if (!GetTempDir(tempDirName, kMaxTempDirLen))
char tempDirName[kMaxTempDirLen * 2]; {
ASSERT_TRUE(GetTempDir(tempDirName, kMaxTempDirLen)); return false;
}
std::stringstream tempFNameStream; std::stringstream tempFNameStream;
tempFNameStream << tempDirName << GetPathSeparator() << "test_temp_" << rand() << ".json"; tempFNameStream << tempDirName << GetPathSeparator() << "test_temp_" << rand() << ".json";
mTempFileName = tempFNameStream.str(); mTempFileName = tempFNameStream.str();
std::string resultsFileName = "--results-file=" + mTempFileName; std::string resultsFileName = "--results-file=" + mTempFileName;
std::vector<const char *> args = {executablePath.c_str(), std::vector<const char *> args = {
kRunTestSuite, executablePath.c_str(), kRunTestSuite, "--gtest_also_run_disabled_tests",
"--gtest_filter=MockTestSuiteTest.DISABLED_*", "--bot-mode", "--test-timeout=5", resultsFileName.c_str()};
"--gtest_also_run_disabled_tests",
"--bot-mode",
"--test-timeout=5",
resultsFileName.c_str()};
ProcessHandle process(args, true, true); for (const std::string &arg : extraArgs)
EXPECT_TRUE(process->started()); {
EXPECT_TRUE(process->finish()); args.push_back(arg.c_str());
EXPECT_TRUE(process->finished()); }
EXPECT_EQ(process->getStderr(), "");
// Uncomment this for debugging. if (kDebugOutput)
// printf("stdout:\n%s\n", process->getStdout().c_str()); {
printf("Test arguments:\n");
for (const char *arg : args)
{
printf("%s ", arg);
}
printf("\n");
}
ProcessHandle process(args, true, true);
EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish());
EXPECT_TRUE(process->finished());
EXPECT_EQ(process->getStderr(), "");
if (kDebugOutput)
{
printf("stdout:\n%s\n", process->getStdout().c_str());
}
return GetTestResultsFromFile(mTempFileName.c_str(), actualResults);
}
std::string mTempFileName;
};
// Tests the ANGLE standalone testing harness. Runs four tests with different ending conditions.
// Verifies that Pass, Fail, Crash and Timeout are all handled correctly.
TEST_F(TestSuiteTest, RunMockTests)
{
std::vector<std::string> extraArgs = {"--gtest_filter=MockTestSuiteTest.DISABLED_*"};
TestResults actual; TestResults actual;
ASSERT_TRUE(GetTestResultsFromFile(mTempFileName.c_str(), &actual)); ASSERT_TRUE(runTestSuite(extraArgs, &actual));
EXPECT_TRUE(DeleteFile(mTempFileName.c_str()));
mTempFileName.clear();
std::map<TestIdentifier, TestResult> expectedResults = { std::map<TestIdentifier, TestResult> expectedResults = {
{{"MockTestSuiteTest", "DISABLED_Pass"}, {TestResultType::Pass, 0.0}}, {{"MockTestSuiteTest", "DISABLED_Pass"}, {TestResultType::Pass, 0.0}},
...@@ -89,6 +115,22 @@ TEST_F(TestSuiteTest, RunMockTests) ...@@ -89,6 +115,22 @@ TEST_F(TestSuiteTest, RunMockTests)
EXPECT_EQ(expectedResults, actual.results); EXPECT_EQ(expectedResults, actual.results);
} }
// Verifies the flaky retry feature works as expected.
TEST_F(TestSuiteTest, RunFlakyTests)
{
std::vector<std::string> extraArgs = {"--gtest_filter=MockFlakyTestSuiteTest.DISABLED_Flaky",
"--flaky-retries=" + std::to_string(kFlakyRetries)};
TestResults actual;
ASSERT_TRUE(runTestSuite(extraArgs, &actual));
std::map<TestIdentifier, TestResult> expectedResults = {
{{"MockFlakyTestSuiteTest", "DISABLED_Flaky"},
{TestResultType::Pass, 0.0, kFlakyRetries - 1}}};
EXPECT_EQ(expectedResults, actual.results);
}
// Normal passing test. // Normal passing test.
TEST(MockTestSuiteTest, DISABLED_Pass) TEST(MockTestSuiteTest, DISABLED_Pass)
{ {
...@@ -107,6 +149,43 @@ TEST(MockTestSuiteTest, DISABLED_Timeout) ...@@ -107,6 +149,43 @@ TEST(MockTestSuiteTest, DISABLED_Timeout)
angle::Sleep(20000); angle::Sleep(20000);
} }
// Trigger a flaky test failure.
TEST(MockFlakyTestSuiteTest, DISABLED_Flaky)
{
constexpr uint32_t kMaxTempDirLen = 100;
char tempDirName[kMaxTempDirLen * 2];
ASSERT_TRUE(GetTempDir(tempDirName, kMaxTempDirLen));
std::stringstream tempFNameStream;
tempFNameStream << tempDirName << GetPathSeparator() << "flaky_temp.txt";
std::string tempFileName = tempFNameStream.str();
int fails = 0;
{
FILE *fp = fopen(tempFileName.c_str(), "r");
if (fp)
{
ASSERT_EQ(fscanf(fp, "%d", &fails), 1);
fclose(fp);
}
}
if (fails >= kFlakyRetries - 1)
{
angle::DeleteFile(tempFileName.c_str());
}
else
{
EXPECT_TRUE(false);
FILE *fp = fopen(tempFileName.c_str(), "w");
ASSERT_NE(fp, nullptr);
fprintf(fp, "%d", fails + 1);
fclose(fp);
}
}
// Trigger a test crash. // Trigger a test crash.
// TEST(MockTestSuiteTest, DISABLED_Crash) // TEST(MockTestSuiteTest, DISABLED_Crash)
// { // {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment