Changeset View
Changeset View
Standalone View
Standalone View
google/googletest/dist/googletest/test/googletest-shuffle-test.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
#!/usr/bin/env python | |||||
# | |||||
# Copyright 2009 Google Inc. All Rights Reserved. | |||||
# | |||||
# Redistribution and use in source and binary forms, with or without | |||||
# modification, are permitted provided that the following conditions are | |||||
# met: | |||||
# | |||||
# * Redistributions of source code must retain the above copyright | |||||
# notice, this list of conditions and the following disclaimer. | |||||
# * Redistributions in binary form must reproduce the above | |||||
# copyright notice, this list of conditions and the following disclaimer | |||||
# in the documentation and/or other materials provided with the | |||||
# distribution. | |||||
# * Neither the name of Google Inc. nor the names of its | |||||
# contributors may be used to endorse or promote products derived from | |||||
# this software without specific prior written permission. | |||||
# | |||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
"""Verifies that test shuffling works.""" | |||||
import os | |||||
import gtest_test_utils | |||||
# Command to run the googletest-shuffle-test_ program. | |||||
COMMAND = gtest_test_utils.GetTestExecutablePath('googletest-shuffle-test_') | |||||
# The environment variables for test sharding. | |||||
TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS' | |||||
SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX' | |||||
TEST_FILTER = 'A*.A:A*.B:C*' | |||||
ALL_TESTS = [] | |||||
ACTIVE_TESTS = [] | |||||
FILTERED_TESTS = [] | |||||
SHARDED_TESTS = [] | |||||
SHUFFLED_ALL_TESTS = [] | |||||
SHUFFLED_ACTIVE_TESTS = [] | |||||
SHUFFLED_FILTERED_TESTS = [] | |||||
SHUFFLED_SHARDED_TESTS = [] | |||||
def AlsoRunDisabledTestsFlag(): | |||||
return '--gtest_also_run_disabled_tests' | |||||
def FilterFlag(test_filter): | |||||
return '--gtest_filter=%s' % (test_filter,) | |||||
def RepeatFlag(n): | |||||
return '--gtest_repeat=%s' % (n,) | |||||
def ShuffleFlag(): | |||||
return '--gtest_shuffle' | |||||
def RandomSeedFlag(n): | |||||
return '--gtest_random_seed=%s' % (n,) | |||||
def RunAndReturnOutput(extra_env, args): | |||||
"""Runs the test program and returns its output.""" | |||||
environ_copy = os.environ.copy() | |||||
environ_copy.update(extra_env) | |||||
return gtest_test_utils.Subprocess([COMMAND] + args, env=environ_copy).output | |||||
def GetTestsForAllIterations(extra_env, args): | |||||
"""Runs the test program and returns a list of test lists. | |||||
Args: | |||||
extra_env: a map from environment variables to their values | |||||
args: command line flags to pass to googletest-shuffle-test_ | |||||
Returns: | |||||
A list where the i-th element is the list of tests run in the i-th | |||||
test iteration. | |||||
""" | |||||
test_iterations = [] | |||||
for line in RunAndReturnOutput(extra_env, args).split('\n'): | |||||
if line.startswith('----'): | |||||
tests = [] | |||||
test_iterations.append(tests) | |||||
elif line.strip(): | |||||
tests.append(line.strip()) # 'TestCaseName.TestName' | |||||
return test_iterations | |||||
def GetTestCases(tests): | |||||
"""Returns a list of test cases in the given full test names. | |||||
Args: | |||||
tests: a list of full test names | |||||
Returns: | |||||
A list of test cases from 'tests', in their original order. | |||||
Consecutive duplicates are removed. | |||||
""" | |||||
test_cases = [] | |||||
for test in tests: | |||||
test_case = test.split('.')[0] | |||||
if not test_case in test_cases: | |||||
test_cases.append(test_case) | |||||
return test_cases | |||||
def CalculateTestLists(): | |||||
"""Calculates the list of tests run under different flags.""" | |||||
if not ALL_TESTS: | |||||
ALL_TESTS.extend( | |||||
GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0]) | |||||
if not ACTIVE_TESTS: | |||||
ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0]) | |||||
if not FILTERED_TESTS: | |||||
FILTERED_TESTS.extend( | |||||
GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0]) | |||||
if not SHARDED_TESTS: | |||||
SHARDED_TESTS.extend( | |||||
GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', | |||||
SHARD_INDEX_ENV_VAR: '1'}, | |||||
[])[0]) | |||||
if not SHUFFLED_ALL_TESTS: | |||||
SHUFFLED_ALL_TESTS.extend(GetTestsForAllIterations( | |||||
{}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)])[0]) | |||||
if not SHUFFLED_ACTIVE_TESTS: | |||||
SHUFFLED_ACTIVE_TESTS.extend(GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(1)])[0]) | |||||
if not SHUFFLED_FILTERED_TESTS: | |||||
SHUFFLED_FILTERED_TESTS.extend(GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)])[0]) | |||||
if not SHUFFLED_SHARDED_TESTS: | |||||
SHUFFLED_SHARDED_TESTS.extend( | |||||
GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', | |||||
SHARD_INDEX_ENV_VAR: '1'}, | |||||
[ShuffleFlag(), RandomSeedFlag(1)])[0]) | |||||
class GTestShuffleUnitTest(gtest_test_utils.TestCase): | |||||
"""Tests test shuffling.""" | |||||
def setUp(self): | |||||
CalculateTestLists() | |||||
def testShufflePreservesNumberOfTests(self): | |||||
self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS)) | |||||
self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS)) | |||||
self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS)) | |||||
self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS)) | |||||
def testShuffleChangesTestOrder(self): | |||||
self.assert_(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS) | |||||
self.assert_(SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS) | |||||
self.assert_(SHUFFLED_FILTERED_TESTS != FILTERED_TESTS, | |||||
SHUFFLED_FILTERED_TESTS) | |||||
self.assert_(SHUFFLED_SHARDED_TESTS != SHARDED_TESTS, | |||||
SHUFFLED_SHARDED_TESTS) | |||||
def testShuffleChangesTestCaseOrder(self): | |||||
self.assert_(GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS), | |||||
GetTestCases(SHUFFLED_ALL_TESTS)) | |||||
self.assert_( | |||||
GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS), | |||||
GetTestCases(SHUFFLED_ACTIVE_TESTS)) | |||||
self.assert_( | |||||
GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS), | |||||
GetTestCases(SHUFFLED_FILTERED_TESTS)) | |||||
self.assert_( | |||||
GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS), | |||||
GetTestCases(SHUFFLED_SHARDED_TESTS)) | |||||
def testShuffleDoesNotRepeatTest(self): | |||||
for test in SHUFFLED_ALL_TESTS: | |||||
self.assertEqual(1, SHUFFLED_ALL_TESTS.count(test), | |||||
'%s appears more than once' % (test,)) | |||||
for test in SHUFFLED_ACTIVE_TESTS: | |||||
self.assertEqual(1, SHUFFLED_ACTIVE_TESTS.count(test), | |||||
'%s appears more than once' % (test,)) | |||||
for test in SHUFFLED_FILTERED_TESTS: | |||||
self.assertEqual(1, SHUFFLED_FILTERED_TESTS.count(test), | |||||
'%s appears more than once' % (test,)) | |||||
for test in SHUFFLED_SHARDED_TESTS: | |||||
self.assertEqual(1, SHUFFLED_SHARDED_TESTS.count(test), | |||||
'%s appears more than once' % (test,)) | |||||
def testShuffleDoesNotCreateNewTest(self): | |||||
for test in SHUFFLED_ALL_TESTS: | |||||
self.assert_(test in ALL_TESTS, '%s is an invalid test' % (test,)) | |||||
for test in SHUFFLED_ACTIVE_TESTS: | |||||
self.assert_(test in ACTIVE_TESTS, '%s is an invalid test' % (test,)) | |||||
for test in SHUFFLED_FILTERED_TESTS: | |||||
self.assert_(test in FILTERED_TESTS, '%s is an invalid test' % (test,)) | |||||
for test in SHUFFLED_SHARDED_TESTS: | |||||
self.assert_(test in SHARDED_TESTS, '%s is an invalid test' % (test,)) | |||||
def testShuffleIncludesAllTests(self): | |||||
for test in ALL_TESTS: | |||||
self.assert_(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,)) | |||||
for test in ACTIVE_TESTS: | |||||
self.assert_(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,)) | |||||
for test in FILTERED_TESTS: | |||||
self.assert_(test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,)) | |||||
for test in SHARDED_TESTS: | |||||
self.assert_(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,)) | |||||
def testShuffleLeavesDeathTestsAtFront(self): | |||||
non_death_test_found = False | |||||
for test in SHUFFLED_ACTIVE_TESTS: | |||||
if 'DeathTest.' in test: | |||||
self.assert_(not non_death_test_found, | |||||
'%s appears after a non-death test' % (test,)) | |||||
else: | |||||
non_death_test_found = True | |||||
def _VerifyTestCasesDoNotInterleave(self, tests): | |||||
test_cases = [] | |||||
for test in tests: | |||||
[test_case, _] = test.split('.') | |||||
if test_cases and test_cases[-1] != test_case: | |||||
test_cases.append(test_case) | |||||
self.assertEqual(1, test_cases.count(test_case), | |||||
'Test case %s is not grouped together in %s' % | |||||
(test_case, tests)) | |||||
def testShuffleDoesNotInterleaveTestCases(self): | |||||
self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS) | |||||
self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS) | |||||
self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS) | |||||
self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS) | |||||
def testShuffleRestoresOrderAfterEachIteration(self): | |||||
# Get the test lists in all 3 iterations, using random seed 1, 2, | |||||
# and 3 respectively. Google Test picks a different seed in each | |||||
# iteration, and this test depends on the current implementation | |||||
# picking successive numbers. This dependency is not ideal, but | |||||
# makes the test much easier to write. | |||||
[tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( | |||||
GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) | |||||
# Make sure running the tests with random seed 1 gets the same | |||||
# order as in iteration 1 above. | |||||
[tests_with_seed1] = GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(1)]) | |||||
self.assertEqual(tests_in_iteration1, tests_with_seed1) | |||||
# Make sure running the tests with random seed 2 gets the same | |||||
# order as in iteration 2 above. Success means that Google Test | |||||
# correctly restores the test order before re-shuffling at the | |||||
# beginning of iteration 2. | |||||
[tests_with_seed2] = GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(2)]) | |||||
self.assertEqual(tests_in_iteration2, tests_with_seed2) | |||||
# Make sure running the tests with random seed 3 gets the same | |||||
# order as in iteration 3 above. Success means that Google Test | |||||
# correctly restores the test order before re-shuffling at the | |||||
# beginning of iteration 3. | |||||
[tests_with_seed3] = GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(3)]) | |||||
self.assertEqual(tests_in_iteration3, tests_with_seed3) | |||||
def testShuffleGeneratesNewOrderInEachIteration(self): | |||||
[tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( | |||||
GetTestsForAllIterations( | |||||
{}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) | |||||
self.assert_(tests_in_iteration1 != tests_in_iteration2, | |||||
tests_in_iteration1) | |||||
self.assert_(tests_in_iteration1 != tests_in_iteration3, | |||||
tests_in_iteration1) | |||||
self.assert_(tests_in_iteration2 != tests_in_iteration3, | |||||
tests_in_iteration2) | |||||
def testShuffleShardedTestsPreservesPartition(self): | |||||
# If we run M tests on N shards, the same M tests should be run in | |||||
# total, regardless of the random seeds used by the shards. | |||||
[tests1] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', | |||||
SHARD_INDEX_ENV_VAR: '0'}, | |||||
[ShuffleFlag(), RandomSeedFlag(1)]) | |||||
[tests2] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', | |||||
SHARD_INDEX_ENV_VAR: '1'}, | |||||
[ShuffleFlag(), RandomSeedFlag(20)]) | |||||
[tests3] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', | |||||
SHARD_INDEX_ENV_VAR: '2'}, | |||||
[ShuffleFlag(), RandomSeedFlag(25)]) | |||||
sorted_sharded_tests = tests1 + tests2 + tests3 | |||||
sorted_sharded_tests.sort() | |||||
sorted_active_tests = [] | |||||
sorted_active_tests.extend(ACTIVE_TESTS) | |||||
sorted_active_tests.sort() | |||||
self.assertEqual(sorted_active_tests, sorted_sharded_tests) | |||||
if __name__ == '__main__': | |||||
gtest_test_utils.Main() |