Implements the test sharding protocol. By Eric Fellheimer.
This commit is contained in:
		@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										163
									
								
								src/gtest.cc
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								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.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user