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