Implements the test sharding protocol. By Eric Fellheimer.

pull/496/merge
zhanyong.wan 16 years ago
parent 886cafd4a3
commit cd3e4016ea
  1. 35
      src/gtest-internal-inl.h
  2. 22
      src/gtest-port.cc
  3. 163
      src/gtest.cc
  4. 162
      test/gtest_filter_unittest.py
  5. 19
      test/gtest_filter_unittest_.cc
  6. 68
      test/gtest_output_test.py
  7. 6
      test/gtest_output_test_.cc
  8. 24
      test/gtest_output_test_golden_lin.txt
  9. 24
      test/gtest_output_test_golden_win.txt
  10. 158
      test/gtest_unittest.cc

@ -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.

@ -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.

@ -36,6 +36,9 @@ the GTEST_FILTER environment variable or the --gtest_filter flag.
This script tests such functionality by invoking
gtest_filter_unittest_ (a program written with Google Test) with different
environments and command line flags.
Note that test sharding may also influence which tests are filtered. Therefore,
we test that here also.
"""
__author__ = 'wan@google.com (Zhanyong Wan)'
@ -43,6 +46,7 @@ __author__ = 'wan@google.com (Zhanyong Wan)'
import os
import re
import sets
import tempfile
import unittest
import gtest_test_utils
@ -51,6 +55,11 @@ import gtest_test_utils
# The environment variable for specifying the test filters.
FILTER_ENV_VAR = 'GTEST_FILTER'
# The environment variables for test sharding.
TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE'
# The command line flag for specifying the test filters.
FILTER_FLAG = 'gtest_filter'
@ -103,6 +112,9 @@ ACTIVE_TESTS = [
'BazTest.TestOne',
'BazTest.TestA',
'BazTest.TestB',
'HasDeathTest.Test1',
'HasDeathTest.Test2',
] + PARAM_TESTS
param_tests_present = None
@ -121,7 +133,7 @@ def SetEnvVar(env_var, value):
def Run(command):
"""Runs a Google Test program and returns a list of full names of the
tests that were run.
tests that were run along with the test exit code.
"""
stdout_file = os.popen(command, 'r')
@ -137,9 +149,32 @@ def Run(command):
if match is not None:
test = match.group(1)
tests_run += [test_case + '.' + test]
stdout_file.close()
return tests_run
exit_code = stdout_file.close()
return (tests_run, exit_code)
def InvokeWithModifiedEnv(extra_env, function, *args, **kwargs):
"""Runs the given function and arguments in a modified environment."""
try:
original_env = os.environ.copy()
os.environ.update(extra_env)
return function(*args, **kwargs)
finally:
for key in extra_env.iterkeys():
if key in original_env:
os.environ[key] = original_env[key]
else:
del os.environ[key]
def RunWithSharding(total_shards, shard_index, command):
"""Runs the Google Test program shard and returns a list of full names of the
tests that were run along with the exit code.
"""
extra_env = {SHARD_INDEX_ENV_VAR: str(shard_index),
TOTAL_SHARDS_ENV_VAR: str(total_shards)}
return InvokeWithModifiedEnv(extra_env, Run, command)
# The unit test.
@ -160,6 +195,15 @@ class GTestFilterUnitTest(unittest.TestCase):
for elem in rhs:
self.assert_(elem in lhs, '%s in %s' % (elem, lhs))
def AssertPartitionIsValid(self, set_var, list_of_sets):
"""Asserts that list_of_sets is a valid partition of set_var."""
full_partition = []
for slice_var in list_of_sets:
full_partition.extend(slice_var)
self.assertEqual(len(set_var), len(full_partition))
self.assertEqual(sorted(set_var), sorted(full_partition))
def RunAndVerify(self, gtest_filter, tests_to_run):
"""Runs gtest_flag_unittest_ with the given filter, and verifies
that the right set of tests were run.
@ -173,7 +217,7 @@ class GTestFilterUnitTest(unittest.TestCase):
# First, tests using GTEST_FILTER.
SetEnvVar(FILTER_ENV_VAR, gtest_filter)
tests_run = Run(COMMAND)
tests_run = Run(COMMAND)[0]
SetEnvVar(FILTER_ENV_VAR, None)
self.AssertSetEqual(tests_run, tests_to_run)
@ -185,9 +229,27 @@ class GTestFilterUnitTest(unittest.TestCase):
else:
command = '%s --%s=%s' % (COMMAND, FILTER_FLAG, gtest_filter)
tests_run = Run(command)
tests_run = Run(command)[0]
self.AssertSetEqual(tests_run, tests_to_run)
def RunAndVerifyWithSharding(self, gtest_filter, total_shards, tests_to_run,
command=COMMAND, check_exit_0=False):
"""Runs all shards of gtest_flag_unittest_ with the given filter, and
verifies that the right set of tests were run. The union of tests run
on each shard should be identical to tests_to_run, without duplicates.
If check_exit_0, make sure that all shards returned 0.
"""
SetEnvVar(FILTER_ENV_VAR, gtest_filter)
partition = []
for i in range(0, total_shards):
(tests_run, exit_code) = RunWithSharding(total_shards, i, command)
if check_exit_0:
self.assert_(exit_code is None)
partition.append(tests_run)
self.AssertPartitionIsValid(tests_to_run, partition)
SetEnvVar(FILTER_ENV_VAR, None)
def RunAndVerifyAllowingDisabled(self, gtest_filter, tests_to_run):
"""Runs gtest_flag_unittest_ with the given filter, and enables
disabled tests. Verifies that the right set of tests were run.
@ -197,7 +259,7 @@ class GTestFilterUnitTest(unittest.TestCase):
if gtest_filter is not None:
command = '%s --%s=%s' % (command, FILTER_FLAG, gtest_filter)
tests_run = Run(command)
tests_run = Run(command)[0]
self.AssertSetEqual(tests_run, tests_to_run)
def setUp(self):
@ -214,10 +276,22 @@ class GTestFilterUnitTest(unittest.TestCase):
self.RunAndVerify(None, ACTIVE_TESTS)
def testDefaultBehaviorWithShards(self):
"""Tests the behavior of not specifying the filter, with sharding
enabled.
"""
self.RunAndVerifyWithSharding(None, 1, ACTIVE_TESTS)
self.RunAndVerifyWithSharding(None, 2, ACTIVE_TESTS)
self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) - 1, ACTIVE_TESTS)
self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS), ACTIVE_TESTS)
self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) + 1, ACTIVE_TESTS)
def testEmptyFilter(self):
"""Tests an empty filter."""
self.RunAndVerify('', [])
self.RunAndVerifyWithSharding('', 1, [])
self.RunAndVerifyWithSharding('', 2, [])
def testBadFilter(self):
"""Tests a filter that matches nothing."""
@ -230,12 +304,14 @@ class GTestFilterUnitTest(unittest.TestCase):
self.RunAndVerify('FooTest.Xyz', ['FooTest.Xyz'])
self.RunAndVerifyAllowingDisabled('FooTest.Xyz', ['FooTest.Xyz'])
self.RunAndVerifyWithSharding('FooTest.Xyz', 5, ['FooTest.Xyz'])
def testUniversalFilters(self):
"""Tests filters that match everything."""
self.RunAndVerify('*', ACTIVE_TESTS)
self.RunAndVerify('*.*', ACTIVE_TESTS)
self.RunAndVerifyWithSharding('*.*', len(ACTIVE_TESTS) - 3, ACTIVE_TESTS)
self.RunAndVerifyAllowingDisabled('*', ACTIVE_TESTS + DISABLED_TESTS)
self.RunAndVerifyAllowingDisabled('*.*', ACTIVE_TESTS + DISABLED_TESTS)
@ -289,7 +365,10 @@ class GTestFilterUnitTest(unittest.TestCase):
'BazTest.TestOne',
'BazTest.TestA',
'BazTest.TestB' ] + PARAM_TESTS)
'BazTest.TestB',
'HasDeathTest.Test1',
'HasDeathTest.Test2', ] + PARAM_TESTS)
def testWildcardInTestName(self):
"""Tests using wildcard in the test name."""
@ -350,7 +429,8 @@ class GTestFilterUnitTest(unittest.TestCase):
])
def testNegativeFilters(self):
self.RunAndVerify('*-FooTest.Abc', [
self.RunAndVerify('*-HasDeathTest.Test1', [
'FooTest.Abc',
'FooTest.Xyz',
'BarTest.TestOne',
@ -360,14 +440,20 @@ class GTestFilterUnitTest(unittest.TestCase):
'BazTest.TestOne',
'BazTest.TestA',
'BazTest.TestB',
'HasDeathTest.Test2',
] + PARAM_TESTS)
self.RunAndVerify('*-FooTest.Abc:BazTest.*', [
self.RunAndVerify('*-FooTest.Abc:HasDeathTest.*', [
'FooTest.Xyz',
'BarTest.TestOne',
'BarTest.TestTwo',
'BarTest.TestThree',
'BazTest.TestOne',
'BazTest.TestA',
'BazTest.TestB',
] + PARAM_TESTS)
self.RunAndVerify('BarTest.*-BarTest.TestOne', [
@ -376,7 +462,7 @@ class GTestFilterUnitTest(unittest.TestCase):
])
# Tests without leading '*'.
self.RunAndVerify('-FooTest.Abc:FooTest.Xyz', [
self.RunAndVerify('-FooTest.Abc:FooTest.Xyz:HasDeathTest.*', [
'BarTest.TestOne',
'BarTest.TestTwo',
'BarTest.TestThree',
@ -412,11 +498,65 @@ class GTestFilterUnitTest(unittest.TestCase):
SetEnvVar(FILTER_ENV_VAR, 'Foo*')
command = '%s --%s=%s' % (COMMAND, FILTER_FLAG, '*One')
tests_run = Run(command)
tests_run = Run(command)[0]
SetEnvVar(FILTER_ENV_VAR, None)
self.AssertSetEqual(tests_run, ['BarTest.TestOne', 'BazTest.TestOne'])
def testShardStatusFileIsCreated(self):
"""Tests that the shard file is created if specified in the environment."""
test_tmpdir = tempfile.mkdtemp()
shard_status_file = os.path.join(test_tmpdir, 'shard_status_file')
self.assert_(not os.path.exists(shard_status_file))
extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file}
stdout_file = InvokeWithModifiedEnv(extra_env, os.popen, COMMAND, 'r')
try:
stdout_file.readlines()
finally:
stdout_file.close()
self.assert_(os.path.exists(shard_status_file))
os.remove(shard_status_file)
os.removedirs(test_tmpdir)
def testShardStatusFileIsCreatedWithListTests(self):
"""Tests that the shard file is created with --gtest_list_tests."""
test_tmpdir = tempfile.mkdtemp()
shard_status_file = os.path.join(test_tmpdir, 'shard_status_file2')
self.assert_(not os.path.exists(shard_status_file))
extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file}
stdout_file = InvokeWithModifiedEnv(extra_env, os.popen,
'%s --gtest_list_tests' % COMMAND, 'r')
try:
stdout_file.readlines()
finally:
stdout_file.close()
self.assert_(os.path.exists(shard_status_file))
os.remove(shard_status_file)
os.removedirs(test_tmpdir)
def testShardingWorksWithDeathTests(self):
"""Tests integration with death tests and sharding."""
gtest_filter = 'HasDeathTest.*:SeqP/*'
expected_tests = [
'HasDeathTest.Test1',
'HasDeathTest.Test2',
'SeqP/ParamTest.TestX/0',
'SeqP/ParamTest.TestX/1',
'SeqP/ParamTest.TestY/0',
'SeqP/ParamTest.TestY/1',
]
for command in (COMMAND + ' --gtest_death_test_style=threadsafe',
COMMAND + ' --gtest_death_test_style=fast'):
self.RunAndVerifyWithSharding(gtest_filter, 3, expected_tests,
check_exit_0=True, command=command)
self.RunAndVerifyWithSharding(gtest_filter, 5, expected_tests,
check_exit_0=True, command=command)
if __name__ == '__main__':
gtest_test_utils.Main()

@ -91,6 +91,25 @@ TEST(BazTest, DISABLED_TestC) {
FAIL() << "Expected failure.";
}
// Test case HasDeathTest
TEST(HasDeathTest, Test1) {
#ifdef GTEST_HAS_DEATH_TEST
EXPECT_DEATH({exit(1);},
".*");
#endif // GTEST_HAS_DEATH_TEST
}
// We need at least two death tests to make sure that the all death tests
// aren't on the first shard.
TEST(HasDeathTest, Test2) {
#ifdef GTEST_HAS_DEATH_TEST
EXPECT_DEATH({exit(1);},
".*");
#endif // GTEST_HAS_DEATH_TEST
}
// Test case FoobarTest
TEST(DISABLED_FoobarTest, Test1) {

@ -64,13 +64,17 @@ PROGRAM_PATH = os.path.join(gtest_test_utils.GetBuildDir(), PROGRAM)
# At least one command we exercise must not have the
# --gtest_internal_skip_environment_and_ad_hoc_tests flag.
COMMAND_WITH_COLOR = PROGRAM_PATH + ' --gtest_color=yes'
COMMAND_WITH_TIME = (PROGRAM_PATH + ' --gtest_print_time '
COMMAND_WITH_COLOR = ({}, PROGRAM_PATH + ' --gtest_color=yes')
COMMAND_WITH_TIME = ({}, PROGRAM_PATH + ' --gtest_print_time '
'--gtest_internal_skip_environment_and_ad_hoc_tests '
'--gtest_filter="FatalFailureTest.*:LoggingTest.*"')
COMMAND_WITH_DISABLED = (PROGRAM_PATH + ' --gtest_also_run_disabled_tests '
COMMAND_WITH_DISABLED = ({}, PROGRAM_PATH + ' --gtest_also_run_disabled_tests '
'--gtest_internal_skip_environment_and_ad_hoc_tests '
'--gtest_filter="*DISABLED_*"')
COMMAND_WITH_SHARDING = ({'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'},
PROGRAM_PATH +
' --gtest_internal_skip_environment_and_ad_hoc_tests '
' --gtest_filter="PassingTest.*"')
GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(),
GOLDEN_NAME)
@ -136,19 +140,26 @@ def NormalizeOutput(output):
return output
def IterShellCommandOutput(cmd, stdin_string=None):
def IterShellCommandOutput(env_cmd, stdin_string=None):
"""Runs a command in a sub-process, and iterates the lines in its STDOUT.
Args:
cmd: The shell command.
stdin_string: The string to be fed to the STDIN of the sub-process;
If None, the sub-process will inherit the STDIN
from the parent process.
env_cmd: The shell command. A 2-tuple where element 0 is a dict
of extra environment variables to set, and element 1
is a string with the command and any flags.
stdin_string: The string to be fed to the STDIN of the sub-process;
If None, the sub-process will inherit the STDIN
from the parent process.
"""
# Spawns cmd in a sub-process, and gets its standard I/O file objects.
stdin_file, stdout_file = os.popen2(cmd, 'b')
# Set and save the environment properly.
old_env_vars = dict(os.environ)
os.environ.update(env_cmd[0])
stdin_file, stdout_file = os.popen2(env_cmd[1], 'b')
os.environ.clear()
os.environ.update(old_env_vars)
# If the caller didn't specify a string for STDIN, gets it from the
# parent process.
@ -168,39 +179,50 @@ def IterShellCommandOutput(cmd, stdin_string=None):
yield line
def GetShellCommandOutput(cmd, stdin_string=None):
def GetShellCommandOutput(env_cmd, stdin_string=None):
"""Runs a command in a sub-process, and returns its STDOUT in a string.
Args:
cmd: The shell command.
stdin_string: The string to be fed to the STDIN of the sub-process;
If None, the sub-process will inherit the STDIN
from the parent process.
env_cmd: The shell command. A 2-tuple where element 0 is a dict
of extra environment variables to set, and element 1
is a string with the command and any flags.
stdin_string: The string to be fed to the STDIN of the sub-process;
If None, the sub-process will inherit the STDIN
from the parent process.
"""
lines = list(IterShellCommandOutput(cmd, stdin_string))
lines = list(IterShellCommandOutput(env_cmd, stdin_string))
return string.join(lines, '')
def GetCommandOutput(cmd):
def GetCommandOutput(env_cmd):
"""Runs a command and returns its output with all file location
info stripped off.
Args:
cmd: the shell command.
env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra
environment variables to set, and element 1 is a string with
the command and any flags.
"""
# Disables exception pop-ups on Windows.
os.environ['GTEST_CATCH_EXCEPTIONS'] = '1'
return NormalizeOutput(GetShellCommandOutput(cmd, ''))
return NormalizeOutput(GetShellCommandOutput(env_cmd, ''))
def GetOutputOfAllCommands():
"""Returns concatenated output from several representative commands."""
return (GetCommandOutput(COMMAND_WITH_COLOR) +
GetCommandOutput(COMMAND_WITH_TIME) +
GetCommandOutput(COMMAND_WITH_DISABLED) +
GetCommandOutput(COMMAND_WITH_SHARDING))
class GTestOutputTest(unittest.TestCase):
def testOutput(self):
output = (GetCommandOutput(COMMAND_WITH_COLOR) +
GetCommandOutput(COMMAND_WITH_TIME) +
GetCommandOutput(COMMAND_WITH_DISABLED))
output = GetOutputOfAllCommands()
golden_file = open(GOLDEN_PATH, 'rb')
golden = golden_file.read()
golden_file.close()
@ -214,9 +236,7 @@ class GTestOutputTest(unittest.TestCase):
if __name__ == '__main__':
if sys.argv[1:] == [GENGOLDEN_FLAG]:
output = (GetCommandOutput(COMMAND_WITH_COLOR) +
GetCommandOutput(COMMAND_WITH_TIME) +
GetCommandOutput(COMMAND_WITH_DISABLED))
output = GetOutputOfAllCommands()
golden_file = open(GOLDEN_PATH, 'wb')
golden_file.write(output)
golden_file.close()

@ -85,6 +85,12 @@ void TryTestSubroutine() {
FAIL() << "This should never be reached.";
}
TEST(PassingTest, PassingTest1) {
}
TEST(PassingTest, PassingTest2) {
}
// Tests catching a fatal failure in a subroutine.
TEST(FatalFailureTest, FatalFailureInSubroutine) {
printf("(expecting a failure that x should be 1)\n");

@ -7,7 +7,7 @@ Expected: true
gtest_output_test_.cc:#: Failure
Value of: 3
Expected: 2
[==========] Running 54 tests from 22 test cases.
[==========] Running 56 tests from 23 test cases.
[----------] Global test environment set-up.
FooEnvironment::SetUp() called.
BarEnvironment::SetUp() called.
@ -26,6 +26,11 @@ BarEnvironment::SetUp() called.
[----------] 1 test from My/ATypeParamDeathTest/1, where TypeParam = double
[ RUN ] My/ATypeParamDeathTest/1.ShouldRunFirst
[ OK ] My/ATypeParamDeathTest/1.ShouldRunFirst
[----------] 2 tests from PassingTest
[ RUN ] PassingTest.PassingTest1
[ OK ] PassingTest.PassingTest1
[ RUN ] PassingTest.PassingTest2
[ OK ] PassingTest.PassingTest2
[----------] 3 tests from FatalFailureTest
[ RUN ] FatalFailureTest.FatalFailureInSubroutine
(expecting a failure that x should be 1)
@ -508,8 +513,8 @@ FooEnvironment::TearDown() called.
gtest_output_test_.cc:#: Failure
Failed
Expected fatal failure.
[==========] 54 tests from 22 test cases ran.
[ PASSED ] 19 tests.
[==========] 56 tests from 23 test cases ran.
[ PASSED ] 21 tests.
[ FAILED ] 35 tests, listed below:
[ FAILED ] FatalFailureTest.FatalFailureInSubroutine
[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine
@ -612,3 +617,16 @@ Note: Google Test filter = *DISABLED_*
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran.
[ PASSED ] 1 test.
Note: Google Test filter = PassingTest.*
Note: This is test shard 1 of 2.
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PassingTest
[ RUN ] PassingTest.PassingTest2
[ OK ] PassingTest.PassingTest2
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran.
[ PASSED ] 1 test.
YOU HAVE 1 DISABLED TEST

@ -5,10 +5,15 @@ gtest_output_test_.cc:#: error: Value of: false
Expected: true
gtest_output_test_.cc:#: error: Value of: 3
Expected: 2
[==========] Running 50 tests from 20 test cases.
[==========] Running 52 tests from 21 test cases.
[----------] Global test environment set-up.
FooEnvironment::SetUp() called.
BarEnvironment::SetUp() called.
[----------] 2 tests from PassingTest
[ RUN ] PassingTest.PassingTest1
[ OK ] PassingTest.PassingTest1
[ RUN ] PassingTest.PassingTest2
[ OK ] PassingTest.PassingTest2
[----------] 3 tests from FatalFailureTest
[ RUN ] FatalFailureTest.FatalFailureInSubroutine
(expecting a failure that x should be 1)
@ -440,8 +445,8 @@ Expected non-fatal failure.
FooEnvironment::TearDown() called.
gtest_output_test_.cc:#: error: Failed
Expected fatal failure.
[==========] 50 tests from 20 test cases ran.
[ PASSED ] 14 tests.
[==========] 52 tests from 21 test cases ran.
[ PASSED ] 16 tests.
[ FAILED ] 36 tests, listed below:
[ FAILED ] FatalFailureTest.FatalFailureInSubroutine
[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine
@ -540,3 +545,16 @@ Note: Google Test filter = *DISABLED_*
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran.
[ PASSED ] 1 test.
Note: Google Test filter = PassingTest.*
Note: This is test shard 1 of 2.
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from PassingTest
[ RUN ] PassingTest.PassingTest2
[ OK ] PassingTest.PassingTest2
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran.
[ PASSED ] 1 test.
YOU HAVE 1 DISABLED TEST

@ -138,7 +138,10 @@ using testing::internal::GetTestTypeId;
using testing::internal::GetTypeId;
using testing::internal::GTestFlagSaver;
using testing::internal::Int32;
using testing::internal::Int32FromEnvOrDie;
using testing::internal::List;
using testing::internal::ShouldRunTestOnShard;
using testing::internal::ShouldShard;
using testing::internal::ShouldUseColor;
using testing::internal::StreamableToString;
using testing::internal::String;
@ -1375,6 +1378,161 @@ TEST(ParseInt32FlagTest, ParsesAndReturnsValidValue) {
EXPECT_EQ(-789, value);
}
// Tests that Int32FromEnvOrDie() parses the value of the var or
// returns the correct default.
TEST(Int32FromEnvOrDieTest, ParsesAndReturnsValidValue) {
EXPECT_EQ(333, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER "UnsetVar", 333));
SetEnv(GTEST_FLAG_PREFIX_UPPER "UnsetVar", "123");
EXPECT_EQ(123, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER "UnsetVar", 333));
SetEnv(GTEST_FLAG_PREFIX_UPPER "UnsetVar", "-123");
EXPECT_EQ(-123, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER "UnsetVar", 333));
}
#ifdef GTEST_HAS_DEATH_TEST
// Tests that Int32FromEnvOrDie() aborts with an error message
// if the variable is not an Int32.
TEST(Int32FromEnvOrDieDeathTest, AbortsOnFailure) {
SetEnv(GTEST_FLAG_PREFIX_UPPER "VAR", "xxx");
EXPECT_DEATH({Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER "VAR", 123);},
".*");
}
// Tests that Int32FromEnvOrDie() aborts with an error message
// if the variable cannot be represnted by an Int32.
TEST(Int32FromEnvOrDieDeathTest, AbortsOnInt32Overflow) {
SetEnv(GTEST_FLAG_PREFIX_UPPER "VAR", "1234567891234567891234");
EXPECT_DEATH({Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER "VAR", 123);},
".*");
}
#endif // GTEST_HAS_DEATH_TEST
// Tests that ShouldRunTestOnShard() selects all tests
// where there is 1 shard.
TEST(ShouldRunTestOnShardTest, IsPartitionWhenThereIsOneShard) {
EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 0));
EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 1));
EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 2));
EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 3));
EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 4));
}
class ShouldShardTest : public testing::Test {
protected:
virtual void SetUp() {
index_var_ = GTEST_FLAG_PREFIX_UPPER "INDEX";
total_var_ = GTEST_FLAG_PREFIX_UPPER "TOTAL";
}
virtual void TearDown() {
SetEnv(index_var_, "");
SetEnv(total_var_, "");
}
const char* index_var_;
const char* total_var_;
};
// Tests that sharding is disabled if neither of the environment variables
// are set.
TEST_F(ShouldShardTest, ReturnsFalseWhenNeitherEnvVarIsSet) {
SetEnv(index_var_, "");
SetEnv(total_var_, "");
EXPECT_FALSE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
}
// Tests that sharding is not enabled if total_shards == 1.
TEST_F(ShouldShardTest, ReturnsFalseWhenTotalShardIsOne) {
SetEnv(index_var_, "0");
SetEnv(total_var_, "1");
EXPECT_FALSE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
}
// Tests that sharding is enabled if total_shards > 1 and
// we are not in a death test subprocess.
TEST_F(ShouldShardTest, WorksWhenShardEnvVarsAreValid) {
SetEnv(index_var_, "4");
SetEnv(total_var_, "22");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
SetEnv(index_var_, "8");
SetEnv(total_var_, "9");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
SetEnv(index_var_, "0");
SetEnv(total_var_, "9");
EXPECT_TRUE(ShouldShard(total_var_, index_var_, false));
EXPECT_FALSE(ShouldShard(total_var_, index_var_, true));
}
#ifdef GTEST_HAS_DEATH_TEST
// Tests that we exit in error if the sharding values are not valid.
TEST_F(ShouldShardTest, AbortsWhenShardingEnvVarsAreInvalid) {
SetEnv(index_var_, "4");
SetEnv(total_var_, "4");
EXPECT_DEATH({ShouldShard(total_var_, index_var_, false);},
".*");
SetEnv(index_var_, "4");
SetEnv(total_var_, "-2");
EXPECT_DEATH({ShouldShard(total_var_, index_var_, false);},
".*");
SetEnv(index_var_, "5");
SetEnv(total_var_, "");
EXPECT_DEATH({ShouldShard(total_var_, index_var_, false);},
".*");
SetEnv(index_var_, "");
SetEnv(total_var_, "5");
EXPECT_DEATH({ShouldShard(total_var_, index_var_, false);},
".*");
}
#endif // GTEST_HAS_DEATH_TEST
// Tests that ShouldRunTestOnShard is a partition when 5
// shards are used.
TEST(ShouldRunTestOnShardTest, IsPartitionWhenThereAreFiveShards) {
// Choose an arbitrary number of tests and shards.
const int num_tests = 17;
const int num_shards = 5;
// Check partitioning: each test should be on exactly 1 shard.
for (int test_id = 0; test_id < num_tests; test_id++) {
int prev_selected_shard_index = -1;
for (int shard_index = 0; shard_index < num_shards; shard_index++) {
if (ShouldRunTestOnShard(num_shards, shard_index, test_id)) {
if (prev_selected_shard_index < 0) {
prev_selected_shard_index = shard_index;
} else {
ADD_FAILURE() << "Shard " << prev_selected_shard_index << " and "
<< shard_index << " are both selected to run test " << test_id;
}
}
}
}
// Check balance: This is not required by the sharding protocol, but is a
// desirable property for performance.
for (int shard_index = 0; shard_index < num_shards; shard_index++) {
int num_tests_on_shard = 0;
for (int test_id = 0; test_id < num_tests; test_id++) {
num_tests_on_shard +=
ShouldRunTestOnShard(num_shards, shard_index, test_id);
}
EXPECT_GE(num_tests_on_shard, num_tests / num_shards);
}
}
// For the same reason we are not explicitly testing everything in the
// Test class, there are no separate tests for the following classes
// (except for some trivial cases):

Loading…
Cancel
Save