From 0a151afb65b8f11820b0ebdee12e86eb0f24d2ce Mon Sep 17 00:00:00 2001 From: Sam Stephenson Date: Mon, 28 Oct 2013 11:51:17 -0500 Subject: [PATCH] Log the full stack trace when a test fails --- libexec/bats-exec-test | 71 ++++++++++++++++++++++++-- test/bats.bats | 8 +++ test/fixtures/bats/failing_helper.bats | 6 +++ test/fixtures/bats/test_helper.bash | 4 ++ 4 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/bats/failing_helper.bats diff --git a/libexec/bats-exec-test b/libexec/bats-exec-test index fe65997..bbbd411 100755 --- a/libexec/bats-exec-test +++ b/libexec/bats-exec-test @@ -82,10 +82,75 @@ bats_test_function() { BATS_TEST_NAMES["${#BATS_TEST_NAMES[@]}"]="$test_name" } -bats_debug_trap() { +bats_capture_stack_trace() { if [ "$BASH_SOURCE" != "$1" ]; then + BATS_STACK_TRACE=() BATS_LINE_NUMBER="$BATS_LINE_NUMBER_" BATS_LINE_NUMBER_="$2" + + local test_pattern=" $BATS_TEST_NAME $BATS_TEST_SOURCE" + local index=0 + local frame + + while frame="$(caller "$index")"; do + BATS_STACK_TRACE[$index]="$frame" + if [[ "$frame" = *"$test_pattern" ]]; then + break + else + ((index++)) + fi + done + fi +} + +bats_print_stack_trace() { + local index=1 + local count="${#BATS_STACK_TRACE[@]}" + local line + + for frame in "${BATS_STACK_TRACE[@]}"; do + if [ $index -eq 1 ]; then + line="$BATS_LINE_NUMBER" + echo -n "# (" + else + line="$(bats_frame_line "$frame")" + echo -n "# " + fi + + if [ $index -eq $count ]; then + echo "in test file $BATS_TEST_FILENAME, line $line)" + else + local fn="$(bats_frame_function "$frame")" + local filename="$(bats_frame_filename "$frame")" + echo "from function \`$fn' in file $filename, line $line," + fi + + ((index++)) + done +} + +bats_frame_line() { + local frame="$1" + local line="${frame%% *}" + echo "$line" +} + +bats_frame_function() { + local frame="$1" + local rest="${frame#* }" + local fn="${rest%% *}" + echo "$fn" +} + +bats_frame_filename() { + local frame="$1" + local rest="${frame#* }" + local filename="${rest#* }" + + if [ "$filename" = "$BATS_TEST_SOURCE" ]; then + echo "$BATS_TEST_FILENAME" + else + echo "$filename" fi } @@ -114,7 +179,7 @@ bats_exit_trap() { if [ -z "$BATS_TEST_COMPLETED" ]; then echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 - echo "# (in test file $BATS_TEST_FILENAME, line $BATS_LINE_NUMBER)" >&3 + bats_print_stack_trace >&3 sed -e "s/^/# /" < "$BATS_OUT" >&3 status=1 else @@ -148,7 +213,7 @@ bats_perform_test() { BATS_TEST_COMPLETED="" BATS_ERROR_LINE="" - trap "bats_debug_trap \"\$BASH_SOURCE\" \$LINENO" debug + trap "bats_capture_stack_trace \"\$BASH_SOURCE\" \$LINENO" debug trap "bats_error_trap" err trap "bats_teardown_trap" exit "$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 diff --git a/test/bats.bats b/test/bats.bats index b408839..5d4d648 100755 --- a/test/bats.bats +++ b/test/bats.bats @@ -150,3 +150,11 @@ fixtures bats [ "$tap_output" != "$pretty_output" ] } + +@test "failing helper function logs the test case's line number" { + run bats "$FIXTURE_ROOT/failing_helper.bats" + [ $status -eq 1 ] + [ "${lines[1]}" = "not ok 1 failing helper function" ] + [ "${lines[2]}" = "# (from function \`failing_helper' in file $FIXTURE_ROOT/test_helper.bash, line 6," ] + [ "${lines[3]}" = "# in test file $FIXTURE_ROOT/failing_helper.bats, line 5)" ] +} diff --git a/test/fixtures/bats/failing_helper.bats b/test/fixtures/bats/failing_helper.bats new file mode 100644 index 0000000..fb5726f --- /dev/null +++ b/test/fixtures/bats/failing_helper.bats @@ -0,0 +1,6 @@ +load "test_helper" + +@test "failing helper function" { + true + failing_helper +} diff --git a/test/fixtures/bats/test_helper.bash b/test/fixtures/bats/test_helper.bash index c68e50e..530d034 100644 --- a/test/fixtures/bats/test_helper.bash +++ b/test/fixtures/bats/test_helper.bash @@ -1,3 +1,7 @@ help_me() { true } + +failing_helper() { + false +}