Skip to content
This repository has been archived by the owner on Apr 29, 2021. It is now read-only.

Add a JUnit output formatter compatible with Jenkins #161

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
21 changes: 16 additions & 5 deletions libexec/bats
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ help() {
echo " -h, --help Display this help message"
echo " -p, --pretty Show results in pretty format (default for terminals)"
echo " -t, --tap Show results in TAP format"
echo " -x, --extended Show results in extended TAP format"
echo " -u, --junit Show results in JUnit format, hudson compatible"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more explicit and future-proof to name them -x, --extended-tap and -j, --junit .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to say Jenkins or Jenkins/Hudson here as most people use Jenkins. Additionally it's a personal name so caps are grammatically correct.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove what's after the comma entirely. Whether it's for Hudson, Jenkins or whatever, it's a JUnit format, and it's all that matters :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we can do so.
I have specified Hudson here because I noticed difference with the JUnit specifications.

echo " -v, --version Display the version number"
echo
echo " For more information, see https://github.com/sstephenson/bats"
Expand Down Expand Up @@ -52,9 +54,9 @@ expand_path() {
} || echo "$1"
}

BATS_LIBEXEC="$(abs_dirname "$0")"
export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")"
export BATS_CWD="$(abs_dirname .)"
BATS_LIBEXEC="${BATS_LIBEXEC-$(abs_dirname "$0")}"
export BATS_PREFIX="${BATS_PREFIX-$(abs_dirname "$BATS_LIBEXEC")}"
export BATS_CWD="${BATS_CWD-$(abs_dirname .)}"
export PATH="$BATS_LIBEXEC:$PATH"

options=()
Expand Down Expand Up @@ -93,12 +95,19 @@ for option in "${options[@]}"; do
"c" | "count" )
count_flag="-c"
;;
"x" | "extended" )
pretty=""
extended_syntax_flag="-x"
;;
"t" | "tap" )
pretty=""
;;
"p" | "pretty" )
pretty="1"
;;
"u" | "junit" )
junit="1"
;;
* )
usage >&2
exit 1
Expand Down Expand Up @@ -130,11 +139,13 @@ else
command="bats-exec-suite"
fi

if [ -n "$pretty" ]; then
if [ -n "$junit" ]; then
extended_syntax_flag="-x"
formatter="bats-format-junit"
elif [ -n "$pretty" ]; then
extended_syntax_flag="-x"
formatter="bats-format-tap-stream"
else
extended_syntax_flag=""
formatter="cat"
fi

