#!/usr/bin/env python # # Copyright 2020 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. """Unit test for Google Test fail_fast. A user can specify if a Google Test program should continue test execution after a test failure via the GTEST_FAIL_FAST environment variable or the --gtest_fail_fast flag. The default value of the flag can also be changed by Bazel fail fast environment variable TESTBRIDGE_TEST_RUNNER_FAIL_FAST. This script tests such functionality by invoking googletest-failfast-unittest_ (a program written with Google Test) with different environments and command line flags. """ import os import gtest_test_utils # Constants. # Bazel testbridge environment variable for fail fast BAZEL_FAIL_FAST_ENV_VAR = 'TESTBRIDGE_TEST_RUNNER_FAIL_FAST' # The environment variable for specifying fail fast. FAIL_FAST_ENV_VAR = 'GTEST_FAIL_FAST' # The command line flag for specifying fail fast. FAIL_FAST_FLAG = 'gtest_fail_fast' # The command line flag to run disabled tests. RUN_DISABLED_FLAG = 'gtest_also_run_disabled_tests' # The command line flag for specifying a filter. FILTER_FLAG = 'gtest_filter' # Command to run the googletest-failfast-unittest_ program. COMMAND = gtest_test_utils.GetTestExecutablePath( 'googletest-failfast-unittest_') # The command line flag to tell Google Test to output the list of tests it # will run. LIST_TESTS_FLAG = '--gtest_list_tests' # Indicates whether Google Test supports death tests. SUPPORTS_DEATH_TESTS = 'HasDeathTest' in gtest_test_utils.Subprocess( [COMMAND, LIST_TESTS_FLAG]).output # Utilities. environ = os.environ.copy() def SetEnvVar(env_var, value): """Sets the env variable to 'value'; unsets it when 'value' is None.""" if value is not None: environ[env_var] = value elif env_var in environ: del environ[env_var] def RunAndReturnOutput(test_suite=None, fail_fast=None, run_disabled=False): """Runs the test program and returns its output.""" args = [] xml_path = os.path.join(gtest_test_utils.GetTempDir(), '.GTestFailFastUnitTest.xml') args += ['--gtest_output=xml:' + xml_path] if fail_fast is not None: if isinstance(fail_fast, str): args += ['--%s=%s' % (FAIL_FAST_FLAG, fail_fast)] elif fail_fast: args += ['--%s' % FAIL_FAST_FLAG] else: args += ['--no%s' % FAIL_FAST_FLAG] if test_suite: args += ['--%s=%s.*' % (FILTER_FLAG, test_suite)] if run_disabled: args += ['--%s' % RUN_DISABLED_FLAG] txt_out = gtest_test_utils.Subprocess([COMMAND] + args, env=environ).output with open(xml_path) as xml_file: return txt_out, xml_file.read() # The unit test. class GTestFailFastUnitTest(gtest_test_utils.TestCase): """Tests the env variable or the command line flag for fail_fast.""" def testDefaultBehavior(self): """Tests the behavior of not specifying the fail_fast.""" txt, _ = RunAndReturnOutput() self.assertIn('22 FAILED TEST', txt) def testGoogletestFlag(self): txt, _ = RunAndReturnOutput(test_suite='HasSimpleTest', fail_fast=True) self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) txt, _ = RunAndReturnOutput(test_suite='HasSimpleTest', fail_fast=False) self.assertIn('4 FAILED TEST', txt) self.assertNotIn('[ SKIPPED ]', txt) def testGoogletestEnvVar(self): """Tests the behavior of specifying fail_fast via Googletest env var.""" try: SetEnvVar(FAIL_FAST_ENV_VAR, '1') txt, _ = RunAndReturnOutput('HasSimpleTest') self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) SetEnvVar(FAIL_FAST_ENV_VAR, '0') txt, _ = RunAndReturnOutput('HasSimpleTest') self.assertIn('4 FAILED TEST', txt) self.assertNotIn('[ SKIPPED ]', txt) finally: SetEnvVar(FAIL_FAST_ENV_VAR, None) def testBazelEnvVar(self): """Tests the behavior of specifying fail_fast via Bazel testbridge.""" try: SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '1') txt, _ = RunAndReturnOutput('HasSimpleTest') self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '0') txt, _ = RunAndReturnOutput('HasSimpleTest') self.assertIn('4 FAILED TEST', txt) self.assertNotIn('[ SKIPPED ]', txt) finally: SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, None) def testFlagOverridesEnvVar(self): """Tests precedence of flag over env var.""" try: SetEnvVar(FAIL_FAST_ENV_VAR, '0') txt, _ = RunAndReturnOutput('HasSimpleTest', True) self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) finally: SetEnvVar(FAIL_FAST_ENV_VAR, None) def testGoogletestEnvVarOverridesBazelEnvVar(self): """Tests that the Googletest native env var over Bazel testbridge.""" try: SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, '0') SetEnvVar(FAIL_FAST_ENV_VAR, '1') txt, _ = RunAndReturnOutput('HasSimpleTest') self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) finally: SetEnvVar(FAIL_FAST_ENV_VAR, None) SetEnvVar(BAZEL_FAIL_FAST_ENV_VAR, None) def testEventListener(self): txt, _ = RunAndReturnOutput(test_suite='HasSkipTest', fail_fast=True) self.assertIn('1 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 3 tests', txt) for expected_count, callback in [(1, 'OnTestSuiteStart'), (5, 'OnTestStart'), (5, 'OnTestEnd'), (5, 'OnTestPartResult'), (1, 'OnTestSuiteEnd')]: self.assertEqual( expected_count, txt.count(callback), 'Expected %d calls to callback %s match count on output: %s ' % (expected_count, callback, txt)) txt, _ = RunAndReturnOutput(test_suite='HasSkipTest', fail_fast=False) self.assertIn('3 FAILED TEST', txt) self.assertIn('[ SKIPPED ] 1 test', txt) for expected_count, callback in [(1, 'OnTestSuiteStart'), (5, 'OnTestStart'), (5, 'OnTestEnd'), (5, 'OnTestPartResult'), (1, 'OnTestSuiteEnd')]: self.assertEqual( expected_count, txt.count(callback), 'Expected %d calls to callback %s match count on output: %s ' % (expected_count, callback, txt)) def assertXmlResultCount(self, result, count, xml): self.assertEqual( count, xml.count('result="%s"' % result), 'Expected \'result="%s"\' match count of %s: %s ' % (result, count, xml)) def assertXmlStatusCount(self, status, count, xml): self.assertEqual( count, xml.count('status="%s"' % status), 'Expected \'status="%s"\' match count of %s: %s ' % (status, count, xml)) def assertFailFastXmlAndTxtOutput(self, fail_fast, test_suite, passed_count, failure_count, skipped_count, suppressed_count, run_disabled=False): """Assert XML and text output of a test execution.""" txt, xml = RunAndReturnOutput(test_suite, fail_fast, run_disabled) if failure_count > 0: self.assertIn('%s FAILED TEST' % failure_count, txt) if suppressed_count > 0: self.assertIn('%s DISABLED TEST' % suppressed_count, txt) if skipped_count > 0: self.assertIn('[ SKIPPED ] %s tests' % skipped_count, txt) self.assertXmlStatusCount('run', passed_count + failure_count + skipped_count, xml) self.assertXmlStatusCount('notrun', suppressed_count, xml) self.assertXmlResultCount('completed', passed_count + failure_count, xml) self.assertXmlResultCount('skipped', skipped_count, xml) self.assertXmlResultCount('suppressed', suppressed_count, xml) def assertFailFastBehavior(self, test_suite, passed_count, failure_count, skipped_count, suppressed_count, run_disabled=False): """Assert --fail_fast via flag.""" for fail_fast in ('true', '1', 't', True): self.assertFailFastXmlAndTxtOutput(fail_fast, test_suite, passed_count, failure_count, skipped_count, suppressed_count, run_disabled) def assertNotFailFastBehavior(self, test_suite, passed_count, failure_count, skipped_count, suppressed_count, run_disabled=False): """Assert --nofail_fast via flag.""" for fail_fast in ('false', '0', 'f', False): self.assertFailFastXmlAndTxtOutput(fail_fast, test_suite, passed_count, failure_count, skipped_count, suppressed_count, run_disabled) def testFlag_HasFixtureTest(self): """Tests the behavior of fail_fast and TEST_F.""" self.assertFailFastBehavior( test_suite='HasFixtureTest', passed_count=1, failure_count=1, skipped_count=3, suppressed_count=0) self.assertNotFailFastBehavior( test_suite='HasFixtureTest', passed_count=1, failure_count=4, skipped_count=0, suppressed_count=0) def testFlag_HasSimpleTest(self): """Tests the behavior of fail_fast and TEST.""" self.assertFailFastBehavior( test_suite='HasSimpleTest', passed_count=1, failure_count=1, skipped_count=3, suppressed_count=0) self.assertNotFailFastBehavior( test_suite='HasSimpleTest', passed_count=1, failure_count=4, skipped_count=0, suppressed_count=0) def testFlag_HasParametersTest(self): """Tests the behavior of fail_fast and TEST_P.""" self.assertFailFastBehavior( test_suite='HasParametersSuite/HasParametersTest', passed_count=0, failure_count=1, skipped_count=3, suppressed_count=0) self.assertNotFailFastBehavior( test_suite='HasParametersSuite/HasParametersTest', passed_count=0, failure_count=4, skipped_count=0, suppressed_count=0) def testFlag_HasDisabledTest(self): """Tests the behavior of fail_fast and Disabled test cases.""" self.assertFailFastBehavior( test_suite='HasDisabledTest', passed_count=1, failure_count=1, skipped_count=2, suppressed_count=1, run_disabled=False) self.assertNotFailFastBehavior( test_suite='HasDisabledTest', passed_count=1, failure_count=3, skipped_count=0, suppressed_count=1, run_disabled=False) def testFlag_HasDisabledRunDisabledTest(self): """Tests the behavior of fail_fast and Disabled test cases enabled.""" self.assertFailFastBehavior( test_suite='HasDisabledTest', passed_count=1, failure_count=1, skipped_count=3, suppressed_count=0, run_disabled=True) self.assertNotFailFastBehavior( test_suite='HasDisabledTest', passed_count=1, failure_count=4, skipped_count=0, suppressed_count=0, run_disabled=True) def testFlag_HasDisabledSuiteTest(self): """Tests the behavior of fail_fast and Disabled test suites.""" self.assertFailFastBehavior( test_suite='DISABLED_HasDisabledSuite', passed_count=0, failure_count=0, skipped_count=0, suppressed_count=5, run_disabled=False) self.assertNotFailFastBehavior( test_suite='DISABLED_HasDisabledSuite', passed_count=0, failure_count=0, skipped_count=0, suppressed_count=5, run_disabled=False) def testFlag_HasDisabledSuiteRunDisabledTest(self): """Tests the behavior of fail_fast and Disabled test suites enabled.""" self.assertFailFastBehavior( test_suite='DISABLED_HasDisabledSuite', passed_count=1, failure_count=1, skipped_count=3, suppressed_count=0, run_disabled=True) self.assertNotFailFastBehavior( test_suite='DISABLED_HasDisabledSuite', passed_count=1, failure_count=4, skipped_count=0, suppressed_count=0, run_disabled=True) if SUPPORTS_DEATH_TESTS: def testFlag_HasDeathTest(self): """Tests the behavior of fail_fast and death tests.""" self.assertFailFastBehavior( test_suite='HasDeathTest', passed_count=1, failure_count=1, skipped_count=3, suppressed_count=0) self.assertNotFailFastBehavior( test_suite='HasDeathTest', passed_count=1, failure_count=4, skipped_count=0, suppressed_count=0) if __name__ == '__main__': gtest_test_utils.Main()