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