3 # Copyright 2009 Google Inc. All Rights Reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """Verifies that test shuffling works."""
34 from googletest.test import gtest_test_utils
36 # Command to run the googletest-shuffle-test_ program.
37 COMMAND = gtest_test_utils.GetTestExecutablePath('googletest-shuffle-test_')
39 # The environment variables for test sharding.
40 TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
41 SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
43 TEST_FILTER = 'A*.A:A*.B:C*'
50 SHUFFLED_ALL_TESTS = []
51 SHUFFLED_ACTIVE_TESTS = []
52 SHUFFLED_FILTERED_TESTS = []
53 SHUFFLED_SHARDED_TESTS = []
56 def AlsoRunDisabledTestsFlag():
57 return '--gtest_also_run_disabled_tests'
60 def FilterFlag(test_filter):
61 return '--gtest_filter=%s' % (test_filter,)
65 return '--gtest_repeat=%s' % (n,)
69 return '--gtest_shuffle'
72 def RandomSeedFlag(n):
73 return '--gtest_random_seed=%s' % (n,)
76 def RunAndReturnOutput(extra_env, args):
77 """Runs the test program and returns its output."""
79 environ_copy = os.environ.copy()
80 environ_copy.update(extra_env)
82 return gtest_test_utils.Subprocess([COMMAND] + args, env=environ_copy).output
85 def GetTestsForAllIterations(extra_env, args):
86 """Runs the test program and returns a list of test lists.
89 extra_env: a map from environment variables to their values
90 args: command line flags to pass to googletest-shuffle-test_
93 A list where the i-th element is the list of tests run in the i-th
98 for line in RunAndReturnOutput(extra_env, args).split('\n'):
99 if line.startswith('----'):
101 test_iterations.append(tests)
103 tests.append(line.strip()) # 'TestCaseName.TestName'
105 return test_iterations
108 def GetTestCases(tests):
109 """Returns a list of test cases in the given full test names.
112 tests: a list of full test names
115 A list of test cases from 'tests', in their original order.
116 Consecutive duplicates are removed.
121 test_case = test.split('.')[0]
122 if not test_case in test_cases:
123 test_cases.append(test_case)
128 def CalculateTestLists():
129 """Calculates the list of tests run under different flags."""
133 GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0]
137 ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0])
139 if not FILTERED_TESTS:
140 FILTERED_TESTS.extend(
141 GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0]
144 if not SHARDED_TESTS:
145 SHARDED_TESTS.extend(
146 GetTestsForAllIterations(
147 {TOTAL_SHARDS_ENV_VAR: '3', SHARD_INDEX_ENV_VAR: '1'}, []
151 if not SHUFFLED_ALL_TESTS:
152 SHUFFLED_ALL_TESTS.extend(
153 GetTestsForAllIterations(
154 {}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)]
158 if not SHUFFLED_ACTIVE_TESTS:
159 SHUFFLED_ACTIVE_TESTS.extend(
160 GetTestsForAllIterations({}, [ShuffleFlag(), RandomSeedFlag(1)])[0]
163 if not SHUFFLED_FILTERED_TESTS:
164 SHUFFLED_FILTERED_TESTS.extend(
165 GetTestsForAllIterations(
166 {}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)]
170 if not SHUFFLED_SHARDED_TESTS:
171 SHUFFLED_SHARDED_TESTS.extend(
172 GetTestsForAllIterations(
173 {TOTAL_SHARDS_ENV_VAR: '3', SHARD_INDEX_ENV_VAR: '1'},
174 [ShuffleFlag(), RandomSeedFlag(1)],
179 class GTestShuffleUnitTest(gtest_test_utils.TestCase):
180 """Tests test shuffling."""
185 def testShufflePreservesNumberOfTests(self):
186 self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS))
187 self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS))
188 self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS))
189 self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS))
191 def testShuffleChangesTestOrder(self):
192 self.assertTrue(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS)
194 SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS
197 SHUFFLED_FILTERED_TESTS != FILTERED_TESTS, SHUFFLED_FILTERED_TESTS
200 SHUFFLED_SHARDED_TESTS != SHARDED_TESTS, SHUFFLED_SHARDED_TESTS
203 def testShuffleChangesTestCaseOrder(self):
205 GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS),
206 GetTestCases(SHUFFLED_ALL_TESTS),
209 GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS),
210 GetTestCases(SHUFFLED_ACTIVE_TESTS),
213 GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS),
214 GetTestCases(SHUFFLED_FILTERED_TESTS),
217 GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS),
218 GetTestCases(SHUFFLED_SHARDED_TESTS),
221 def testShuffleDoesNotRepeatTest(self):
222 for test in SHUFFLED_ALL_TESTS:
225 SHUFFLED_ALL_TESTS.count(test),
226 '%s appears more than once' % (test,),
228 for test in SHUFFLED_ACTIVE_TESTS:
231 SHUFFLED_ACTIVE_TESTS.count(test),
232 '%s appears more than once' % (test,),
234 for test in SHUFFLED_FILTERED_TESTS:
237 SHUFFLED_FILTERED_TESTS.count(test),
238 '%s appears more than once' % (test,),
240 for test in SHUFFLED_SHARDED_TESTS:
243 SHUFFLED_SHARDED_TESTS.count(test),
244 '%s appears more than once' % (test,),
247 def testShuffleDoesNotCreateNewTest(self):
248 for test in SHUFFLED_ALL_TESTS:
249 self.assertTrue(test in ALL_TESTS, '%s is an invalid test' % (test,))
250 for test in SHUFFLED_ACTIVE_TESTS:
251 self.assertTrue(test in ACTIVE_TESTS, '%s is an invalid test' % (test,))
252 for test in SHUFFLED_FILTERED_TESTS:
253 self.assertTrue(test in FILTERED_TESTS, '%s is an invalid test' % (test,))
254 for test in SHUFFLED_SHARDED_TESTS:
255 self.assertTrue(test in SHARDED_TESTS, '%s is an invalid test' % (test,))
257 def testShuffleIncludesAllTests(self):
258 for test in ALL_TESTS:
259 self.assertTrue(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,))
260 for test in ACTIVE_TESTS:
261 self.assertTrue(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,))
262 for test in FILTERED_TESTS:
264 test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,)
266 for test in SHARDED_TESTS:
267 self.assertTrue(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,))
269 def testShuffleLeavesDeathTestsAtFront(self):
270 non_death_test_found = False
271 for test in SHUFFLED_ACTIVE_TESTS:
272 if 'DeathTest.' in test:
274 not non_death_test_found,
275 '%s appears after a non-death test' % (test,),
278 non_death_test_found = True
280 def _VerifyTestCasesDoNotInterleave(self, tests):
283 [test_case, _] = test.split('.')
284 if test_cases and test_cases[-1] != test_case:
285 test_cases.append(test_case)
288 test_cases.count(test_case),
289 'Test case %s is not grouped together in %s' % (test_case, tests),
292 def testShuffleDoesNotInterleaveTestCases(self):
293 self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS)
294 self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS)
295 self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS)
296 self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS)
298 def testShuffleRestoresOrderAfterEachIteration(self):
299 # Get the test lists in all 3 iterations, using random seed 1, 2,
300 # and 3 respectively. Google Test picks a different seed in each
301 # iteration, and this test depends on the current implementation
302 # picking successive numbers. This dependency is not ideal, but
303 # makes the test much easier to write.
304 # pylint: disable-next=unbalanced-tuple-unpacking
305 [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
306 GetTestsForAllIterations(
307 {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]
311 # Make sure running the tests with random seed 1 gets the same
312 # order as in iteration 1 above.
313 tests_with_seed1 = GetTestsForAllIterations(
314 {}, [ShuffleFlag(), RandomSeedFlag(1)]
316 self.assertEqual(tests_in_iteration1, tests_with_seed1)
318 # Make sure running the tests with random seed 2 gets the same
319 # order as in iteration 2 above. Success means that Google Test
320 # correctly restores the test order before re-shuffling at the
321 # beginning of iteration 2.
322 tests_with_seed2 = GetTestsForAllIterations(
323 {}, [ShuffleFlag(), RandomSeedFlag(2)]
325 self.assertEqual(tests_in_iteration2, tests_with_seed2)
327 # Make sure running the tests with random seed 3 gets the same
328 # order as in iteration 3 above. Success means that Google Test
329 # correctly restores the test order before re-shuffling at the
330 # beginning of iteration 3.
331 tests_with_seed3 = GetTestsForAllIterations(
332 {}, [ShuffleFlag(), RandomSeedFlag(3)]
334 self.assertEqual(tests_in_iteration3, tests_with_seed3)
336 def testShuffleGeneratesNewOrderInEachIteration(self):
337 # pylint: disable-next=unbalanced-tuple-unpacking
338 [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
339 GetTestsForAllIterations(
340 {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]
345 tests_in_iteration1 != tests_in_iteration2, tests_in_iteration1
348 tests_in_iteration1 != tests_in_iteration3, tests_in_iteration1
351 tests_in_iteration2 != tests_in_iteration3, tests_in_iteration2
354 def testShuffleShardedTestsPreservesPartition(self):
355 # If we run M tests on N shards, the same M tests should be run in
356 # total, regardless of the random seeds used by the shards.
357 tests1 = GetTestsForAllIterations(
358 {TOTAL_SHARDS_ENV_VAR: '3', SHARD_INDEX_ENV_VAR: '0'},
359 [ShuffleFlag(), RandomSeedFlag(1)],
361 tests2 = GetTestsForAllIterations(
362 {TOTAL_SHARDS_ENV_VAR: '3', SHARD_INDEX_ENV_VAR: '1'},
363 [ShuffleFlag(), RandomSeedFlag(20)],
365 tests3 = GetTestsForAllIterations(
366 {TOTAL_SHARDS_ENV_VAR: '3', SHARD_INDEX_ENV_VAR: '2'},
367 [ShuffleFlag(), RandomSeedFlag(25)],
369 sorted_sharded_tests = tests1 + tests2 + tests3
370 sorted_sharded_tests.sort()
371 sorted_active_tests = []
372 sorted_active_tests.extend(ACTIVE_TESTS)
373 sorted_active_tests.sort()
374 self.assertEqual(sorted_active_tests, sorted_sharded_tests)
377 if __name__ == '__main__':
378 gtest_test_utils.Main()