diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gtest-internal-inl.h | 35 | ||||
-rw-r--r-- | src/gtest-port.cc | 22 | ||||
-rw-r--r-- | src/gtest.cc | 163 |
3 files changed, 199 insertions, 21 deletions
diff --git a/src/gtest-internal-inl.h b/src/gtest-internal-inl.h index cefed209..07d0c5b4 100644 --- a/src/gtest-internal-inl.h +++ b/src/gtest-internal-inl.h @@ -162,6 +162,32 @@ String WideStringToUtf8(const wchar_t* str, int num_chars); // Returns the number of active threads, or 0 when there is an error. size_t GetThreadCount(); +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_str, const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id); + // List is a simple singly-linked list container. // // We cannot use std::list as Microsoft's implementation of STL has @@ -1111,11 +1137,18 @@ class UnitTestImpl { ad_hoc_test_result_.Clear(); } + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + // Matches the full name of each test against the user-specified // filter to decide whether the test should run, then records the // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. // Returns the number of tests that should run. - int FilterTests(); + int FilterTests(ReactionToSharding shard_tests); // Lists all the tests by name. void ListAllTests(); diff --git a/src/gtest-port.cc b/src/gtest-port.cc index 3c9ec4bb..9348f55c 100644 --- a/src/gtest-port.cc +++ b/src/gtest-port.cc @@ -512,17 +512,6 @@ static String FlagToEnvVar(const char* flag) { return env_var.GetString(); } -// Reads and returns the Boolean environment variable corresponding to -// the given flag; if it's not set, returns default_value. -// -// The value is considered true iff it's not "0". -bool BoolFromGTestEnv(const char* flag, bool default_value) { - const String env_var = FlagToEnvVar(flag); - const char* const string_value = GetEnv(env_var.c_str()); - return string_value == NULL ? - default_value : strcmp(string_value, "0") != 0; -} - // Parses 'str' for a 32-bit signed integer. If successful, writes // the result to *value and returns true; otherwise leaves *value // unchanged and returns false. @@ -564,6 +553,17 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) { return true; } +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + // Reads and returns a 32-bit integer stored in the environment // variable corresponding to the given flag; if it isn't set or // doesn't represent a valid 32-bit integer, returns default_value. diff --git a/src/gtest.cc b/src/gtest.cc index 8e8238a9..0d161e0e 100644 --- a/src/gtest.cc +++ b/src/gtest.cc @@ -145,6 +145,13 @@ static const char kUniversalFilter[] = "*"; // The default output file for XML output. static const char kDefaultOutputFile[] = "test_detail.xml"; +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + namespace internal { // The text used in failure messages to indicate the start of the @@ -2595,6 +2602,13 @@ void PrettyUnitTestResultPrinter::OnUnitTestStart( "Note: %s filter = %s\n", GTEST_NAME, filter); } + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %s of %s.\n", + internal::GetEnv(kTestShardIndex), + internal::GetEnv(kTestTotalShards)); + } + const internal::UnitTestImpl* const impl = unit_test->impl(); ColoredPrintf(COLOR_GREEN, "[==========] "); printf("Running %s from %s.\n", @@ -3510,6 +3524,11 @@ int UnitTestImpl::RunAllTests() { RegisterParameterizedTests(); + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + // Lists all the tests and exits if the --gtest_list_tests // flag was specified. if (GTEST_FLAG(list_tests)) { @@ -3528,9 +3547,15 @@ int UnitTestImpl::RunAllTests() { UnitTestEventListenerInterface * const printer = result_printer(); + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + // Compares the full test names with the filter to decide which // tests to run. - const bool has_tests_to_run = FilterTests() > 0; + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + // True iff at least one test has failed. bool failed = false; @@ -3586,12 +3611,126 @@ int UnitTestImpl::RunAllTests() { return failed ? 1 : 0; } +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { +#ifdef _MSC_VER // MSVC 8 deprecates fopen(). +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning on + // deprecated functions. +#endif + FILE* const file = fopen(test_shard_file, "w"); +#ifdef _MSC_VER +#pragma warning(pop) // Restores the warning state. +#endif + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* const var, Int32 default_val) { + const char* str_val = GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + // Compares the name of each test with the user-specified filter to // decide whether the test should be run, then records the result in // each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. // Returns the number of tests that should run. -int UnitTestImpl::FilterTests() { +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. int num_runnable_tests = 0; + int num_selected_tests = 0; for (const internal::ListNode<TestCase *> *test_case_node = test_cases_.Head(); test_case_node != NULL; @@ -3615,18 +3754,24 @@ int UnitTestImpl::FilterTests() { kDisableTestFilter); test_info->impl()->set_is_disabled(is_disabled); - const bool should_run = + const bool is_runnable = (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && internal::UnitTestOptions::FilterMatchesTest(test_case_name, test_name); - test_info->impl()->set_should_run(should_run); - test_case->set_should_run(test_case->should_run() || should_run); - if (should_run) { - num_runnable_tests++; - } + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->impl()->set_should_run(is_selected); + test_case->set_should_run(test_case->should_run() || is_selected); } } - return num_runnable_tests; + return num_selected_tests; } // Lists all tests by name. |