123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- #!/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."""
- __author__ = 'wan@google.com (Zhanyong Wan)'
- import os
- import gtest_test_utils
- # Command to run the gtest_shuffle_test_ program.
- COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_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 gtest_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()
|