diff options
author | Junio C Hamano <gitster@pobox.com> | 2022-06-07 21:10:57 (GMT) |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2022-06-07 21:10:57 (GMT) |
commit | fc5a070f591c2114a6e55b4fe3395f79e93699bb (patch) | |
tree | 8d6a907caca61f52958a3271d544b8e17eaa2742 /t | |
parent | 2da81d1efb0166e1cec7a8582b837994dde6225b (diff) | |
parent | 3069f2a6f4c38e7e599067d2e4a8e31b4f53e2d3 (diff) | |
download | git-fc5a070f591c2114a6e55b4fe3395f79e93699bb.zip git-fc5a070f591c2114a6e55b4fe3395f79e93699bb.tar.gz git-fc5a070f591c2114a6e55b4fe3395f79e93699bb.tar.bz2 |
Merge branch 'js/ci-github-workflow-markup'
Update the GitHub workflow support to make it quicker to get to the
failing test.
* js/ci-github-workflow-markup:
ci: call `finalize_test_case_output` a little later
ci(github): mention where the full logs can be found
ci: use `--github-workflow-markup` in the GitHub workflow
ci(github): avoid printing test case preamble twice
ci(github): skip the logs of the successful test cases
ci: optionally mark up output in the GitHub workflow
ci/run-build-and-tests: add some structure to the GitHub workflow output
ci: make it easier to find failed tests' logs in the GitHub workflow
ci/run-build-and-tests: take a more high-level view
test(junit): avoid line feeds in XML attributes
tests: refactor --write-junit-xml code
ci: fix code style
Diffstat (limited to 't')
-rw-r--r-- | t/test-lib-functions.sh | 6 | ||||
-rw-r--r-- | t/test-lib-github-workflow-markup.sh | 56 | ||||
-rw-r--r-- | t/test-lib-junit.sh | 132 | ||||
-rw-r--r-- | t/test-lib.sh | 128 |
4 files changed, 212 insertions, 110 deletions
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 50970d3..6da7273 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -795,7 +795,7 @@ test_verify_prereq () { } test_expect_failure () { - test_start_ + test_start_ "$@" test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= test "$#" = 2 || BUG "not 2 or 3 parameters to test-expect-failure" @@ -803,6 +803,7 @@ test_expect_failure () { export test_prereq if ! test_skip "$@" then + test -n "$test_skip_test_preamble" || say >&3 "checking known breakage of $TEST_NUMBER.$test_count '$1': $2" if test_run_ "$2" expecting_failure then @@ -815,7 +816,7 @@ test_expect_failure () { } test_expect_success () { - test_start_ + test_start_ "$@" test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= test "$#" = 2 || BUG "not 2 or 3 parameters to test-expect-success" @@ -823,6 +824,7 @@ test_expect_success () { export test_prereq if ! test_skip "$@" then + test -n "$test_skip_test_preamble" || say >&3 "expecting success of $TEST_NUMBER.$test_count '$1': $2" if test_run_ "$2" then diff --git a/t/test-lib-github-workflow-markup.sh b/t/test-lib-github-workflow-markup.sh new file mode 100644 index 0000000..9c5339c --- /dev/null +++ b/t/test-lib-github-workflow-markup.sh @@ -0,0 +1,56 @@ +# Library of functions to mark up test scripts' output suitable for +# pretty-printing it in GitHub workflows. +# +# Copyright (c) 2022 Johannes Schindelin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/ . +# +# The idea is for `test-lib.sh` to source this file when run in GitHub +# workflows; these functions will then override (empty) functions +# that are are called at the appropriate times during the test runs. + +test_skip_test_preamble=t + +start_test_output () { + test -n "$GIT_TEST_TEE_OUTPUT_FILE" || + die "--github-workflow-markup requires --verbose-log" + github_markup_output="${GIT_TEST_TEE_OUTPUT_FILE%.out}.markup" + >$github_markup_output + GIT_TEST_TEE_OFFSET=0 +} + +# No need to override start_test_case_output + +finalize_test_case_output () { + test_case_result=$1 + shift + case "$test_case_result" in + failure) + echo >>$github_markup_output "::error::failed: $this_test.$test_count $1" + ;; + fixed) + echo >>$github_markup_output "::notice::fixed: $this_test.$test_count $1" + ;; + ok) + # Exit without printing the "ok" tests + return + ;; + esac + echo >>$github_markup_output "::group::$test_case_result: $this_test.$test_count $*" + test-tool >>$github_markup_output path-utils skip-n-bytes \ + "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET + echo >>$github_markup_output "::endgroup::" +} + +# No need to override finalize_test_output diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh new file mode 100644 index 0000000..c959183 --- /dev/null +++ b/t/test-lib-junit.sh @@ -0,0 +1,132 @@ +# Library of functions to format test scripts' output in JUnit XML +# format, to support Git's test suite result to be presented in an +# easily digestible way on Azure Pipelines. +# +# Copyright (c) 2022 Johannes Schindelin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/ . +# +# The idea is for `test-lib.sh` to source this file when the user asks +# for JUnit XML; these functions will then override (empty) functions +# that are are called at the appropriate times during the test runs. + +start_test_output () { + junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out" + mkdir -p "$junit_xml_dir" + junit_xml_base=${1##*/} + junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml" + junit_attrs="name=\"${junit_xml_base%.sh}\"" + junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \ + date +%Y-%m-%dT%H:%M:%S)\"" + write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>" + junit_suite_start=$(test-tool date getnanos) + if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + GIT_TEST_TEE_OFFSET=0 + fi +} + +start_test_case_output () { + junit_start=$(test-tool date getnanos) +} + +finalize_test_case_output () { + test_case_result=$1 + shift + case "$test_case_result" in + ok) + set "$*" + ;; + failure) + junit_insert="<failure message=\"not ok $test_count -" + junit_insert="$junit_insert $(xml_attr_encode --no-lf "$1")\">" + junit_insert="$junit_insert $(xml_attr_encode \ + "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + test-tool path-utils skip-n-bytes \ + "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET + else + printf '%s\n' "$@" | sed 1d + fi)")" + junit_insert="$junit_insert</failure>" + if test -n "$GIT_TEST_TEE_OUTPUT_FILE" + then + junit_insert="$junit_insert<system-err>$(xml_attr_encode \ + "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>" + fi + set "$1" " $junit_insert" + ;; + fixed) + set "$* (breakage fixed)" + ;; + broken) + set "$* (known breakage)" + ;; + skip) + message="$(xml_attr_encode --no-lf "$skipped_reason")" + set "$1" " <skipped message=\"$message\" />" + ;; + esac + + junit_attrs="name=\"$(xml_attr_encode --no-lf "$this_test.$test_count $1")\"" + shift + junit_attrs="$junit_attrs classname=\"$this_test\"" + junit_attrs="$junit_attrs time=\"$(test-tool \ + date getnanos $junit_start)\"" + write_junit_xml "$(printf '%s\n' \ + " <testcase $junit_attrs>" "$@" " </testcase>")" + junit_have_testcase=t +} + +finalize_test_output () { + if test -n "$junit_xml_path" + then + test -n "$junit_have_testcase" || { + junit_start=$(test-tool date getnanos) + write_junit_xml_testcase "all tests skipped" + } + + # adjust the overall time + junit_time=$(test-tool date getnanos $junit_suite_start) + sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \ + -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \ + -e '/^ *<\/testsuite/d' \ + <"$junit_xml_path" >"$junit_xml_path.new" + mv "$junit_xml_path.new" "$junit_xml_path" + + write_junit_xml " </testsuite>" "</testsuites>" + write_junit_xml= + fi +} + +write_junit_xml () { + case "$1" in + --truncate) + >"$junit_xml_path" + junit_have_testcase= + shift + ;; + esac + printf '%s\n' "$@" >>"$junit_xml_path" +} + +xml_attr_encode () { + if test "x$1" = "x--no-lf" + then + shift + printf '%s' "$*" | test-tool xml-encode + else + printf '%s\n' "$@" | test-tool xml-encode + fi +} diff --git a/t/test-lib.sh b/t/test-lib.sh index 8ba5ca1..736c644 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -137,6 +137,12 @@ mark_option_requires_arg () { store_arg_to=$2 } +# These functions can be overridden e.g. to output JUnit XML +start_test_output () { :; } +start_test_case_output () { :; } +finalize_test_case_output () { :; } +finalize_test_output () { :; } + parse_option () { local opt="$1" @@ -196,7 +202,10 @@ parse_option () { tee=t ;; --write-junit-xml) - write_junit_xml=t + . "$TEST_DIRECTORY/test-lib-junit.sh" + ;; + --github-workflow-markup) + . "$TEST_DIRECTORY/test-lib-github-workflow-markup.sh" ;; --stress) stress=t ;; @@ -664,7 +673,7 @@ exec 6<&0 exec 7>&2 _error_exit () { - finalize_junit_xml + finalize_test_output GIT_EXIT_OK=t exit 1 } @@ -774,35 +783,13 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP # the test_expect_* functions instead. test_ok_ () { - if test -n "$write_junit_xml" - then - write_junit_xml_testcase "$*" - fi test_success=$(($test_success + 1)) say_color "" "ok $test_count - $@" + finalize_test_case_output ok "$@" } test_failure_ () { - if test -n "$write_junit_xml" - then - junit_insert="<failure message=\"not ok $test_count -" - junit_insert="$junit_insert $(xml_attr_encode "$1")\">" - junit_insert="$junit_insert $(xml_attr_encode \ - "$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE" - then - test-tool path-utils skip-n-bytes \ - "$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET - else - printf '%s\n' "$@" | sed 1d - fi)")" - junit_insert="$junit_insert</failure>" - if test -n "$GIT_TEST_TEE_OUTPUT_FILE" - then - junit_insert="$junit_insert<system-err>$(xml_attr_encode \ - "$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>" - fi - write_junit_xml_testcase "$1" " $junit_insert" - fi + failure_label=$1 test_failure=$(($test_failure + 1)) say_color error "not ok $test_count - $1" shift @@ -812,24 +799,19 @@ test_failure_ () { say_color error "1..$test_count" _error_exit fi + finalize_test_case_output failure "$failure_label" "$@" } test_known_broken_ok_ () { - if test -n "$write_junit_xml" - then - write_junit_xml_testcase "$* (breakage fixed)" - fi test_fixed=$(($test_fixed+1)) say_color error "ok $test_count - $@ # TODO known breakage vanished" + finalize_test_case_output fixed "$@" } test_known_broken_failure_ () { - if test -n "$write_junit_xml" - then - write_junit_xml_testcase "$* (known breakage)" - fi test_broken=$(($test_broken+1)) say_color warn "not ok $test_count - $@ # TODO known breakage" + finalize_test_case_output broken "$@" } test_debug () { @@ -1104,10 +1086,7 @@ test_start_ () { test_count=$(($test_count+1)) maybe_setup_verbose maybe_setup_valgrind - if test -n "$write_junit_xml" - then - junit_start=$(test-tool date getnanos) - fi + start_test_case_output "$@" } test_finish_ () { @@ -1158,15 +1137,10 @@ test_skip () { case "$to_skip" in t) - if test -n "$write_junit_xml" - then - message="$(xml_attr_encode "$skipped_reason")" - write_junit_xml_testcase "$1" \ - " <skipped message=\"$message\" />" - fi say_color skip "ok $test_count # skip $1 ($skipped_reason)" : true + finalize_test_case_output skip "$@" ;; *) false @@ -1179,53 +1153,6 @@ test_at_end_hook_ () { : } -write_junit_xml () { - case "$1" in - --truncate) - >"$junit_xml_path" - junit_have_testcase= - shift - ;; - esac - printf '%s\n' "$@" >>"$junit_xml_path" -} - -xml_attr_encode () { - printf '%s\n' "$@" | test-tool xml-encode -} - -write_junit_xml_testcase () { - junit_attrs="name=\"$(xml_attr_encode "$this_test.$test_count $1")\"" - shift - junit_attrs="$junit_attrs classname=\"$this_test\"" - junit_attrs="$junit_attrs time=\"$(test-tool \ - date getnanos $junit_start)\"" - write_junit_xml "$(printf '%s\n' \ - " <testcase $junit_attrs>" "$@" " </testcase>")" - junit_have_testcase=t -} - -finalize_junit_xml () { - if test -n "$write_junit_xml" && test -n "$junit_xml_path" - then - test -n "$junit_have_testcase" || { - junit_start=$(test-tool date getnanos) - write_junit_xml_testcase "all tests skipped" - } - - # adjust the overall time - junit_time=$(test-tool date getnanos $junit_suite_start) - sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \ - -e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \ - -e '/^ *<\/testsuite/d' \ - <"$junit_xml_path" >"$junit_xml_path.new" - mv "$junit_xml_path.new" "$junit_xml_path" - - write_junit_xml " </testsuite>" "</testsuites>" - write_junit_xml= - fi -} - test_atexit_cleanup=: test_atexit_handler () { # In a succeeding test script 'test_atexit_handler' is invoked @@ -1248,7 +1175,7 @@ test_done () { # removed, so the commands can access pidfiles and socket files. test_atexit_handler - finalize_junit_xml + finalize_test_output if test -z "$HARNESS_ACTIVE" then @@ -1539,22 +1466,7 @@ fi # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$TRASH_DIRECTORY" || exit 1 -if test -n "$write_junit_xml" -then - junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out" - mkdir -p "$junit_xml_dir" - junit_xml_base=${0##*/} - junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml" - junit_attrs="name=\"${junit_xml_base%.sh}\"" - junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \ - date +%Y-%m-%dT%H:%M:%S)\"" - write_junit_xml --truncate "<testsuites>" " <testsuite $junit_attrs>" - junit_suite_start=$(test-tool date getnanos) - if test -n "$GIT_TEST_TEE_OUTPUT_FILE" - then - GIT_TEST_TEE_OFFSET=0 - fi -fi +start_test_output "$0" # Convenience # A regexp to match 5 and 35 hexdigits |