googletest-shuffle-test.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2009 Google Inc. All Rights Reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions are
  7. # met:
  8. #
  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
  14. # distribution.
  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.
  18. #
  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.
  30. """Verifies that test shuffling works."""
  31. import os
  32. import gtest_test_utils
  33. # Command to run the googletest-shuffle-test_ program.
  34. COMMAND = gtest_test_utils.GetTestExecutablePath('googletest-shuffle-test_')
  35. # The environment variables for test sharding.
  36. TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
  37. SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
  38. TEST_FILTER = 'A*.A:A*.B:C*'
  39. ALL_TESTS = []
  40. ACTIVE_TESTS = []
  41. FILTERED_TESTS = []
  42. SHARDED_TESTS = []
  43. SHUFFLED_ALL_TESTS = []
  44. SHUFFLED_ACTIVE_TESTS = []
  45. SHUFFLED_FILTERED_TESTS = []
  46. SHUFFLED_SHARDED_TESTS = []
  47. def AlsoRunDisabledTestsFlag():
  48. return '--gtest_also_run_disabled_tests'
  49. def FilterFlag(test_filter):
  50. return '--gtest_filter=%s' % (test_filter,)
  51. def RepeatFlag(n):
  52. return '--gtest_repeat=%s' % (n,)
  53. def ShuffleFlag():
  54. return '--gtest_shuffle'
  55. def RandomSeedFlag(n):
  56. return '--gtest_random_seed=%s' % (n,)
  57. def RunAndReturnOutput(extra_env, args):
  58. """Runs the test program and returns its output."""
  59. environ_copy = os.environ.copy()
  60. environ_copy.update(extra_env)
  61. return gtest_test_utils.Subprocess([COMMAND] + args, env=environ_copy).output
  62. def GetTestsForAllIterations(extra_env, args):
  63. """Runs the test program and returns a list of test lists.
  64. Args:
  65. extra_env: a map from environment variables to their values
  66. args: command line flags to pass to googletest-shuffle-test_
  67. Returns:
  68. A list where the i-th element is the list of tests run in the i-th
  69. test iteration.
  70. """
  71. test_iterations = []
  72. for line in RunAndReturnOutput(extra_env, args).split('\n'):
  73. if line.startswith('----'):
  74. tests = []
  75. test_iterations.append(tests)
  76. elif line.strip():
  77. tests.append(line.strip()) # 'TestCaseName.TestName'
  78. return test_iterations
  79. def GetTestCases(tests):
  80. """Returns a list of test cases in the given full test names.
  81. Args:
  82. tests: a list of full test names
  83. Returns:
  84. A list of test cases from 'tests', in their original order.
  85. Consecutive duplicates are removed.
  86. """
  87. test_cases = []
  88. for test in tests:
  89. test_case = test.split('.')[0]
  90. if not test_case in test_cases:
  91. test_cases.append(test_case)
  92. return test_cases
  93. def CalculateTestLists():
  94. """Calculates the list of tests run under different flags."""
  95. if not ALL_TESTS:
  96. ALL_TESTS.extend(
  97. GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0])
  98. if not ACTIVE_TESTS:
  99. ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0])
  100. if not FILTERED_TESTS:
  101. FILTERED_TESTS.extend(
  102. GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0])
  103. if not SHARDED_TESTS:
  104. SHARDED_TESTS.extend(
  105. GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
  106. SHARD_INDEX_ENV_VAR: '1'},
  107. [])[0])
  108. if not SHUFFLED_ALL_TESTS:
  109. SHUFFLED_ALL_TESTS.extend(GetTestsForAllIterations(
  110. {}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)])[0])
  111. if not SHUFFLED_ACTIVE_TESTS:
  112. SHUFFLED_ACTIVE_TESTS.extend(GetTestsForAllIterations(
  113. {}, [ShuffleFlag(), RandomSeedFlag(1)])[0])
  114. if not SHUFFLED_FILTERED_TESTS:
  115. SHUFFLED_FILTERED_TESTS.extend(GetTestsForAllIterations(
  116. {}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)])[0])
  117. if not SHUFFLED_SHARDED_TESTS:
  118. SHUFFLED_SHARDED_TESTS.extend(
  119. GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
  120. SHARD_INDEX_ENV_VAR: '1'},
  121. [ShuffleFlag(), RandomSeedFlag(1)])[0])
  122. class GTestShuffleUnitTest(gtest_test_utils.TestCase):
  123. """Tests test shuffling."""
  124. def setUp(self):
  125. CalculateTestLists()
  126. def testShufflePreservesNumberOfTests(self):
  127. self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS))
  128. self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS))
  129. self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS))
  130. self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS))
  131. def testShuffleChangesTestOrder(self):
  132. self.assert_(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS)
  133. self.assert_(SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS)
  134. self.assert_(SHUFFLED_FILTERED_TESTS != FILTERED_TESTS,
  135. SHUFFLED_FILTERED_TESTS)
  136. self.assert_(SHUFFLED_SHARDED_TESTS != SHARDED_TESTS,
  137. SHUFFLED_SHARDED_TESTS)
  138. def testShuffleChangesTestCaseOrder(self):
  139. self.assert_(GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS),
  140. GetTestCases(SHUFFLED_ALL_TESTS))
  141. self.assert_(
  142. GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS),
  143. GetTestCases(SHUFFLED_ACTIVE_TESTS))
  144. self.assert_(
  145. GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS),
  146. GetTestCases(SHUFFLED_FILTERED_TESTS))
  147. self.assert_(
  148. GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS),
  149. GetTestCases(SHUFFLED_SHARDED_TESTS))
  150. def testShuffleDoesNotRepeatTest(self):
  151. for test in SHUFFLED_ALL_TESTS:
  152. self.assertEqual(1, SHUFFLED_ALL_TESTS.count(test),
  153. '%s appears more than once' % (test,))
  154. for test in SHUFFLED_ACTIVE_TESTS:
  155. self.assertEqual(1, SHUFFLED_ACTIVE_TESTS.count(test),
  156. '%s appears more than once' % (test,))
  157. for test in SHUFFLED_FILTERED_TESTS:
  158. self.assertEqual(1, SHUFFLED_FILTERED_TESTS.count(test),
  159. '%s appears more than once' % (test,))
  160. for test in SHUFFLED_SHARDED_TESTS:
  161. self.assertEqual(1, SHUFFLED_SHARDED_TESTS.count(test),
  162. '%s appears more than once' % (test,))
  163. def testShuffleDoesNotCreateNewTest(self):
  164. for test in SHUFFLED_ALL_TESTS:
  165. self.assert_(test in ALL_TESTS, '%s is an invalid test' % (test,))
  166. for test in SHUFFLED_ACTIVE_TESTS:
  167. self.assert_(test in ACTIVE_TESTS, '%s is an invalid test' % (test,))
  168. for test in SHUFFLED_FILTERED_TESTS:
  169. self.assert_(test in FILTERED_TESTS, '%s is an invalid test' % (test,))
  170. for test in SHUFFLED_SHARDED_TESTS:
  171. self.assert_(test in SHARDED_TESTS, '%s is an invalid test' % (test,))
  172. def testShuffleIncludesAllTests(self):
  173. for test in ALL_TESTS:
  174. self.assert_(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,))
  175. for test in ACTIVE_TESTS:
  176. self.assert_(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,))
  177. for test in FILTERED_TESTS:
  178. self.assert_(test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,))
  179. for test in SHARDED_TESTS:
  180. self.assert_(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,))
  181. def testShuffleLeavesDeathTestsAtFront(self):
  182. non_death_test_found = False
  183. for test in SHUFFLED_ACTIVE_TESTS:
  184. if 'DeathTest.' in test:
  185. self.assert_(not non_death_test_found,
  186. '%s appears after a non-death test' % (test,))
  187. else:
  188. non_death_test_found = True
  189. def _VerifyTestCasesDoNotInterleave(self, tests):
  190. test_cases = []
  191. for test in tests:
  192. [test_case, _] = test.split('.')
  193. if test_cases and test_cases[-1] != test_case:
  194. test_cases.append(test_case)
  195. self.assertEqual(1, test_cases.count(test_case),
  196. 'Test case %s is not grouped together in %s' %
  197. (test_case, tests))
  198. def testShuffleDoesNotInterleaveTestCases(self):
  199. self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS)
  200. self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS)
  201. self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS)
  202. self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS)
  203. def testShuffleRestoresOrderAfterEachIteration(self):
  204. # Get the test lists in all 3 iterations, using random seed 1, 2,
  205. # and 3 respectively. Google Test picks a different seed in each
  206. # iteration, and this test depends on the current implementation
  207. # picking successive numbers. This dependency is not ideal, but
  208. # makes the test much easier to write.
  209. [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
  210. GetTestsForAllIterations(
  211. {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]))
  212. # Make sure running the tests with random seed 1 gets the same
  213. # order as in iteration 1 above.
  214. [tests_with_seed1] = GetTestsForAllIterations(
  215. {}, [ShuffleFlag(), RandomSeedFlag(1)])
  216. self.assertEqual(tests_in_iteration1, tests_with_seed1)
  217. # Make sure running the tests with random seed 2 gets the same
  218. # order as in iteration 2 above. Success means that Google Test
  219. # correctly restores the test order before re-shuffling at the
  220. # beginning of iteration 2.
  221. [tests_with_seed2] = GetTestsForAllIterations(
  222. {}, [ShuffleFlag(), RandomSeedFlag(2)])
  223. self.assertEqual(tests_in_iteration2, tests_with_seed2)
  224. # Make sure running the tests with random seed 3 gets the same
  225. # order as in iteration 3 above. Success means that Google Test
  226. # correctly restores the test order before re-shuffling at the
  227. # beginning of iteration 3.
  228. [tests_with_seed3] = GetTestsForAllIterations(
  229. {}, [ShuffleFlag(), RandomSeedFlag(3)])
  230. self.assertEqual(tests_in_iteration3, tests_with_seed3)
  231. def testShuffleGeneratesNewOrderInEachIteration(self):
  232. [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
  233. GetTestsForAllIterations(
  234. {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]))
  235. self.assert_(tests_in_iteration1 != tests_in_iteration2,
  236. tests_in_iteration1)
  237. self.assert_(tests_in_iteration1 != tests_in_iteration3,
  238. tests_in_iteration1)
  239. self.assert_(tests_in_iteration2 != tests_in_iteration3,
  240. tests_in_iteration2)
  241. def testShuffleShardedTestsPreservesPartition(self):
  242. # If we run M tests on N shards, the same M tests should be run in
  243. # total, regardless of the random seeds used by the shards.
  244. [tests1] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
  245. SHARD_INDEX_ENV_VAR: '0'},
  246. [ShuffleFlag(), RandomSeedFlag(1)])
  247. [tests2] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
  248. SHARD_INDEX_ENV_VAR: '1'},
  249. [ShuffleFlag(), RandomSeedFlag(20)])
  250. [tests3] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
  251. SHARD_INDEX_ENV_VAR: '2'},
  252. [ShuffleFlag(), RandomSeedFlag(25)])
  253. sorted_sharded_tests = tests1 + tests2 + tests3
  254. sorted_sharded_tests.sort()
  255. sorted_active_tests = []
  256. sorted_active_tests.extend(ACTIVE_TESTS)
  257. sorted_active_tests.sort()
  258. self.assertEqual(sorted_active_tests, sorted_sharded_tests)
  259. if __name__ == '__main__':
  260. gtest_test_utils.Main()