Expand Down
3 changes: 3 additions & 0 deletions libexec/bats-exec-suite
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ status=0
offset=0
for filename in "$@"; do
index=0
if test -n "$extended_syntax_flag"; then
echo suite bats.$(basename "$filename"| sed -e 's/\.bats//')
fi
{
IFS= read -r # 1..n
while IFS= read -r line; do
Expand Down
11 changes: 9 additions & 2 deletions libexec/bats-exec-test
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ skip() {

bats_test_begin() {
BATS_TEST_DESCRIPTION="$1"
BATS_TEST_START_TIME=${SECONDS}
if [ -n "$BATS_EXTENDED_SYNTAX" ]; then
echo "begin $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3
fi
Expand Down Expand Up @@ -252,14 +253,20 @@ bats_exit_trap() {
fi
fi

if [ -n "${skipped}" ] || [ -z "$BATS_EXTENDED_SYNTAX" ]; then
BATS_TEST_TIME=""
else
BATS_TEST_TIME=" in "$((SECONDS - BATS_TEST_START_TIME))"sec"
fi

if [ -z "$BATS_TEST_COMPLETED" ] || [ -z "$BATS_TEARDOWN_COMPLETED" ]; then
echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3
echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION${BATS_TEST_TIME}" >&3
bats_print_stack_trace "${BATS_ERROR_STACK_TRACE[@]}" >&3
bats_print_failed_command "${BATS_ERROR_STACK_TRACE[${#BATS_ERROR_STACK_TRACE[@]}-1]}" "$BATS_ERROR_STATUS" >&3
sed -e "s/^/# /" < "$BATS_OUT" >&3
status=1
else
echo "ok ${BATS_TEST_NUMBER}${skipped} ${BATS_TEST_DESCRIPTION}" >&3
echo "ok ${BATS_TEST_NUMBER}${skipped} ${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
status=0
fi

Expand Down
132 changes: 132 additions & 0 deletions libexec/bats-format-junit
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env bash
set -e

IFS= read -r header

index=0

init_suite() {
count=0
failures=0
skipped=0
suitetest_exec_time=0
name=""
_buffer=""
}

header() {
printf "\
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<testsuite name=\"${class}\" tests=\"${count}\" failures=\"${failures}\" skip=\"${skipped}\" time=\"${suitetest_exec_time}\">\n"
}

footer() {
printf "</testsuite>\n"
}


pass() {
echo " <testcase classname=\"${class}\" name=\"${name}\" time=\"${test_exec_time}\"/>\n"
}

fail() {
printf "\
<testcase classname=\"${class}\" name=\"${name}\" time=\"${test_exec_time}\">
<failure><![CDATA["

echo -n "${1}"

printf "]]></failure>
</testcase>\n"
}

skip() {
echo "\
<testcase classname=\"${class}\" name=\"${name}\" time=\"${test_exec_time}\">
<skipped>$1</skipped>
</testcase>\n"
}

buffer() {
_buffer="${_buffer}$("$@")"
}

flush() {
echo "${_buffer}"
_buffer=""
}

_buffer_log=""
log() {
_buffer_log="${_buffer_log}
$1"
}

flush_log() {
if [[ -n "${_buffer_log}" ]]; then
buffer fail "${_buffer_log}"
_buffer_log=""
fi
}

finish_suite() {
[[ ${count} -gt 0 ]] && {
(
flush_log
header
flush
footer
) > "TestReport-${class-case}.xml"
}
init_suite
}

trap finish_suite EXIT

while IFS= read -r line; do
case "$line" in
"suite "*)
flush_log
finish_suite
suite_expr="suite (.*)"
if [[ "$line" =~ $suite_expr ]]; then
class="${BASH_REMATCH[1]}"
fi
;;
"begin "* )
flush_log
let index+=1
name="${line#* $index }"
;;
"ok "* )
let count+=1
expr_ok="ok $index .* in ([0-9]+)sec"
expr_skip="ok $index # skip (.*)"
if [[ "$line" =~ $expr_skip ]]; then
let skipped+=1
test_exec_time=0
buffer skip "${BASH_REMATCH[1]}"
elif [[ "$line" =~ $expr_ok ]]; then
test_exec_time="${BASH_REMATCH[1]}"
suitetest_exec_time=$((suitetest_exec_time + test_exec_time))
buffer pass
else
log "Wrong output format: ${line}"
let failures+=1
fi
;;
"not ok "* )
let count+=1
let failures+=1
expr_notok="not ok $index .* in ([0-9]+)sec"
if [[ "$line" =~ $expr_notok ]]; then
test_exec_time="${BASH_REMATCH[1]}"
suitetest_exec_time=$((suitetest_exec_time + test_exec_time))
fi
;;
"# "* )
log "${line:2}"
;;
esac
done

1 change: 1 addition & 0 deletions libexec/bats-format-tap-stream
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ begin() {

pass() {
go_to_column 0
set_color 2
printf " ✓ %s" "$name"
advance
}
Expand Down
5 changes: 3 additions & 2 deletions test/bats.bats
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ fixtures bats
}

@test "failing test file outside of BATS_CWD" {
unset BATS_LIBEXEC BATS_PREFIX BATS_CWD
cd "$TMP"
run bats "$FIXTURE_ROOT/failing.bats"
[ $status -eq 1 ]
Expand Down Expand Up @@ -222,9 +223,9 @@ fixtures bats
run bats-exec-test -x "$FIXTURE_ROOT/failing_and_passing.bats"
[ $status -eq 1 ]
[ "${lines[1]}" = 'begin 1 a failing test' ]
[ "${lines[2]}" = 'not ok 1 a failing test' ]
[ "${lines[2]}" = 'not ok 1 a failing test in 0sec' ]
[ "${lines[5]}" = 'begin 2 a passing test' ]
[ "${lines[6]}" = 'ok 2 a passing test' ]
[ "${lines[6]}" = 'ok 2 a passing test in 0sec' ]
}

@test "pretty and tap formats" {
Expand Down
14 changes: 8 additions & 6 deletions test/suite.bats
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ fixtures suite
FLUNK=1 run bats-exec-suite -x "$FIXTURE_ROOT/multiple/"*.bats
[ $status -eq 1 ]
[ "${lines[0]}" = "1..3" ]
[ "${lines[1]}" = "begin 1 truth" ]
[ "${lines[2]}" = "ok 1 truth" ]
[ "${lines[3]}" = "begin 2 more truth" ]
[ "${lines[4]}" = "ok 2 more truth" ]
[ "${lines[5]}" = "begin 3 quasi-truth" ]
[ "${lines[6]}" = "not ok 3 quasi-truth" ]
[ "${lines[1]}" = "suite bats.a" ]
[ "${lines[2]}" = "begin 1 truth" ]
[ "${lines[3]}" = "ok 1 truth in 0sec" ]
[ "${lines[4]}" = "suite bats.b" ]
[ "${lines[5]}" = "begin 2 more truth" ]
[ "${lines[6]}" = "ok 2 more truth in 0sec" ]
[ "${lines[7]}" = "begin 3 quasi-truth" ]
[ "${lines[8]}" = "not ok 3 quasi-truth in 0sec" ]
}