Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | # |
michael@0 | 3 | # Copyright 2009 Google Inc. All Rights Reserved. |
michael@0 | 4 | # |
michael@0 | 5 | # Redistribution and use in source and binary forms, with or without |
michael@0 | 6 | # modification, are permitted provided that the following conditions are |
michael@0 | 7 | # met: |
michael@0 | 8 | # |
michael@0 | 9 | # * Redistributions of source code must retain the above copyright |
michael@0 | 10 | # notice, this list of conditions and the following disclaimer. |
michael@0 | 11 | # * Redistributions in binary form must reproduce the above |
michael@0 | 12 | # copyright notice, this list of conditions and the following disclaimer |
michael@0 | 13 | # in the documentation and/or other materials provided with the |
michael@0 | 14 | # distribution. |
michael@0 | 15 | # * Neither the name of Google Inc. nor the names of its |
michael@0 | 16 | # contributors may be used to endorse or promote products derived from |
michael@0 | 17 | # this software without specific prior written permission. |
michael@0 | 18 | # |
michael@0 | 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 22 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 23 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 24 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 25 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 26 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 27 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 28 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 30 | |
michael@0 | 31 | """Verifies that test shuffling works.""" |
michael@0 | 32 | |
michael@0 | 33 | __author__ = 'wan@google.com (Zhanyong Wan)' |
michael@0 | 34 | |
michael@0 | 35 | import os |
michael@0 | 36 | import gtest_test_utils |
michael@0 | 37 | |
michael@0 | 38 | # Command to run the gtest_shuffle_test_ program. |
michael@0 | 39 | COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_shuffle_test_') |
michael@0 | 40 | |
michael@0 | 41 | # The environment variables for test sharding. |
michael@0 | 42 | TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS' |
michael@0 | 43 | SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX' |
michael@0 | 44 | |
michael@0 | 45 | TEST_FILTER = 'A*.A:A*.B:C*' |
michael@0 | 46 | |
michael@0 | 47 | ALL_TESTS = [] |
michael@0 | 48 | ACTIVE_TESTS = [] |
michael@0 | 49 | FILTERED_TESTS = [] |
michael@0 | 50 | SHARDED_TESTS = [] |
michael@0 | 51 | |
michael@0 | 52 | SHUFFLED_ALL_TESTS = [] |
michael@0 | 53 | SHUFFLED_ACTIVE_TESTS = [] |
michael@0 | 54 | SHUFFLED_FILTERED_TESTS = [] |
michael@0 | 55 | SHUFFLED_SHARDED_TESTS = [] |
michael@0 | 56 | |
michael@0 | 57 | |
michael@0 | 58 | def AlsoRunDisabledTestsFlag(): |
michael@0 | 59 | return '--gtest_also_run_disabled_tests' |
michael@0 | 60 | |
michael@0 | 61 | |
michael@0 | 62 | def FilterFlag(test_filter): |
michael@0 | 63 | return '--gtest_filter=%s' % (test_filter,) |
michael@0 | 64 | |
michael@0 | 65 | |
michael@0 | 66 | def RepeatFlag(n): |
michael@0 | 67 | return '--gtest_repeat=%s' % (n,) |
michael@0 | 68 | |
michael@0 | 69 | |
michael@0 | 70 | def ShuffleFlag(): |
michael@0 | 71 | return '--gtest_shuffle' |
michael@0 | 72 | |
michael@0 | 73 | |
michael@0 | 74 | def RandomSeedFlag(n): |
michael@0 | 75 | return '--gtest_random_seed=%s' % (n,) |
michael@0 | 76 | |
michael@0 | 77 | |
michael@0 | 78 | def RunAndReturnOutput(extra_env, args): |
michael@0 | 79 | """Runs the test program and returns its output.""" |
michael@0 | 80 | |
michael@0 | 81 | environ_copy = os.environ.copy() |
michael@0 | 82 | environ_copy.update(extra_env) |
michael@0 | 83 | |
michael@0 | 84 | return gtest_test_utils.Subprocess([COMMAND] + args, env=environ_copy, |
michael@0 | 85 | capture_stderr=False).output |
michael@0 | 86 | |
michael@0 | 87 | |
michael@0 | 88 | def GetTestsForAllIterations(extra_env, args): |
michael@0 | 89 | """Runs the test program and returns a list of test lists. |
michael@0 | 90 | |
michael@0 | 91 | Args: |
michael@0 | 92 | extra_env: a map from environment variables to their values |
michael@0 | 93 | args: command line flags to pass to gtest_shuffle_test_ |
michael@0 | 94 | |
michael@0 | 95 | Returns: |
michael@0 | 96 | A list where the i-th element is the list of tests run in the i-th |
michael@0 | 97 | test iteration. |
michael@0 | 98 | """ |
michael@0 | 99 | |
michael@0 | 100 | test_iterations = [] |
michael@0 | 101 | for line in RunAndReturnOutput(extra_env, args).split('\n'): |
michael@0 | 102 | if line.startswith('----'): |
michael@0 | 103 | tests = [] |
michael@0 | 104 | test_iterations.append(tests) |
michael@0 | 105 | elif line.strip(): |
michael@0 | 106 | tests.append(line.strip()) # 'TestCaseName.TestName' |
michael@0 | 107 | |
michael@0 | 108 | return test_iterations |
michael@0 | 109 | |
michael@0 | 110 | |
michael@0 | 111 | def GetTestCases(tests): |
michael@0 | 112 | """Returns a list of test cases in the given full test names. |
michael@0 | 113 | |
michael@0 | 114 | Args: |
michael@0 | 115 | tests: a list of full test names |
michael@0 | 116 | |
michael@0 | 117 | Returns: |
michael@0 | 118 | A list of test cases from 'tests', in their original order. |
michael@0 | 119 | Consecutive duplicates are removed. |
michael@0 | 120 | """ |
michael@0 | 121 | |
michael@0 | 122 | test_cases = [] |
michael@0 | 123 | for test in tests: |
michael@0 | 124 | test_case = test.split('.')[0] |
michael@0 | 125 | if not test_case in test_cases: |
michael@0 | 126 | test_cases.append(test_case) |
michael@0 | 127 | |
michael@0 | 128 | return test_cases |
michael@0 | 129 | |
michael@0 | 130 | |
michael@0 | 131 | def CalculateTestLists(): |
michael@0 | 132 | """Calculates the list of tests run under different flags.""" |
michael@0 | 133 | |
michael@0 | 134 | if not ALL_TESTS: |
michael@0 | 135 | ALL_TESTS.extend( |
michael@0 | 136 | GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0]) |
michael@0 | 137 | |
michael@0 | 138 | if not ACTIVE_TESTS: |
michael@0 | 139 | ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0]) |
michael@0 | 140 | |
michael@0 | 141 | if not FILTERED_TESTS: |
michael@0 | 142 | FILTERED_TESTS.extend( |
michael@0 | 143 | GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0]) |
michael@0 | 144 | |
michael@0 | 145 | if not SHARDED_TESTS: |
michael@0 | 146 | SHARDED_TESTS.extend( |
michael@0 | 147 | GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', |
michael@0 | 148 | SHARD_INDEX_ENV_VAR: '1'}, |
michael@0 | 149 | [])[0]) |
michael@0 | 150 | |
michael@0 | 151 | if not SHUFFLED_ALL_TESTS: |
michael@0 | 152 | SHUFFLED_ALL_TESTS.extend(GetTestsForAllIterations( |
michael@0 | 153 | {}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)])[0]) |
michael@0 | 154 | |
michael@0 | 155 | if not SHUFFLED_ACTIVE_TESTS: |
michael@0 | 156 | SHUFFLED_ACTIVE_TESTS.extend(GetTestsForAllIterations( |
michael@0 | 157 | {}, [ShuffleFlag(), RandomSeedFlag(1)])[0]) |
michael@0 | 158 | |
michael@0 | 159 | if not SHUFFLED_FILTERED_TESTS: |
michael@0 | 160 | SHUFFLED_FILTERED_TESTS.extend(GetTestsForAllIterations( |
michael@0 | 161 | {}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)])[0]) |
michael@0 | 162 | |
michael@0 | 163 | if not SHUFFLED_SHARDED_TESTS: |
michael@0 | 164 | SHUFFLED_SHARDED_TESTS.extend( |
michael@0 | 165 | GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', |
michael@0 | 166 | SHARD_INDEX_ENV_VAR: '1'}, |
michael@0 | 167 | [ShuffleFlag(), RandomSeedFlag(1)])[0]) |
michael@0 | 168 | |
michael@0 | 169 | |
michael@0 | 170 | class GTestShuffleUnitTest(gtest_test_utils.TestCase): |
michael@0 | 171 | """Tests test shuffling.""" |
michael@0 | 172 | |
michael@0 | 173 | def setUp(self): |
michael@0 | 174 | CalculateTestLists() |
michael@0 | 175 | |
michael@0 | 176 | def testShufflePreservesNumberOfTests(self): |
michael@0 | 177 | self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS)) |
michael@0 | 178 | self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS)) |
michael@0 | 179 | self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS)) |
michael@0 | 180 | self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS)) |
michael@0 | 181 | |
michael@0 | 182 | def testShuffleChangesTestOrder(self): |
michael@0 | 183 | self.assert_(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS) |
michael@0 | 184 | self.assert_(SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS) |
michael@0 | 185 | self.assert_(SHUFFLED_FILTERED_TESTS != FILTERED_TESTS, |
michael@0 | 186 | SHUFFLED_FILTERED_TESTS) |
michael@0 | 187 | self.assert_(SHUFFLED_SHARDED_TESTS != SHARDED_TESTS, |
michael@0 | 188 | SHUFFLED_SHARDED_TESTS) |
michael@0 | 189 | |
michael@0 | 190 | def testShuffleChangesTestCaseOrder(self): |
michael@0 | 191 | self.assert_(GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS), |
michael@0 | 192 | GetTestCases(SHUFFLED_ALL_TESTS)) |
michael@0 | 193 | self.assert_( |
michael@0 | 194 | GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS), |
michael@0 | 195 | GetTestCases(SHUFFLED_ACTIVE_TESTS)) |
michael@0 | 196 | self.assert_( |
michael@0 | 197 | GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS), |
michael@0 | 198 | GetTestCases(SHUFFLED_FILTERED_TESTS)) |
michael@0 | 199 | self.assert_( |
michael@0 | 200 | GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS), |
michael@0 | 201 | GetTestCases(SHUFFLED_SHARDED_TESTS)) |
michael@0 | 202 | |
michael@0 | 203 | def testShuffleDoesNotRepeatTest(self): |
michael@0 | 204 | for test in SHUFFLED_ALL_TESTS: |
michael@0 | 205 | self.assertEqual(1, SHUFFLED_ALL_TESTS.count(test), |
michael@0 | 206 | '%s appears more than once' % (test,)) |
michael@0 | 207 | for test in SHUFFLED_ACTIVE_TESTS: |
michael@0 | 208 | self.assertEqual(1, SHUFFLED_ACTIVE_TESTS.count(test), |
michael@0 | 209 | '%s appears more than once' % (test,)) |
michael@0 | 210 | for test in SHUFFLED_FILTERED_TESTS: |
michael@0 | 211 | self.assertEqual(1, SHUFFLED_FILTERED_TESTS.count(test), |
michael@0 | 212 | '%s appears more than once' % (test,)) |
michael@0 | 213 | for test in SHUFFLED_SHARDED_TESTS: |
michael@0 | 214 | self.assertEqual(1, SHUFFLED_SHARDED_TESTS.count(test), |
michael@0 | 215 | '%s appears more than once' % (test,)) |
michael@0 | 216 | |
michael@0 | 217 | def testShuffleDoesNotCreateNewTest(self): |
michael@0 | 218 | for test in SHUFFLED_ALL_TESTS: |
michael@0 | 219 | self.assert_(test in ALL_TESTS, '%s is an invalid test' % (test,)) |
michael@0 | 220 | for test in SHUFFLED_ACTIVE_TESTS: |
michael@0 | 221 | self.assert_(test in ACTIVE_TESTS, '%s is an invalid test' % (test,)) |
michael@0 | 222 | for test in SHUFFLED_FILTERED_TESTS: |
michael@0 | 223 | self.assert_(test in FILTERED_TESTS, '%s is an invalid test' % (test,)) |
michael@0 | 224 | for test in SHUFFLED_SHARDED_TESTS: |
michael@0 | 225 | self.assert_(test in SHARDED_TESTS, '%s is an invalid test' % (test,)) |
michael@0 | 226 | |
michael@0 | 227 | def testShuffleIncludesAllTests(self): |
michael@0 | 228 | for test in ALL_TESTS: |
michael@0 | 229 | self.assert_(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,)) |
michael@0 | 230 | for test in ACTIVE_TESTS: |
michael@0 | 231 | self.assert_(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,)) |
michael@0 | 232 | for test in FILTERED_TESTS: |
michael@0 | 233 | self.assert_(test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,)) |
michael@0 | 234 | for test in SHARDED_TESTS: |
michael@0 | 235 | self.assert_(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,)) |
michael@0 | 236 | |
michael@0 | 237 | def testShuffleLeavesDeathTestsAtFront(self): |
michael@0 | 238 | non_death_test_found = False |
michael@0 | 239 | for test in SHUFFLED_ACTIVE_TESTS: |
michael@0 | 240 | if 'DeathTest.' in test: |
michael@0 | 241 | self.assert_(not non_death_test_found, |
michael@0 | 242 | '%s appears after a non-death test' % (test,)) |
michael@0 | 243 | else: |
michael@0 | 244 | non_death_test_found = True |
michael@0 | 245 | |
michael@0 | 246 | def _VerifyTestCasesDoNotInterleave(self, tests): |
michael@0 | 247 | test_cases = [] |
michael@0 | 248 | for test in tests: |
michael@0 | 249 | [test_case, _] = test.split('.') |
michael@0 | 250 | if test_cases and test_cases[-1] != test_case: |
michael@0 | 251 | test_cases.append(test_case) |
michael@0 | 252 | self.assertEqual(1, test_cases.count(test_case), |
michael@0 | 253 | 'Test case %s is not grouped together in %s' % |
michael@0 | 254 | (test_case, tests)) |
michael@0 | 255 | |
michael@0 | 256 | def testShuffleDoesNotInterleaveTestCases(self): |
michael@0 | 257 | self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS) |
michael@0 | 258 | self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS) |
michael@0 | 259 | self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS) |
michael@0 | 260 | self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS) |
michael@0 | 261 | |
michael@0 | 262 | def testShuffleRestoresOrderAfterEachIteration(self): |
michael@0 | 263 | # Get the test lists in all 3 iterations, using random seed 1, 2, |
michael@0 | 264 | # and 3 respectively. Google Test picks a different seed in each |
michael@0 | 265 | # iteration, and this test depends on the current implementation |
michael@0 | 266 | # picking successive numbers. This dependency is not ideal, but |
michael@0 | 267 | # makes the test much easier to write. |
michael@0 | 268 | [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( |
michael@0 | 269 | GetTestsForAllIterations( |
michael@0 | 270 | {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) |
michael@0 | 271 | |
michael@0 | 272 | # Make sure running the tests with random seed 1 gets the same |
michael@0 | 273 | # order as in iteration 1 above. |
michael@0 | 274 | [tests_with_seed1] = GetTestsForAllIterations( |
michael@0 | 275 | {}, [ShuffleFlag(), RandomSeedFlag(1)]) |
michael@0 | 276 | self.assertEqual(tests_in_iteration1, tests_with_seed1) |
michael@0 | 277 | |
michael@0 | 278 | # Make sure running the tests with random seed 2 gets the same |
michael@0 | 279 | # order as in iteration 2 above. Success means that Google Test |
michael@0 | 280 | # correctly restores the test order before re-shuffling at the |
michael@0 | 281 | # beginning of iteration 2. |
michael@0 | 282 | [tests_with_seed2] = GetTestsForAllIterations( |
michael@0 | 283 | {}, [ShuffleFlag(), RandomSeedFlag(2)]) |
michael@0 | 284 | self.assertEqual(tests_in_iteration2, tests_with_seed2) |
michael@0 | 285 | |
michael@0 | 286 | # Make sure running the tests with random seed 3 gets the same |
michael@0 | 287 | # order as in iteration 3 above. Success means that Google Test |
michael@0 | 288 | # correctly restores the test order before re-shuffling at the |
michael@0 | 289 | # beginning of iteration 3. |
michael@0 | 290 | [tests_with_seed3] = GetTestsForAllIterations( |
michael@0 | 291 | {}, [ShuffleFlag(), RandomSeedFlag(3)]) |
michael@0 | 292 | self.assertEqual(tests_in_iteration3, tests_with_seed3) |
michael@0 | 293 | |
michael@0 | 294 | def testShuffleGeneratesNewOrderInEachIteration(self): |
michael@0 | 295 | [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( |
michael@0 | 296 | GetTestsForAllIterations( |
michael@0 | 297 | {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) |
michael@0 | 298 | |
michael@0 | 299 | self.assert_(tests_in_iteration1 != tests_in_iteration2, |
michael@0 | 300 | tests_in_iteration1) |
michael@0 | 301 | self.assert_(tests_in_iteration1 != tests_in_iteration3, |
michael@0 | 302 | tests_in_iteration1) |
michael@0 | 303 | self.assert_(tests_in_iteration2 != tests_in_iteration3, |
michael@0 | 304 | tests_in_iteration2) |
michael@0 | 305 | |
michael@0 | 306 | def testShuffleShardedTestsPreservesPartition(self): |
michael@0 | 307 | # If we run M tests on N shards, the same M tests should be run in |
michael@0 | 308 | # total, regardless of the random seeds used by the shards. |
michael@0 | 309 | [tests1] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', |
michael@0 | 310 | SHARD_INDEX_ENV_VAR: '0'}, |
michael@0 | 311 | [ShuffleFlag(), RandomSeedFlag(1)]) |
michael@0 | 312 | [tests2] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', |
michael@0 | 313 | SHARD_INDEX_ENV_VAR: '1'}, |
michael@0 | 314 | [ShuffleFlag(), RandomSeedFlag(20)]) |
michael@0 | 315 | [tests3] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', |
michael@0 | 316 | SHARD_INDEX_ENV_VAR: '2'}, |
michael@0 | 317 | [ShuffleFlag(), RandomSeedFlag(25)]) |
michael@0 | 318 | sorted_sharded_tests = tests1 + tests2 + tests3 |
michael@0 | 319 | sorted_sharded_tests.sort() |
michael@0 | 320 | sorted_active_tests = [] |
michael@0 | 321 | sorted_active_tests.extend(ACTIVE_TESTS) |
michael@0 | 322 | sorted_active_tests.sort() |
michael@0 | 323 | self.assertEqual(sorted_active_tests, sorted_sharded_tests) |
michael@0 | 324 | |
michael@0 | 325 | if __name__ == '__main__': |
michael@0 | 326 | gtest_test_utils.Main() |