Home | History | Annotate | Download | only in test
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2005, Google Inc.
      4 # All rights reserved.
      5 #
      6 # Redistribution and use in source and binary forms, with or without
      7 # modification, are permitted provided that the following conditions are
      8 # met:
      9 #
     10 #     * Redistributions of source code must retain the above copyright
     11 # notice, this list of conditions and the following disclaimer.
     12 #     * Redistributions in binary form must reproduce the above
     13 # copyright notice, this list of conditions and the following disclaimer
     14 # in the documentation and/or other materials provided with the
     15 # distribution.
     16 #     * Neither the name of Google Inc. nor the names of its
     17 # contributors may be used to endorse or promote products derived from
     18 # this software without specific prior written permission.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 
     32 """Unit test for Google Test test filters.
     33 
     34 A user can specify which test(s) in a Google Test program to run via either
     35 the GTEST_FILTER environment variable or the --gtest_filter flag.
     36 This script tests such functionality by invoking
     37 gtest_filter_unittest_ (a program written with Google Test) with different
     38 environments and command line flags.
     39 
     40 Note that test sharding may also influence which tests are filtered. Therefore,
     41 we test that here also.
     42 """
     43 
     44 __author__ = 'wan (at] google.com (Zhanyong Wan)'
     45 
     46 import os
     47 import re
     48 import sets
     49 import tempfile
     50 import unittest
     51 import gtest_test_utils
     52 
     53 # Constants.
     54 
     55 # The environment variable for specifying the test filters.
     56 FILTER_ENV_VAR = 'GTEST_FILTER'
     57 
     58 # The environment variables for test sharding.
     59 TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
     60 SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
     61 SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE'
     62 
     63 # The command line flag for specifying the test filters.
     64 FILTER_FLAG = 'gtest_filter'
     65 
     66 # The command line flag for including disabled tests.
     67 ALSO_RUN_DISABED_TESTS_FLAG = 'gtest_also_run_disabled_tests'
     68 
     69 # Command to run the gtest_filter_unittest_ program.
     70 COMMAND = os.path.join(gtest_test_utils.GetBuildDir(),
     71                        'gtest_filter_unittest_')
     72 
     73 # Regex for determining whether parameterized tests are enabled in the binary.
     74 PARAM_TEST_REGEX = re.compile(r'/ParamTest')
     75 
     76 # Regex for parsing test case names from Google Test's output.
     77 TEST_CASE_REGEX = re.compile(r'^\[\-+\] \d+ tests? from (\w+(/\w+)?)')
     78 
     79 # Regex for parsing test names from Google Test's output.
     80 TEST_REGEX = re.compile(r'^\[\s*RUN\s*\].*\.(\w+(/\w+)?)')
     81 
     82 # Full names of all tests in gtest_filter_unittests_.
     83 PARAM_TESTS = [
     84     'SeqP/ParamTest.TestX/0',
     85     'SeqP/ParamTest.TestX/1',
     86     'SeqP/ParamTest.TestY/0',
     87     'SeqP/ParamTest.TestY/1',
     88     'SeqQ/ParamTest.TestX/0',
     89     'SeqQ/ParamTest.TestX/1',
     90     'SeqQ/ParamTest.TestY/0',
     91     'SeqQ/ParamTest.TestY/1',
     92     ]
     93 
     94 DISABLED_TESTS = [
     95     'BarTest.DISABLED_TestFour',
     96     'BarTest.DISABLED_TestFive',
     97     'BazTest.DISABLED_TestC',
     98     'DISABLED_FoobarTest.Test1',
     99     'DISABLED_FoobarTest.DISABLED_Test2',
    100     'DISABLED_FoobarbazTest.TestA',
    101     ]
    102 
    103 # All the non-disabled tests.
    104 ACTIVE_TESTS = [
    105     'FooTest.Abc',
    106     'FooTest.Xyz',
    107 
    108     'BarTest.TestOne',
    109     'BarTest.TestTwo',
    110     'BarTest.TestThree',
    111 
    112     'BazTest.TestOne',
    113     'BazTest.TestA',
    114     'BazTest.TestB',
    115 
    116     'HasDeathTest.Test1',
    117     'HasDeathTest.Test2',
    118     ] + PARAM_TESTS
    119 
    120 param_tests_present = None
    121 
    122 # Utilities.
    123 
    124 
    125 def SetEnvVar(env_var, value):
    126   """Sets the env variable to 'value'; unsets it when 'value' is None."""
    127 
    128   if value is not None:
    129     os.environ[env_var] = value
    130   elif env_var in os.environ:
    131     del os.environ[env_var]
    132 
    133 
    134 def Run(command):
    135   """Runs a Google Test program and returns a list of full names of the
    136   tests that were run along with the test exit code.
    137   """
    138 
    139   stdout_file = os.popen(command, 'r')
    140   tests_run = []
    141   test_case = ''
    142   test = ''
    143   for line in stdout_file:
    144     match = TEST_CASE_REGEX.match(line)
    145     if match is not None:
    146       test_case = match.group(1)
    147     else:
    148       match = TEST_REGEX.match(line)
    149       if match is not None:
    150         test = match.group(1)
    151         tests_run += [test_case + '.' + test]
    152   exit_code = stdout_file.close()
    153   return (tests_run, exit_code)
    154 
    155 
    156 def InvokeWithModifiedEnv(extra_env, function, *args, **kwargs):
    157   """Runs the given function and arguments in a modified environment."""
    158   try:
    159     original_env = os.environ.copy()
    160     os.environ.update(extra_env)
    161     return function(*args, **kwargs)
    162   finally:
    163     for key in extra_env.iterkeys():
    164       if key in original_env:
    165         os.environ[key] = original_env[key]
    166       else:
    167         del os.environ[key]
    168 
    169 
    170 def RunWithSharding(total_shards, shard_index, command):
    171   """Runs the Google Test program shard and returns a list of full names of the
    172   tests that were run along with the exit code.
    173   """
    174 
    175   extra_env = {SHARD_INDEX_ENV_VAR: str(shard_index),
    176                TOTAL_SHARDS_ENV_VAR: str(total_shards)}
    177   return InvokeWithModifiedEnv(extra_env, Run, command)
    178 
    179 # The unit test.
    180 
    181 
    182 class GTestFilterUnitTest(unittest.TestCase):
    183   """Tests using the GTEST_FILTER environment variable or the
    184   --gtest_filter flag to filter tests.
    185   """
    186 
    187   # Utilities.
    188 
    189   def AssertSetEqual(self, lhs, rhs):
    190     """Asserts that two sets are equal."""
    191 
    192     for elem in lhs:
    193       self.assert_(elem in rhs, '%s in %s' % (elem, rhs))
    194 
    195     for elem in rhs:
    196       self.assert_(elem in lhs, '%s in %s' % (elem, lhs))
    197 
    198   def AssertPartitionIsValid(self, set_var, list_of_sets):
    199     """Asserts that list_of_sets is a valid partition of set_var."""
    200 
    201     full_partition = []
    202     for slice_var in list_of_sets:
    203       full_partition.extend(slice_var)
    204     self.assertEqual(len(set_var), len(full_partition))
    205     self.assertEqual(sets.Set(set_var), sets.Set(full_partition))
    206 
    207   def RunAndVerify(self, gtest_filter, tests_to_run):
    208     """Runs gtest_flag_unittest_ with the given filter, and verifies
    209     that the right set of tests were run.
    210     """
    211     # Adjust tests_to_run in case value parameterized tests are disabled
    212     # in the binary.
    213     global param_tests_present
    214     if not param_tests_present:
    215       tests_to_run = list(sets.Set(tests_to_run) - sets.Set(PARAM_TESTS))
    216 
    217     # First, tests using GTEST_FILTER.
    218 
    219     SetEnvVar(FILTER_ENV_VAR, gtest_filter)
    220     tests_run = Run(COMMAND)[0]
    221     SetEnvVar(FILTER_ENV_VAR, None)
    222 
    223     self.AssertSetEqual(tests_run, tests_to_run)
    224 
    225     # Next, tests using --gtest_filter.
    226 
    227     if gtest_filter is None:
    228       command = COMMAND
    229     else:
    230       command = '%s --%s=%s' % (COMMAND, FILTER_FLAG, gtest_filter)
    231 
    232     tests_run = Run(command)[0]
    233     self.AssertSetEqual(tests_run, tests_to_run)
    234 
    235   def RunAndVerifyWithSharding(self, gtest_filter, total_shards, tests_to_run,
    236                                command=COMMAND, check_exit_0=False):
    237     """Runs all shards of gtest_flag_unittest_ with the given filter, and
    238     verifies that the right set of tests were run. The union of tests run
    239     on each shard should be identical to tests_to_run, without duplicates.
    240     If check_exit_0, make sure that all shards returned 0.
    241     """
    242     SetEnvVar(FILTER_ENV_VAR, gtest_filter)
    243     partition = []
    244     for i in range(0, total_shards):
    245       (tests_run, exit_code) = RunWithSharding(total_shards, i, command)
    246       if check_exit_0:
    247         self.assert_(exit_code is None)
    248       partition.append(tests_run)
    249 
    250     self.AssertPartitionIsValid(tests_to_run, partition)
    251     SetEnvVar(FILTER_ENV_VAR, None)
    252 
    253   def RunAndVerifyAllowingDisabled(self, gtest_filter, tests_to_run):
    254     """Runs gtest_flag_unittest_ with the given filter, and enables
    255     disabled tests. Verifies that the right set of tests were run.
    256     """
    257     # Construct the command line.
    258     command = '%s --%s' % (COMMAND, ALSO_RUN_DISABED_TESTS_FLAG)
    259     if gtest_filter is not None:
    260       command = '%s --%s=%s' % (command, FILTER_FLAG, gtest_filter)
    261 
    262     tests_run = Run(command)[0]
    263     self.AssertSetEqual(tests_run, tests_to_run)
    264 
    265   def setUp(self):
    266     """Sets up test case. Determines whether value-parameterized tests are
    267     enabled in the binary and sets flags accordingly.
    268     """
    269     global param_tests_present
    270     if param_tests_present is None:
    271       param_tests_present = PARAM_TEST_REGEX.search(
    272           '\n'.join(os.popen(COMMAND, 'r').readlines())) is not None
    273 
    274   def testDefaultBehavior(self):
    275     """Tests the behavior of not specifying the filter."""
    276 
    277     self.RunAndVerify(None, ACTIVE_TESTS)
    278 
    279   def testDefaultBehaviorWithShards(self):
    280     """Tests the behavior of not specifying the filter, with sharding
    281     enabled.
    282     """
    283     self.RunAndVerifyWithSharding(None, 1, ACTIVE_TESTS)
    284     self.RunAndVerifyWithSharding(None, 2, ACTIVE_TESTS)
    285     self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) - 1, ACTIVE_TESTS)
    286     self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS), ACTIVE_TESTS)
    287     self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) + 1, ACTIVE_TESTS)
    288 
    289   def testEmptyFilter(self):
    290     """Tests an empty filter."""
    291 
    292     self.RunAndVerify('', [])
    293     self.RunAndVerifyWithSharding('', 1, [])
    294     self.RunAndVerifyWithSharding('', 2, [])
    295 
    296   def testBadFilter(self):
    297     """Tests a filter that matches nothing."""
    298 
    299     self.RunAndVerify('BadFilter', [])
    300     self.RunAndVerifyAllowingDisabled('BadFilter', [])
    301 
    302   def testFullName(self):
    303     """Tests filtering by full name."""
    304 
    305     self.RunAndVerify('FooTest.Xyz', ['FooTest.Xyz'])
    306     self.RunAndVerifyAllowingDisabled('FooTest.Xyz', ['FooTest.Xyz'])
    307     self.RunAndVerifyWithSharding('FooTest.Xyz', 5, ['FooTest.Xyz'])
    308 
    309   def testUniversalFilters(self):
    310     """Tests filters that match everything."""
    311 
    312     self.RunAndVerify('*', ACTIVE_TESTS)
    313     self.RunAndVerify('*.*', ACTIVE_TESTS)
    314     self.RunAndVerifyWithSharding('*.*', len(ACTIVE_TESTS) - 3, ACTIVE_TESTS)
    315     self.RunAndVerifyAllowingDisabled('*', ACTIVE_TESTS + DISABLED_TESTS)
    316     self.RunAndVerifyAllowingDisabled('*.*', ACTIVE_TESTS + DISABLED_TESTS)
    317 
    318   def testFilterByTestCase(self):
    319     """Tests filtering by test case name."""
    320 
    321     self.RunAndVerify('FooTest.*', ['FooTest.Abc', 'FooTest.Xyz'])
    322 
    323     BAZ_TESTS = ['BazTest.TestOne', 'BazTest.TestA', 'BazTest.TestB']
    324     self.RunAndVerify('BazTest.*', BAZ_TESTS)
    325     self.RunAndVerifyAllowingDisabled('BazTest.*',
    326                                       BAZ_TESTS + ['BazTest.DISABLED_TestC'])
    327 
    328   def testFilterByTest(self):
    329     """Tests filtering by test name."""
    330 
    331     self.RunAndVerify('*.TestOne', ['BarTest.TestOne', 'BazTest.TestOne'])
    332 
    333   def testFilterDisabledTests(self):
    334     """Select only the disabled tests to run."""
    335 
    336     self.RunAndVerify('DISABLED_FoobarTest.Test1', [])
    337     self.RunAndVerifyAllowingDisabled('DISABLED_FoobarTest.Test1',
    338                                       ['DISABLED_FoobarTest.Test1'])
    339 
    340     self.RunAndVerify('*DISABLED_*', [])
    341     self.RunAndVerifyAllowingDisabled('*DISABLED_*', DISABLED_TESTS)
    342 
    343     self.RunAndVerify('*.DISABLED_*', [])
    344     self.RunAndVerifyAllowingDisabled('*.DISABLED_*', [
    345         'BarTest.DISABLED_TestFour',
    346         'BarTest.DISABLED_TestFive',
    347         'BazTest.DISABLED_TestC',
    348         'DISABLED_FoobarTest.DISABLED_Test2',
    349         ])
    350 
    351     self.RunAndVerify('DISABLED_*', [])
    352     self.RunAndVerifyAllowingDisabled('DISABLED_*', [
    353         'DISABLED_FoobarTest.Test1',
    354         'DISABLED_FoobarTest.DISABLED_Test2',
    355         'DISABLED_FoobarbazTest.TestA',
    356         ])
    357 
    358   def testWildcardInTestCaseName(self):
    359     """Tests using wildcard in the test case name."""
    360 
    361     self.RunAndVerify('*a*.*', [
    362         'BarTest.TestOne',
    363         'BarTest.TestTwo',
    364         'BarTest.TestThree',
    365 
    366         'BazTest.TestOne',
    367         'BazTest.TestA',
    368         'BazTest.TestB',
    369 
    370         'HasDeathTest.Test1',
    371         'HasDeathTest.Test2', ] + PARAM_TESTS)
    372 
    373   def testWildcardInTestName(self):
    374     """Tests using wildcard in the test name."""
    375 
    376     self.RunAndVerify('*.*A*', ['FooTest.Abc', 'BazTest.TestA'])
    377 
    378   def testFilterWithoutDot(self):
    379     """Tests a filter that has no '.' in it."""
    380 
    381     self.RunAndVerify('*z*', [
    382         'FooTest.Xyz',
    383 
    384         'BazTest.TestOne',
    385         'BazTest.TestA',
    386         'BazTest.TestB',
    387         ])
    388 
    389   def testTwoPatterns(self):
    390     """Tests filters that consist of two patterns."""
    391 
    392     self.RunAndVerify('Foo*.*:*A*', [
    393         'FooTest.Abc',
    394         'FooTest.Xyz',
    395 
    396         'BazTest.TestA',
    397         ])
    398 
    399     # An empty pattern + a non-empty one
    400     self.RunAndVerify(':*A*', ['FooTest.Abc', 'BazTest.TestA'])
    401 
    402   def testThreePatterns(self):
    403     """Tests filters that consist of three patterns."""
    404 
    405     self.RunAndVerify('*oo*:*A*:*One', [
    406         'FooTest.Abc',
    407         'FooTest.Xyz',
    408 
    409         'BarTest.TestOne',
    410 
    411         'BazTest.TestOne',
    412         'BazTest.TestA',
    413         ])
    414 
    415     # The 2nd pattern is empty.
    416     self.RunAndVerify('*oo*::*One', [
    417         'FooTest.Abc',
    418         'FooTest.Xyz',
    419 
    420         'BarTest.TestOne',
    421 
    422         'BazTest.TestOne',
    423         ])
    424 
    425     # The last 2 patterns are empty.
    426     self.RunAndVerify('*oo*::', [
    427         'FooTest.Abc',
    428         'FooTest.Xyz',
    429         ])
    430 
    431   def testNegativeFilters(self):
    432     self.RunAndVerify('*-HasDeathTest.Test1', [
    433         'FooTest.Abc',
    434         'FooTest.Xyz',
    435 
    436         'BarTest.TestOne',
    437         'BarTest.TestTwo',
    438         'BarTest.TestThree',
    439 
    440         'BazTest.TestOne',
    441         'BazTest.TestA',
    442         'BazTest.TestB',
    443 
    444         'HasDeathTest.Test2',
    445         ] + PARAM_TESTS)
    446 
    447     self.RunAndVerify('*-FooTest.Abc:HasDeathTest.*', [
    448         'FooTest.Xyz',
    449 
    450         'BarTest.TestOne',
    451         'BarTest.TestTwo',
    452         'BarTest.TestThree',
    453 
    454         'BazTest.TestOne',
    455         'BazTest.TestA',
    456         'BazTest.TestB',
    457         ] + PARAM_TESTS)
    458 
    459     self.RunAndVerify('BarTest.*-BarTest.TestOne', [
    460         'BarTest.TestTwo',
    461         'BarTest.TestThree',
    462         ])
    463 
    464     # Tests without leading '*'.
    465     self.RunAndVerify('-FooTest.Abc:FooTest.Xyz:HasDeathTest.*', [
    466         'BarTest.TestOne',
    467         'BarTest.TestTwo',
    468         'BarTest.TestThree',
    469 
    470         'BazTest.TestOne',
    471         'BazTest.TestA',
    472         'BazTest.TestB',
    473         ] + PARAM_TESTS)
    474 
    475     # Value parameterized tests.
    476     self.RunAndVerify('*/*', PARAM_TESTS)
    477 
    478     # Value parameterized tests filtering by the sequence name.
    479     self.RunAndVerify('SeqP/*', [
    480         'SeqP/ParamTest.TestX/0',
    481         'SeqP/ParamTest.TestX/1',
    482         'SeqP/ParamTest.TestY/0',
    483         'SeqP/ParamTest.TestY/1',
    484         ])
    485 
    486     # Value parameterized tests filtering by the test name.
    487     self.RunAndVerify('*/0', [
    488         'SeqP/ParamTest.TestX/0',
    489         'SeqP/ParamTest.TestY/0',
    490         'SeqQ/ParamTest.TestX/0',
    491         'SeqQ/ParamTest.TestY/0',
    492         ])
    493 
    494   def testFlagOverridesEnvVar(self):
    495     """Tests that the --gtest_filter flag overrides the GTEST_FILTER
    496     environment variable.
    497     """
    498 
    499     SetEnvVar(FILTER_ENV_VAR, 'Foo*')
    500     command = '%s --%s=%s' % (COMMAND, FILTER_FLAG, '*One')
    501     tests_run = Run(command)[0]
    502     SetEnvVar(FILTER_ENV_VAR, None)
    503 
    504     self.AssertSetEqual(tests_run, ['BarTest.TestOne', 'BazTest.TestOne'])
    505 
    506   def testShardStatusFileIsCreated(self):
    507     """Tests that the shard file is created if specified in the environment."""
    508 
    509     test_tmpdir = tempfile.mkdtemp()
    510     shard_status_file = os.path.join(test_tmpdir, 'shard_status_file')
    511     self.assert_(not os.path.exists(shard_status_file))
    512 
    513     extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file}
    514     stdout_file = InvokeWithModifiedEnv(extra_env, os.popen, COMMAND, 'r')
    515     try:
    516       stdout_file.readlines()
    517     finally:
    518       stdout_file.close()
    519       self.assert_(os.path.exists(shard_status_file))
    520       os.remove(shard_status_file)
    521       os.removedirs(test_tmpdir)
    522 
    523   def testShardStatusFileIsCreatedWithListTests(self):
    524     """Tests that the shard file is created with --gtest_list_tests."""
    525 
    526     test_tmpdir = tempfile.mkdtemp()
    527     shard_status_file = os.path.join(test_tmpdir, 'shard_status_file2')
    528     self.assert_(not os.path.exists(shard_status_file))
    529 
    530     extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file}
    531     stdout_file = InvokeWithModifiedEnv(extra_env, os.popen,
    532                                         '%s --gtest_list_tests' % COMMAND, 'r')
    533     try:
    534       stdout_file.readlines()
    535     finally:
    536       stdout_file.close()
    537       self.assert_(os.path.exists(shard_status_file))
    538       os.remove(shard_status_file)
    539       os.removedirs(test_tmpdir)
    540 
    541   def testShardingWorksWithDeathTests(self):
    542     """Tests integration with death tests and sharding."""
    543     gtest_filter = 'HasDeathTest.*:SeqP/*'
    544     expected_tests = [
    545         'HasDeathTest.Test1',
    546         'HasDeathTest.Test2',
    547 
    548         'SeqP/ParamTest.TestX/0',
    549         'SeqP/ParamTest.TestX/1',
    550         'SeqP/ParamTest.TestY/0',
    551         'SeqP/ParamTest.TestY/1',
    552         ]
    553 
    554     for command in (COMMAND + ' --gtest_death_test_style=threadsafe',
    555                     COMMAND + ' --gtest_death_test_style=fast'):
    556       self.RunAndVerifyWithSharding(gtest_filter, 3, expected_tests,
    557                                     check_exit_0=True, command=command)
    558       self.RunAndVerifyWithSharding(gtest_filter, 5, expected_tests,
    559                                     check_exit_0=True, command=command)
    560 
    561 if __name__ == '__main__':
    562   gtest_test_utils.Main()
    563