1*9e965d6fSRomain Jobredeaux# Copyright 2020 The Bazel Authors. All rights reserved. 2*9e965d6fSRomain Jobredeaux# 3*9e965d6fSRomain Jobredeaux# Licensed under the Apache License, Version 2.0 (the "License"); 4*9e965d6fSRomain Jobredeaux# you may not use this file except in compliance with the License. 5*9e965d6fSRomain Jobredeaux# You may obtain a copy of the License at 6*9e965d6fSRomain Jobredeaux# 7*9e965d6fSRomain Jobredeaux# http://www.apache.org/licenses/LICENSE-2.0 8*9e965d6fSRomain Jobredeaux# 9*9e965d6fSRomain Jobredeaux# Unless required by applicable law or agreed to in writing, software 10*9e965d6fSRomain Jobredeaux# distributed under the License is distributed on an "AS IS" BASIS, 11*9e965d6fSRomain Jobredeaux# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e965d6fSRomain Jobredeaux# See the License for the specific language governing permissions and 13*9e965d6fSRomain Jobredeaux# limitations under the License. 14*9e965d6fSRomain Jobredeaux 15*9e965d6fSRomain Jobredeaux"""Tests for unittest.bash.""" 16*9e965d6fSRomain Jobredeaux 17*9e965d6fSRomain Jobredeauxfrom __future__ import absolute_import 18*9e965d6fSRomain Jobredeauxfrom __future__ import division 19*9e965d6fSRomain Jobredeauxfrom __future__ import print_function 20*9e965d6fSRomain Jobredeaux 21*9e965d6fSRomain Jobredeauximport os 22*9e965d6fSRomain Jobredeauximport re 23*9e965d6fSRomain Jobredeauximport shutil 24*9e965d6fSRomain Jobredeauximport stat 25*9e965d6fSRomain Jobredeauximport subprocess 26*9e965d6fSRomain Jobredeauximport tempfile 27*9e965d6fSRomain Jobredeauximport textwrap 28*9e965d6fSRomain Jobredeauximport unittest 29*9e965d6fSRomain Jobredeaux 30*9e965d6fSRomain Jobredeaux# The test setup for this external test is forwarded to the internal bash test. 31*9e965d6fSRomain Jobredeaux# This allows the internal test to use the same runfiles to load unittest.bash. 32*9e965d6fSRomain Jobredeaux_TEST_PREAMBLE = """ 33*9e965d6fSRomain Jobredeaux#!/bin/bash 34*9e965d6fSRomain Jobredeaux# --- begin runfiles.bash initialization --- 35*9e965d6fSRomain Jobredeauxif [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then 36*9e965d6fSRomain Jobredeaux source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash" 37*9e965d6fSRomain Jobredeauxelse 38*9e965d6fSRomain Jobredeaux echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash" 39*9e965d6fSRomain Jobredeaux exit 1 40*9e965d6fSRomain Jobredeauxfi 41*9e965d6fSRomain Jobredeaux# --- end runfiles.bash initialization --- 42*9e965d6fSRomain Jobredeaux 43*9e965d6fSRomain Jobredeauxecho "Writing XML to ${XML_OUTPUT_FILE}" 44*9e965d6fSRomain Jobredeaux 45*9e965d6fSRomain Jobredeauxsource "$(rlocation "rules_android/test/bashunit/unittest.bash")" \ 46*9e965d6fSRomain Jobredeaux || { echo "Could not source unittest.bash" >&2; exit 1; } 47*9e965d6fSRomain Jobredeaux""" 48*9e965d6fSRomain Jobredeaux 49*9e965d6fSRomain JobredeauxANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") 50*9e965d6fSRomain Jobredeaux 51*9e965d6fSRomain Jobredeaux 52*9e965d6fSRomain Jobredeauxdef remove_ansi(line): 53*9e965d6fSRomain Jobredeaux """Remove ANSI-style escape sequences from the input.""" 54*9e965d6fSRomain Jobredeaux return ANSI_ESCAPE.sub("", line) 55*9e965d6fSRomain Jobredeaux 56*9e965d6fSRomain Jobredeaux 57*9e965d6fSRomain Jobredeauxclass TestResult(object): 58*9e965d6fSRomain Jobredeaux """Save test results for easy checking.""" 59*9e965d6fSRomain Jobredeaux 60*9e965d6fSRomain Jobredeaux def __init__(self, asserter, return_code, output, xmlfile): 61*9e965d6fSRomain Jobredeaux self._asserter = asserter 62*9e965d6fSRomain Jobredeaux self._return_code = return_code 63*9e965d6fSRomain Jobredeaux self._output = remove_ansi(output) 64*9e965d6fSRomain Jobredeaux 65*9e965d6fSRomain Jobredeaux # Read in the XML result file. 66*9e965d6fSRomain Jobredeaux if os.path.isfile(xmlfile): 67*9e965d6fSRomain Jobredeaux with open(xmlfile, "r") as f: 68*9e965d6fSRomain Jobredeaux self._xml = f.read() 69*9e965d6fSRomain Jobredeaux else: 70*9e965d6fSRomain Jobredeaux # Unable to read the file, errors will be reported later. 71*9e965d6fSRomain Jobredeaux self._xml = "" 72*9e965d6fSRomain Jobredeaux 73*9e965d6fSRomain Jobredeaux # Methods to assert on the state of the results. 74*9e965d6fSRomain Jobredeaux 75*9e965d6fSRomain Jobredeaux def assertLogMessage(self, message): 76*9e965d6fSRomain Jobredeaux self.assertExactlyOneMatch(self._output, message) 77*9e965d6fSRomain Jobredeaux 78*9e965d6fSRomain Jobredeaux def assertNotLogMessage(self, message): 79*9e965d6fSRomain Jobredeaux self._asserter.assertNotRegex(self._output, message) 80*9e965d6fSRomain Jobredeaux 81*9e965d6fSRomain Jobredeaux def assertXmlMessage(self, message): 82*9e965d6fSRomain Jobredeaux self.assertExactlyOneMatch(self._xml, message) 83*9e965d6fSRomain Jobredeaux 84*9e965d6fSRomain Jobredeaux def assertNotXmlMessage(self, message): 85*9e965d6fSRomain Jobredeaux self._asserter.assertNotRegex(self._xml, message) 86*9e965d6fSRomain Jobredeaux 87*9e965d6fSRomain Jobredeaux def assertSuccess(self, suite_name): 88*9e965d6fSRomain Jobredeaux self._asserter.assertEqual(0, self._return_code, 89*9e965d6fSRomain Jobredeaux f"Script failed unexpectedly:\n{self._output}") 90*9e965d6fSRomain Jobredeaux self.assertLogMessage(suite_name) 91*9e965d6fSRomain Jobredeaux self.assertXmlMessage("<testsuites [^/]*failures=\"0\"") 92*9e965d6fSRomain Jobredeaux self.assertXmlMessage("<testsuites [^/]*errors=\"0\"") 93*9e965d6fSRomain Jobredeaux 94*9e965d6fSRomain Jobredeaux def assertNotSuccess(self, suite_name, failures=0, errors=0): 95*9e965d6fSRomain Jobredeaux self._asserter.assertNotEqual(0, self._return_code) 96*9e965d6fSRomain Jobredeaux self.assertLogMessage(suite_name) 97*9e965d6fSRomain Jobredeaux if failures: 98*9e965d6fSRomain Jobredeaux self.assertXmlMessage(f'<testsuites [^/]*failures="{failures}"') 99*9e965d6fSRomain Jobredeaux if errors: 100*9e965d6fSRomain Jobredeaux self.assertXmlMessage(f'<testsuites [^/]*errors="{errors}"') 101*9e965d6fSRomain Jobredeaux 102*9e965d6fSRomain Jobredeaux def assertTestPassed(self, test_name): 103*9e965d6fSRomain Jobredeaux self.assertLogMessage(f"PASSED: {test_name}") 104*9e965d6fSRomain Jobredeaux 105*9e965d6fSRomain Jobredeaux def assertTestFailed(self, test_name, message=""): 106*9e965d6fSRomain Jobredeaux self.assertLogMessage(f"{test_name} FAILED: {message}") 107*9e965d6fSRomain Jobredeaux 108*9e965d6fSRomain Jobredeaux def assertExactlyOneMatch(self, text, pattern): 109*9e965d6fSRomain Jobredeaux self._asserter.assertRegex(text, pattern) 110*9e965d6fSRomain Jobredeaux self._asserter.assertEqual( 111*9e965d6fSRomain Jobredeaux len(re.findall(pattern, text)), 112*9e965d6fSRomain Jobredeaux 1, 113*9e965d6fSRomain Jobredeaux msg=f"Found more than 1 match of '{pattern}' in '{text}'") 114*9e965d6fSRomain Jobredeaux 115*9e965d6fSRomain Jobredeaux 116*9e965d6fSRomain Jobredeauxclass UnittestTest(unittest.TestCase): 117*9e965d6fSRomain Jobredeaux 118*9e965d6fSRomain Jobredeaux def setUp(self): 119*9e965d6fSRomain Jobredeaux """Create a working directory under our temp dir.""" 120*9e965d6fSRomain Jobredeaux super(UnittestTest, self).setUp() 121*9e965d6fSRomain Jobredeaux self.work_dir = tempfile.mkdtemp(dir=os.environ["TEST_TMPDIR"]) 122*9e965d6fSRomain Jobredeaux 123*9e965d6fSRomain Jobredeaux def tearDown(self): 124*9e965d6fSRomain Jobredeaux """Clean up the working directory.""" 125*9e965d6fSRomain Jobredeaux super(UnittestTest, self).tearDown() 126*9e965d6fSRomain Jobredeaux shutil.rmtree(self.work_dir) 127*9e965d6fSRomain Jobredeaux 128*9e965d6fSRomain Jobredeaux def write_file(self, filename, contents=""): 129*9e965d6fSRomain Jobredeaux """Write the contents to a file in the workdir.""" 130*9e965d6fSRomain Jobredeaux 131*9e965d6fSRomain Jobredeaux filepath = os.path.join(self.work_dir, filename) 132*9e965d6fSRomain Jobredeaux with open(filepath, "w") as f: 133*9e965d6fSRomain Jobredeaux f.write(_TEST_PREAMBLE.strip()) 134*9e965d6fSRomain Jobredeaux f.write(contents) 135*9e965d6fSRomain Jobredeaux os.chmod(filepath, stat.S_IEXEC | stat.S_IWRITE | stat.S_IREAD) 136*9e965d6fSRomain Jobredeaux 137*9e965d6fSRomain Jobredeaux def find_runfiles(self): 138*9e965d6fSRomain Jobredeaux if "RUNFILES_DIR" in os.environ: 139*9e965d6fSRomain Jobredeaux return os.environ["RUNFILES_DIR"] 140*9e965d6fSRomain Jobredeaux 141*9e965d6fSRomain Jobredeaux # Fall back to being based on the srcdir. 142*9e965d6fSRomain Jobredeaux if "TEST_SRCDIR" in os.environ: 143*9e965d6fSRomain Jobredeaux return os.environ["TEST_SRCDIR"] 144*9e965d6fSRomain Jobredeaux 145*9e965d6fSRomain Jobredeaux # Base on the current dir 146*9e965d6fSRomain Jobredeaux return f"{os.getcwd()}/.." 147*9e965d6fSRomain Jobredeaux 148*9e965d6fSRomain Jobredeaux def execute_test(self, filename, env=None, args=()): 149*9e965d6fSRomain Jobredeaux """Executes the file and stores the results.""" 150*9e965d6fSRomain Jobredeaux 151*9e965d6fSRomain Jobredeaux filepath = os.path.join(self.work_dir, filename) 152*9e965d6fSRomain Jobredeaux xmlfile = os.path.join(self.work_dir, "dummy-testlog.xml") 153*9e965d6fSRomain Jobredeaux test_env = { 154*9e965d6fSRomain Jobredeaux "TEST_TMPDIR": self.work_dir, 155*9e965d6fSRomain Jobredeaux "RUNFILES_DIR": self.find_runfiles(), 156*9e965d6fSRomain Jobredeaux "TEST_SRCDIR": os.environ["TEST_SRCDIR"], 157*9e965d6fSRomain Jobredeaux "XML_OUTPUT_FILE": xmlfile, 158*9e965d6fSRomain Jobredeaux } 159*9e965d6fSRomain Jobredeaux # Add in env, forcing everything to be a string. 160*9e965d6fSRomain Jobredeaux if env: 161*9e965d6fSRomain Jobredeaux for k, v in env.items(): 162*9e965d6fSRomain Jobredeaux test_env[k] = str(v) 163*9e965d6fSRomain Jobredeaux completed = subprocess.run( 164*9e965d6fSRomain Jobredeaux [filepath, *args], 165*9e965d6fSRomain Jobredeaux env=test_env, 166*9e965d6fSRomain Jobredeaux stdout=subprocess.PIPE, 167*9e965d6fSRomain Jobredeaux stderr=subprocess.STDOUT, 168*9e965d6fSRomain Jobredeaux ) 169*9e965d6fSRomain Jobredeaux return TestResult(self, completed.returncode, 170*9e965d6fSRomain Jobredeaux completed.stdout.decode("utf-8"), xmlfile) 171*9e965d6fSRomain Jobredeaux 172*9e965d6fSRomain Jobredeaux # Actual test cases. 173*9e965d6fSRomain Jobredeaux 174*9e965d6fSRomain Jobredeaux def test_success(self): 175*9e965d6fSRomain Jobredeaux self.write_file( 176*9e965d6fSRomain Jobredeaux "thing.sh", """ 177*9e965d6fSRomain Jobredeauxfunction test_success() { 178*9e965d6fSRomain Jobredeaux echo foo >&${TEST_log} || fail "expected echo to succeed" 179*9e965d6fSRomain Jobredeaux expect_log "foo" 180*9e965d6fSRomain Jobredeaux} 181*9e965d6fSRomain Jobredeaux 182*9e965d6fSRomain Jobredeauxrun_suite "success tests" 183*9e965d6fSRomain Jobredeaux""") 184*9e965d6fSRomain Jobredeaux 185*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 186*9e965d6fSRomain Jobredeaux result.assertSuccess("success tests") 187*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_success") 188*9e965d6fSRomain Jobredeaux 189*9e965d6fSRomain Jobredeaux def test_timestamp(self): 190*9e965d6fSRomain Jobredeaux self.write_file( 191*9e965d6fSRomain Jobredeaux "thing.sh", """ 192*9e965d6fSRomain Jobredeauxfunction test_timestamp() { 193*9e965d6fSRomain Jobredeaux local ts=$(timestamp) 194*9e965d6fSRomain Jobredeaux [[ $ts =~ ^[0-9]{13}$ ]] || fail "timestamp wan't valid: $ts" 195*9e965d6fSRomain Jobredeaux 196*9e965d6fSRomain Jobredeaux local time_diff=$(get_run_time 100000 223456) 197*9e965d6fSRomain Jobredeaux assert_equals $time_diff 123.456 198*9e965d6fSRomain Jobredeaux} 199*9e965d6fSRomain Jobredeaux 200*9e965d6fSRomain Jobredeauxrun_suite "timestamp tests" 201*9e965d6fSRomain Jobredeaux""") 202*9e965d6fSRomain Jobredeaux 203*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 204*9e965d6fSRomain Jobredeaux result.assertSuccess("timestamp tests") 205*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_timestamp") 206*9e965d6fSRomain Jobredeaux 207*9e965d6fSRomain Jobredeaux def test_failure(self): 208*9e965d6fSRomain Jobredeaux self.write_file( 209*9e965d6fSRomain Jobredeaux "thing.sh", """ 210*9e965d6fSRomain Jobredeauxfunction test_failure() { 211*9e965d6fSRomain Jobredeaux fail "I'm a failure with <>&\\" escaped symbols" 212*9e965d6fSRomain Jobredeaux} 213*9e965d6fSRomain Jobredeaux 214*9e965d6fSRomain Jobredeauxrun_suite "failure tests" 215*9e965d6fSRomain Jobredeaux""") 216*9e965d6fSRomain Jobredeaux 217*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 218*9e965d6fSRomain Jobredeaux result.assertNotSuccess("failure tests", failures=0, errors=1) 219*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_failure") 220*9e965d6fSRomain Jobredeaux result.assertXmlMessage( 221*9e965d6fSRomain Jobredeaux "message=\"I'm a failure with <>&" escaped symbols\"") 222*9e965d6fSRomain Jobredeaux result.assertXmlMessage("I'm a failure with <>&\" escaped symbols") 223*9e965d6fSRomain Jobredeaux 224*9e965d6fSRomain Jobredeaux def test_set_bash_errexit_prints_stack_trace(self): 225*9e965d6fSRomain Jobredeaux self.write_file( 226*9e965d6fSRomain Jobredeaux "thing.sh", """ 227*9e965d6fSRomain Jobredeauxset -euo pipefail 228*9e965d6fSRomain Jobredeaux 229*9e965d6fSRomain Jobredeauxfunction helper() { 230*9e965d6fSRomain Jobredeaux echo before 231*9e965d6fSRomain Jobredeaux false 232*9e965d6fSRomain Jobredeaux echo after 233*9e965d6fSRomain Jobredeaux} 234*9e965d6fSRomain Jobredeaux 235*9e965d6fSRomain Jobredeauxfunction test_failure_in_helper() { 236*9e965d6fSRomain Jobredeaux helper 237*9e965d6fSRomain Jobredeaux} 238*9e965d6fSRomain Jobredeaux 239*9e965d6fSRomain Jobredeauxrun_suite "bash errexit tests" 240*9e965d6fSRomain Jobredeaux""") 241*9e965d6fSRomain Jobredeaux 242*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 243*9e965d6fSRomain Jobredeaux result.assertNotSuccess("bash errexit tests") 244*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_failure_in_helper") 245*9e965d6fSRomain Jobredeaux result.assertLogMessage(r"./thing.sh:\d*: in call to helper") 246*9e965d6fSRomain Jobredeaux result.assertLogMessage( 247*9e965d6fSRomain Jobredeaux r"./thing.sh:\d*: in call to test_failure_in_helper") 248*9e965d6fSRomain Jobredeaux 249*9e965d6fSRomain Jobredeaux def test_set_bash_errexit_runs_tear_down(self): 250*9e965d6fSRomain Jobredeaux self.write_file( 251*9e965d6fSRomain Jobredeaux "thing.sh", """ 252*9e965d6fSRomain Jobredeauxset -euo pipefail 253*9e965d6fSRomain Jobredeaux 254*9e965d6fSRomain Jobredeauxfunction tear_down() { 255*9e965d6fSRomain Jobredeaux echo "Running tear_down" 256*9e965d6fSRomain Jobredeaux} 257*9e965d6fSRomain Jobredeaux 258*9e965d6fSRomain Jobredeauxfunction testenv_tear_down() { 259*9e965d6fSRomain Jobredeaux echo "Running testenv_tear_down" 260*9e965d6fSRomain Jobredeaux} 261*9e965d6fSRomain Jobredeaux 262*9e965d6fSRomain Jobredeauxfunction test_failure_in_helper() { 263*9e965d6fSRomain Jobredeaux wrong_command 264*9e965d6fSRomain Jobredeaux} 265*9e965d6fSRomain Jobredeaux 266*9e965d6fSRomain Jobredeauxrun_suite "bash errexit tests" 267*9e965d6fSRomain Jobredeaux""") 268*9e965d6fSRomain Jobredeaux 269*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 270*9e965d6fSRomain Jobredeaux result.assertNotSuccess("bash errexit tests") 271*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_failure_in_helper") 272*9e965d6fSRomain Jobredeaux result.assertLogMessage("Running tear_down") 273*9e965d6fSRomain Jobredeaux result.assertLogMessage("Running testenv_tear_down") 274*9e965d6fSRomain Jobredeaux 275*9e965d6fSRomain Jobredeaux def test_set_bash_errexit_pipefail_propagates_failure_through_pipe(self): 276*9e965d6fSRomain Jobredeaux self.write_file( 277*9e965d6fSRomain Jobredeaux "thing.sh", """ 278*9e965d6fSRomain Jobredeauxset -euo pipefail 279*9e965d6fSRomain Jobredeaux 280*9e965d6fSRomain Jobredeauxfunction test_pipefail() { 281*9e965d6fSRomain Jobredeaux wrong_command | cat 282*9e965d6fSRomain Jobredeaux echo after 283*9e965d6fSRomain Jobredeaux} 284*9e965d6fSRomain Jobredeaux 285*9e965d6fSRomain Jobredeauxrun_suite "bash errexit tests" 286*9e965d6fSRomain Jobredeaux""") 287*9e965d6fSRomain Jobredeaux 288*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 289*9e965d6fSRomain Jobredeaux result.assertNotSuccess("bash errexit tests") 290*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_pipefail") 291*9e965d6fSRomain Jobredeaux result.assertLogMessage("wrong_command: command not found") 292*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("after") 293*9e965d6fSRomain Jobredeaux 294*9e965d6fSRomain Jobredeaux def test_set_bash_errexit_no_pipefail_ignores_failure_before_pipe(self): 295*9e965d6fSRomain Jobredeaux self.write_file( 296*9e965d6fSRomain Jobredeaux "thing.sh", """ 297*9e965d6fSRomain Jobredeauxset -eu 298*9e965d6fSRomain Jobredeauxset +o pipefail 299*9e965d6fSRomain Jobredeaux 300*9e965d6fSRomain Jobredeauxfunction test_nopipefail() { 301*9e965d6fSRomain Jobredeaux wrong_command | cat 302*9e965d6fSRomain Jobredeaux echo after 303*9e965d6fSRomain Jobredeaux} 304*9e965d6fSRomain Jobredeaux 305*9e965d6fSRomain Jobredeauxrun_suite "bash errexit tests" 306*9e965d6fSRomain Jobredeaux""") 307*9e965d6fSRomain Jobredeaux 308*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 309*9e965d6fSRomain Jobredeaux result.assertSuccess("bash errexit tests") 310*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_nopipefail") 311*9e965d6fSRomain Jobredeaux result.assertLogMessage("wrong_command: command not found") 312*9e965d6fSRomain Jobredeaux result.assertLogMessage("after") 313*9e965d6fSRomain Jobredeaux 314*9e965d6fSRomain Jobredeaux def test_set_bash_errexit_pipefail_long_testname_succeeds(self): 315*9e965d6fSRomain Jobredeaux test_name = "x" * 1000 316*9e965d6fSRomain Jobredeaux self.write_file( 317*9e965d6fSRomain Jobredeaux "thing.sh", """ 318*9e965d6fSRomain Jobredeauxset -euo pipefail 319*9e965d6fSRomain Jobredeaux 320*9e965d6fSRomain Jobredeauxfunction test_%s() { 321*9e965d6fSRomain Jobredeaux : 322*9e965d6fSRomain Jobredeaux} 323*9e965d6fSRomain Jobredeaux 324*9e965d6fSRomain Jobredeauxrun_suite "bash errexit tests" 325*9e965d6fSRomain Jobredeaux""" % test_name) 326*9e965d6fSRomain Jobredeaux 327*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 328*9e965d6fSRomain Jobredeaux result.assertSuccess("bash errexit tests") 329*9e965d6fSRomain Jobredeaux 330*9e965d6fSRomain Jobredeaux def test_empty_test_fails(self): 331*9e965d6fSRomain Jobredeaux self.write_file("thing.sh", """ 332*9e965d6fSRomain Jobredeaux# No tests present. 333*9e965d6fSRomain Jobredeaux 334*9e965d6fSRomain Jobredeauxrun_suite "empty test suite" 335*9e965d6fSRomain Jobredeaux""") 336*9e965d6fSRomain Jobredeaux 337*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 338*9e965d6fSRomain Jobredeaux result.assertNotSuccess("empty test suite") 339*9e965d6fSRomain Jobredeaux result.assertLogMessage("No tests found.") 340*9e965d6fSRomain Jobredeaux 341*9e965d6fSRomain Jobredeaux def test_empty_test_succeeds_sharding(self): 342*9e965d6fSRomain Jobredeaux self.write_file( 343*9e965d6fSRomain Jobredeaux "thing.sh", """ 344*9e965d6fSRomain Jobredeaux# Only one test. 345*9e965d6fSRomain Jobredeauxfunction test_thing() { 346*9e965d6fSRomain Jobredeaux echo 347*9e965d6fSRomain Jobredeaux} 348*9e965d6fSRomain Jobredeaux 349*9e965d6fSRomain Jobredeauxrun_suite "empty test suite" 350*9e965d6fSRomain Jobredeaux""") 351*9e965d6fSRomain Jobredeaux 352*9e965d6fSRomain Jobredeaux # First shard. 353*9e965d6fSRomain Jobredeaux result = self.execute_test( 354*9e965d6fSRomain Jobredeaux "thing.sh", env={ 355*9e965d6fSRomain Jobredeaux "TEST_TOTAL_SHARDS": 2, 356*9e965d6fSRomain Jobredeaux "TEST_SHARD_INDEX": 0, 357*9e965d6fSRomain Jobredeaux }) 358*9e965d6fSRomain Jobredeaux result.assertSuccess("empty test suite") 359*9e965d6fSRomain Jobredeaux result.assertLogMessage("No tests executed due to sharding") 360*9e965d6fSRomain Jobredeaux 361*9e965d6fSRomain Jobredeaux # Second shard. 362*9e965d6fSRomain Jobredeaux result = self.execute_test( 363*9e965d6fSRomain Jobredeaux "thing.sh", env={ 364*9e965d6fSRomain Jobredeaux "TEST_TOTAL_SHARDS": 2, 365*9e965d6fSRomain Jobredeaux "TEST_SHARD_INDEX": 1, 366*9e965d6fSRomain Jobredeaux }) 367*9e965d6fSRomain Jobredeaux result.assertSuccess("empty test suite") 368*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("No tests") 369*9e965d6fSRomain Jobredeaux 370*9e965d6fSRomain Jobredeaux def test_filter_runs_only_matching_test(self): 371*9e965d6fSRomain Jobredeaux self.write_file( 372*9e965d6fSRomain Jobredeaux "thing.sh", 373*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 374*9e965d6fSRomain Jobredeaux function test_abc() { 375*9e965d6fSRomain Jobredeaux : 376*9e965d6fSRomain Jobredeaux } 377*9e965d6fSRomain Jobredeaux 378*9e965d6fSRomain Jobredeaux function test_def() { 379*9e965d6fSRomain Jobredeaux echo "running def" 380*9e965d6fSRomain Jobredeaux } 381*9e965d6fSRomain Jobredeaux 382*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 383*9e965d6fSRomain Jobredeaux """)) 384*9e965d6fSRomain Jobredeaux 385*9e965d6fSRomain Jobredeaux result = self.execute_test( 386*9e965d6fSRomain Jobredeaux "thing.sh", env={"TESTBRIDGE_TEST_ONLY": "test_a*"}) 387*9e965d6fSRomain Jobredeaux 388*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 389*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_abc") 390*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running def") 391*9e965d6fSRomain Jobredeaux 392*9e965d6fSRomain Jobredeaux def test_filter_prefix_match_only_skips_test(self): 393*9e965d6fSRomain Jobredeaux self.write_file( 394*9e965d6fSRomain Jobredeaux "thing.sh", 395*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 396*9e965d6fSRomain Jobredeaux function test_abc() { 397*9e965d6fSRomain Jobredeaux echo "running abc" 398*9e965d6fSRomain Jobredeaux } 399*9e965d6fSRomain Jobredeaux 400*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 401*9e965d6fSRomain Jobredeaux """)) 402*9e965d6fSRomain Jobredeaux 403*9e965d6fSRomain Jobredeaux result = self.execute_test( 404*9e965d6fSRomain Jobredeaux "thing.sh", env={"TESTBRIDGE_TEST_ONLY": "test_a"}) 405*9e965d6fSRomain Jobredeaux 406*9e965d6fSRomain Jobredeaux result.assertNotSuccess("tests to filter") 407*9e965d6fSRomain Jobredeaux result.assertLogMessage("No tests found.") 408*9e965d6fSRomain Jobredeaux 409*9e965d6fSRomain Jobredeaux def test_filter_multiple_globs_runs_tests_matching_any(self): 410*9e965d6fSRomain Jobredeaux self.write_file( 411*9e965d6fSRomain Jobredeaux "thing.sh", 412*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 413*9e965d6fSRomain Jobredeaux function test_abc() { 414*9e965d6fSRomain Jobredeaux echo "running abc" 415*9e965d6fSRomain Jobredeaux } 416*9e965d6fSRomain Jobredeaux 417*9e965d6fSRomain Jobredeaux function test_def() { 418*9e965d6fSRomain Jobredeaux echo "running def" 419*9e965d6fSRomain Jobredeaux } 420*9e965d6fSRomain Jobredeaux 421*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 422*9e965d6fSRomain Jobredeaux """)) 423*9e965d6fSRomain Jobredeaux 424*9e965d6fSRomain Jobredeaux result = self.execute_test( 425*9e965d6fSRomain Jobredeaux "thing.sh", env={"TESTBRIDGE_TEST_ONLY": "donotmatch:*a*"}) 426*9e965d6fSRomain Jobredeaux 427*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 428*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_abc") 429*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running def") 430*9e965d6fSRomain Jobredeaux 431*9e965d6fSRomain Jobredeaux def test_filter_character_group_runs_only_matching_tests(self): 432*9e965d6fSRomain Jobredeaux self.write_file( 433*9e965d6fSRomain Jobredeaux "thing.sh", 434*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 435*9e965d6fSRomain Jobredeaux function test_aaa() { 436*9e965d6fSRomain Jobredeaux : 437*9e965d6fSRomain Jobredeaux } 438*9e965d6fSRomain Jobredeaux 439*9e965d6fSRomain Jobredeaux function test_daa() { 440*9e965d6fSRomain Jobredeaux : 441*9e965d6fSRomain Jobredeaux } 442*9e965d6fSRomain Jobredeaux 443*9e965d6fSRomain Jobredeaux function test_zaa() { 444*9e965d6fSRomain Jobredeaux echo "running zaa" 445*9e965d6fSRomain Jobredeaux } 446*9e965d6fSRomain Jobredeaux 447*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 448*9e965d6fSRomain Jobredeaux """)) 449*9e965d6fSRomain Jobredeaux 450*9e965d6fSRomain Jobredeaux result = self.execute_test( 451*9e965d6fSRomain Jobredeaux "thing.sh", env={"TESTBRIDGE_TEST_ONLY": "test_[a-f]aa"}) 452*9e965d6fSRomain Jobredeaux 453*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 454*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_aaa") 455*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_daa") 456*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running zaa") 457*9e965d6fSRomain Jobredeaux 458*9e965d6fSRomain Jobredeaux def test_filter_sharded_runs_subset_of_filtered_tests(self): 459*9e965d6fSRomain Jobredeaux for index in range(2): 460*9e965d6fSRomain Jobredeaux with self.subTest(index=index): 461*9e965d6fSRomain Jobredeaux self.__filter_sharded_runs_subset_of_filtered_tests(index) 462*9e965d6fSRomain Jobredeaux 463*9e965d6fSRomain Jobredeaux def __filter_sharded_runs_subset_of_filtered_tests(self, index): 464*9e965d6fSRomain Jobredeaux self.write_file( 465*9e965d6fSRomain Jobredeaux "thing.sh", 466*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 467*9e965d6fSRomain Jobredeaux function test_a0() { 468*9e965d6fSRomain Jobredeaux echo "running a0" 469*9e965d6fSRomain Jobredeaux } 470*9e965d6fSRomain Jobredeaux 471*9e965d6fSRomain Jobredeaux function test_a1() { 472*9e965d6fSRomain Jobredeaux echo "running a1" 473*9e965d6fSRomain Jobredeaux } 474*9e965d6fSRomain Jobredeaux 475*9e965d6fSRomain Jobredeaux function test_bb() { 476*9e965d6fSRomain Jobredeaux echo "running bb" 477*9e965d6fSRomain Jobredeaux } 478*9e965d6fSRomain Jobredeaux 479*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 480*9e965d6fSRomain Jobredeaux """)) 481*9e965d6fSRomain Jobredeaux 482*9e965d6fSRomain Jobredeaux result = self.execute_test( 483*9e965d6fSRomain Jobredeaux "thing.sh", 484*9e965d6fSRomain Jobredeaux env={ 485*9e965d6fSRomain Jobredeaux "TESTBRIDGE_TEST_ONLY": "test_a*", 486*9e965d6fSRomain Jobredeaux "TEST_TOTAL_SHARDS": 2, 487*9e965d6fSRomain Jobredeaux "TEST_SHARD_INDEX": index 488*9e965d6fSRomain Jobredeaux }) 489*9e965d6fSRomain Jobredeaux 490*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 491*9e965d6fSRomain Jobredeaux # The sharding logic is shifted by 1, starts with 2nd shard. 492*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_a" + str(index ^ 1)) 493*9e965d6fSRomain Jobredeaux result.assertLogMessage("running a" + str(index ^ 1)) 494*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running a" + str(index)) 495*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running bb") 496*9e965d6fSRomain Jobredeaux 497*9e965d6fSRomain Jobredeaux def test_arg_runs_only_matching_test_and_issues_warning(self): 498*9e965d6fSRomain Jobredeaux self.write_file( 499*9e965d6fSRomain Jobredeaux "thing.sh", 500*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 501*9e965d6fSRomain Jobredeaux function test_abc() { 502*9e965d6fSRomain Jobredeaux : 503*9e965d6fSRomain Jobredeaux } 504*9e965d6fSRomain Jobredeaux 505*9e965d6fSRomain Jobredeaux function test_def() { 506*9e965d6fSRomain Jobredeaux echo "running def" 507*9e965d6fSRomain Jobredeaux } 508*9e965d6fSRomain Jobredeaux 509*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 510*9e965d6fSRomain Jobredeaux """)) 511*9e965d6fSRomain Jobredeaux 512*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh", args=["test_abc"]) 513*9e965d6fSRomain Jobredeaux 514*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 515*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_abc") 516*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running def") 517*9e965d6fSRomain Jobredeaux result.assertLogMessage( 518*9e965d6fSRomain Jobredeaux r"WARNING: Passing test names in arguments \(--test_arg\) is " 519*9e965d6fSRomain Jobredeaux "deprecated, please use --test_filter='test_abc' instead.") 520*9e965d6fSRomain Jobredeaux 521*9e965d6fSRomain Jobredeaux def test_arg_multiple_tests_issues_warning_with_test_filter_command(self): 522*9e965d6fSRomain Jobredeaux self.write_file( 523*9e965d6fSRomain Jobredeaux "thing.sh", 524*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 525*9e965d6fSRomain Jobredeaux function test_abc() { 526*9e965d6fSRomain Jobredeaux : 527*9e965d6fSRomain Jobredeaux } 528*9e965d6fSRomain Jobredeaux 529*9e965d6fSRomain Jobredeaux function test_def() { 530*9e965d6fSRomain Jobredeaux : 531*9e965d6fSRomain Jobredeaux } 532*9e965d6fSRomain Jobredeaux 533*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 534*9e965d6fSRomain Jobredeaux """)) 535*9e965d6fSRomain Jobredeaux 536*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh", args=["test_abc", "test_def"]) 537*9e965d6fSRomain Jobredeaux 538*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 539*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_abc") 540*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_def") 541*9e965d6fSRomain Jobredeaux result.assertLogMessage( 542*9e965d6fSRomain Jobredeaux r"WARNING: Passing test names in arguments \(--test_arg\) is " 543*9e965d6fSRomain Jobredeaux "deprecated, please use --test_filter='test_abc:test_def' instead.") 544*9e965d6fSRomain Jobredeaux 545*9e965d6fSRomain Jobredeaux def test_arg_and_filter_ignores_arg(self): 546*9e965d6fSRomain Jobredeaux self.write_file( 547*9e965d6fSRomain Jobredeaux "thing.sh", 548*9e965d6fSRomain Jobredeaux textwrap.dedent(""" 549*9e965d6fSRomain Jobredeaux function test_abc() { 550*9e965d6fSRomain Jobredeaux : 551*9e965d6fSRomain Jobredeaux } 552*9e965d6fSRomain Jobredeaux 553*9e965d6fSRomain Jobredeaux function test_def() { 554*9e965d6fSRomain Jobredeaux echo "running def" 555*9e965d6fSRomain Jobredeaux } 556*9e965d6fSRomain Jobredeaux 557*9e965d6fSRomain Jobredeaux run_suite "tests to filter" 558*9e965d6fSRomain Jobredeaux """)) 559*9e965d6fSRomain Jobredeaux 560*9e965d6fSRomain Jobredeaux result = self.execute_test( 561*9e965d6fSRomain Jobredeaux "thing.sh", args=["test_def"], env={"TESTBRIDGE_TEST_ONLY": "test_a*"}) 562*9e965d6fSRomain Jobredeaux 563*9e965d6fSRomain Jobredeaux result.assertSuccess("tests to filter") 564*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_abc") 565*9e965d6fSRomain Jobredeaux result.assertNotLogMessage("running def") 566*9e965d6fSRomain Jobredeaux result.assertLogMessage( 567*9e965d6fSRomain Jobredeaux "WARNING: Both --test_arg and --test_filter specified, ignoring --test_arg" 568*9e965d6fSRomain Jobredeaux ) 569*9e965d6fSRomain Jobredeaux 570*9e965d6fSRomain Jobredeaux def test_custom_ifs_variable_finds_and_runs_test(self): 571*9e965d6fSRomain Jobredeaux for sharded in (False, True): 572*9e965d6fSRomain Jobredeaux for ifs in (r"\t", "t"): 573*9e965d6fSRomain Jobredeaux with self.subTest(ifs=ifs, sharded=sharded): 574*9e965d6fSRomain Jobredeaux self.__custom_ifs_variable_finds_and_runs_test(ifs, sharded) 575*9e965d6fSRomain Jobredeaux 576*9e965d6fSRomain Jobredeaux def __custom_ifs_variable_finds_and_runs_test(self, ifs, sharded): 577*9e965d6fSRomain Jobredeaux self.write_file( 578*9e965d6fSRomain Jobredeaux "thing.sh", 579*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 580*9e965d6fSRomain Jobredeaux set -euo pipefail 581*9e965d6fSRomain Jobredeaux IFS=$'%s' 582*9e965d6fSRomain Jobredeaux function test_foo() { 583*9e965d6fSRomain Jobredeaux : 584*9e965d6fSRomain Jobredeaux } 585*9e965d6fSRomain Jobredeaux 586*9e965d6fSRomain Jobredeaux run_suite "custom IFS test" 587*9e965d6fSRomain Jobredeaux """ % ifs)) 588*9e965d6fSRomain Jobredeaux 589*9e965d6fSRomain Jobredeaux result = self.execute_test( 590*9e965d6fSRomain Jobredeaux "thing.sh", 591*9e965d6fSRomain Jobredeaux env={} if not sharded else { 592*9e965d6fSRomain Jobredeaux "TEST_TOTAL_SHARDS": 2, 593*9e965d6fSRomain Jobredeaux "TEST_SHARD_INDEX": 1 594*9e965d6fSRomain Jobredeaux }) 595*9e965d6fSRomain Jobredeaux 596*9e965d6fSRomain Jobredeaux result.assertSuccess("custom IFS test") 597*9e965d6fSRomain Jobredeaux result.assertTestPassed("test_foo") 598*9e965d6fSRomain Jobredeaux 599*9e965d6fSRomain Jobredeaux def test_fail_in_teardown_reports_failure(self): 600*9e965d6fSRomain Jobredeaux self.write_file( 601*9e965d6fSRomain Jobredeaux "thing.sh", 602*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 603*9e965d6fSRomain Jobredeaux function tear_down() { 604*9e965d6fSRomain Jobredeaux echo "tear_down log" >"${TEST_log}" 605*9e965d6fSRomain Jobredeaux fail "tear_down failure" 606*9e965d6fSRomain Jobredeaux } 607*9e965d6fSRomain Jobredeaux 608*9e965d6fSRomain Jobredeaux function test_foo() { 609*9e965d6fSRomain Jobredeaux : 610*9e965d6fSRomain Jobredeaux } 611*9e965d6fSRomain Jobredeaux 612*9e965d6fSRomain Jobredeaux run_suite "Failure in tear_down test" 613*9e965d6fSRomain Jobredeaux """)) 614*9e965d6fSRomain Jobredeaux 615*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 616*9e965d6fSRomain Jobredeaux 617*9e965d6fSRomain Jobredeaux result.assertNotSuccess("Failure in tear_down test", errors=1) 618*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_foo", "tear_down failure") 619*9e965d6fSRomain Jobredeaux result.assertXmlMessage('message="tear_down failure"') 620*9e965d6fSRomain Jobredeaux result.assertLogMessage("tear_down log") 621*9e965d6fSRomain Jobredeaux 622*9e965d6fSRomain Jobredeaux def test_fail_in_teardown_after_test_failure_reports_both_failures(self): 623*9e965d6fSRomain Jobredeaux self.write_file( 624*9e965d6fSRomain Jobredeaux "thing.sh", 625*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 626*9e965d6fSRomain Jobredeaux function tear_down() { 627*9e965d6fSRomain Jobredeaux echo "tear_down log" >"${TEST_log}" 628*9e965d6fSRomain Jobredeaux fail "tear_down failure" 629*9e965d6fSRomain Jobredeaux } 630*9e965d6fSRomain Jobredeaux 631*9e965d6fSRomain Jobredeaux function test_foo() { 632*9e965d6fSRomain Jobredeaux echo "test_foo log" >"${TEST_log}" 633*9e965d6fSRomain Jobredeaux fail "Test failure" 634*9e965d6fSRomain Jobredeaux } 635*9e965d6fSRomain Jobredeaux 636*9e965d6fSRomain Jobredeaux run_suite "Failure in tear_down test" 637*9e965d6fSRomain Jobredeaux """)) 638*9e965d6fSRomain Jobredeaux 639*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 640*9e965d6fSRomain Jobredeaux 641*9e965d6fSRomain Jobredeaux result.assertNotSuccess("Failure in tear_down test", errors=1) 642*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_foo", "Test failure") 643*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_foo", "tear_down failure") 644*9e965d6fSRomain Jobredeaux result.assertXmlMessage('message="Test failure"') 645*9e965d6fSRomain Jobredeaux result.assertNotXmlMessage('message="tear_down failure"') 646*9e965d6fSRomain Jobredeaux result.assertXmlMessage("test_foo log") 647*9e965d6fSRomain Jobredeaux result.assertXmlMessage("tear_down log") 648*9e965d6fSRomain Jobredeaux result.assertLogMessage("Test failure") 649*9e965d6fSRomain Jobredeaux result.assertLogMessage("tear_down failure") 650*9e965d6fSRomain Jobredeaux result.assertLogMessage("test_foo log") 651*9e965d6fSRomain Jobredeaux result.assertLogMessage("tear_down log") 652*9e965d6fSRomain Jobredeaux 653*9e965d6fSRomain Jobredeaux def test_errexit_in_teardown_reports_failure(self): 654*9e965d6fSRomain Jobredeaux self.write_file( 655*9e965d6fSRomain Jobredeaux "thing.sh", 656*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 657*9e965d6fSRomain Jobredeaux set -euo pipefail 658*9e965d6fSRomain Jobredeaux 659*9e965d6fSRomain Jobredeaux function tear_down() { 660*9e965d6fSRomain Jobredeaux invalid_command 661*9e965d6fSRomain Jobredeaux } 662*9e965d6fSRomain Jobredeaux 663*9e965d6fSRomain Jobredeaux function test_foo() { 664*9e965d6fSRomain Jobredeaux : 665*9e965d6fSRomain Jobredeaux } 666*9e965d6fSRomain Jobredeaux 667*9e965d6fSRomain Jobredeaux run_suite "errexit in tear_down test" 668*9e965d6fSRomain Jobredeaux """)) 669*9e965d6fSRomain Jobredeaux 670*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 671*9e965d6fSRomain Jobredeaux 672*9e965d6fSRomain Jobredeaux result.assertNotSuccess("errexit in tear_down test") 673*9e965d6fSRomain Jobredeaux result.assertLogMessage("invalid_command: command not found") 674*9e965d6fSRomain Jobredeaux result.assertXmlMessage('message="No failure message"') 675*9e965d6fSRomain Jobredeaux result.assertXmlMessage("invalid_command: command not found") 676*9e965d6fSRomain Jobredeaux 677*9e965d6fSRomain Jobredeaux def test_fail_in_tear_down_after_errexit_reports_both_failures(self): 678*9e965d6fSRomain Jobredeaux self.write_file( 679*9e965d6fSRomain Jobredeaux "thing.sh", 680*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 681*9e965d6fSRomain Jobredeaux set -euo pipefail 682*9e965d6fSRomain Jobredeaux 683*9e965d6fSRomain Jobredeaux function tear_down() { 684*9e965d6fSRomain Jobredeaux echo "tear_down log" >"${TEST_log}" 685*9e965d6fSRomain Jobredeaux fail "tear_down failure" 686*9e965d6fSRomain Jobredeaux } 687*9e965d6fSRomain Jobredeaux 688*9e965d6fSRomain Jobredeaux function test_foo() { 689*9e965d6fSRomain Jobredeaux invalid_command 690*9e965d6fSRomain Jobredeaux } 691*9e965d6fSRomain Jobredeaux 692*9e965d6fSRomain Jobredeaux run_suite "fail after failure" 693*9e965d6fSRomain Jobredeaux """)) 694*9e965d6fSRomain Jobredeaux 695*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 696*9e965d6fSRomain Jobredeaux 697*9e965d6fSRomain Jobredeaux result.assertNotSuccess("fail after failure") 698*9e965d6fSRomain Jobredeaux result.assertTestFailed( 699*9e965d6fSRomain Jobredeaux "test_foo", 700*9e965d6fSRomain Jobredeaux "terminated because this command returned a non-zero status") 701*9e965d6fSRomain Jobredeaux result.assertTestFailed("test_foo", "tear_down failure") 702*9e965d6fSRomain Jobredeaux result.assertLogMessage("invalid_command: command not found") 703*9e965d6fSRomain Jobredeaux result.assertLogMessage("tear_down log") 704*9e965d6fSRomain Jobredeaux result.assertXmlMessage('message="No failure message"') 705*9e965d6fSRomain Jobredeaux result.assertXmlMessage("invalid_command: command not found") 706*9e965d6fSRomain Jobredeaux 707*9e965d6fSRomain Jobredeaux def test_errexit_in_tear_down_after_errexit_reports_both_failures(self): 708*9e965d6fSRomain Jobredeaux self.write_file( 709*9e965d6fSRomain Jobredeaux "thing.sh", 710*9e965d6fSRomain Jobredeaux textwrap.dedent(r""" 711*9e965d6fSRomain Jobredeaux set -euo pipefail 712*9e965d6fSRomain Jobredeaux 713*9e965d6fSRomain Jobredeaux function tear_down() { 714*9e965d6fSRomain Jobredeaux invalid_command_tear_down 715*9e965d6fSRomain Jobredeaux } 716*9e965d6fSRomain Jobredeaux 717*9e965d6fSRomain Jobredeaux function test_foo() { 718*9e965d6fSRomain Jobredeaux invalid_command_test 719*9e965d6fSRomain Jobredeaux } 720*9e965d6fSRomain Jobredeaux 721*9e965d6fSRomain Jobredeaux run_suite "fail after failure" 722*9e965d6fSRomain Jobredeaux """)) 723*9e965d6fSRomain Jobredeaux 724*9e965d6fSRomain Jobredeaux result = self.execute_test("thing.sh") 725*9e965d6fSRomain Jobredeaux 726*9e965d6fSRomain Jobredeaux result.assertNotSuccess("fail after failure") 727*9e965d6fSRomain Jobredeaux result.assertTestFailed( 728*9e965d6fSRomain Jobredeaux "test_foo", 729*9e965d6fSRomain Jobredeaux "terminated because this command returned a non-zero status") 730*9e965d6fSRomain Jobredeaux result.assertLogMessage("invalid_command_test: command not found") 731*9e965d6fSRomain Jobredeaux result.assertLogMessage("invalid_command_tear_down: command not found") 732*9e965d6fSRomain Jobredeaux result.assertXmlMessage('message="No failure message"') 733*9e965d6fSRomain Jobredeaux result.assertXmlMessage("invalid_command_test: command not found") 734*9e965d6fSRomain Jobredeaux result.assertXmlMessage("invalid_command_tear_down: command not found") 735*9e965d6fSRomain Jobredeaux 736*9e965d6fSRomain Jobredeaux 737*9e965d6fSRomain Jobredeauxif __name__ == "__main__": 738*9e965d6fSRomain Jobredeaux unittest.main() 739