File: bats-format-junit

package info (click to toggle)
bats 1.13.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,016 kB
  • sloc: sh: 4,351; makefile: 33; python: 28; xml: 3
file content (262 lines) | stat: -rwxr-xr-x 6,659 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/usr/bin/env bash
set -euo pipefail

# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/$BATS_LIBDIR/bats-core/formatter.bash"

BASE_PATH=.

while [[ "$#" -ne 0 ]]; do
  case "$1" in
  --base-path)
    shift
    normalize_base_path BASE_PATH "$1"
    ;;
  esac
  shift
done

init_suite() {
  suite_test_exec_time=0
  # since we have to print the suite header before its contents but we don't know the contents before the header,
  # we have to buffer the contents
  _suite_buffer=""
  test_result_state="" # declare for the first flush, when no test has been encountered
}

_buffer_log=
init_file() {
  file_count=0
  file_failures=0
  file_skipped=0
  file_exec_time=0
  test_exec_time=0
  name=""
  _buffer=""
  _buffer_log=""
  _system_out_log=""
  test_result_state="" # mark that no test has run in this file so far
}

host() {
  local hostname="${HOST:-}"
  [[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
  [[ -z "$hostname" ]] && hostname="$(uname -n)"
  [[ -z "$hostname" ]] && hostname="$(hostname -f)"

  echo "$hostname"
}

# convert $1 (time in milliseconds) to seconds
milliseconds_to_seconds() {
  # we cannot rely on having bc for this calculation
  full_seconds=$(($1 / 1000))
  remaining_milliseconds=$(($1 % 1000))
  if [[ $remaining_milliseconds -eq 0 ]]; then
    printf "%d" "$full_seconds"
  else
    printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
  fi
}

suite_header() {
  printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
}

file_header() {
  timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
  printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
    "$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
}

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

suite_footer() {
  printf "</testsuites>\n"
}

print_test_case() {
  if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
    # pass and no output can be shortened
    printf "    <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
  else
    printf "    <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
    if [[ -n "$_system_out_log" ]]; then
      printf "        <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
    fi
    if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
      printf "        <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
    fi
    if [[ "$test_result_state" == skipped ]]; then
      printf "        <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
    fi
    printf "    </testcase>\n"
  fi
}

xml_escape() {
  output=${1//&/\&amp;}
  output=${output//</\&lt;}
  output=${output//>/\&gt;}
  output=${output//'"'/\&quot;}
  output=${output//\'/\&#39;}
  # remove ANSI escape sequences (e.g. color codes, cursor movements)
  local CONTROL_CHAR=$'\033'
  local REGEX="$CONTROL_CHAR\[[0-9;]*[a-zA-Z]"
  while [[ "$output" =~ $REGEX ]]; do
      output=${output//${BASH_REMATCH[0]}/}
  done
  printf "%s" "$output"
}

suite_buffer() {
  local output
  output="$(
    "$@"
    printf "x"
  )" # use x marker to avoid losing trailing newlines
  _suite_buffer="${_suite_buffer}${output%x}"
}

suite_flush() {
  echo -n "${_suite_buffer}"
  _suite_buffer=""
}

buffer() {
  local output
  output="$(
    "$@"
    printf "x"
  )" # use x marker to avoid losing trailing newlines
  _buffer="${_buffer}${output%x}"
}

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

log() {
  if [[ -n "$_buffer_log" ]]; then
    _buffer_log="${_buffer_log}
$1"
  else
    _buffer_log="$1"
  fi
}

flush_log() {
  if [[ -n "$test_result_state" ]]; then
    buffer print_test_case
  fi
  _buffer_log=""
  _system_out_log=""
  test_result_state="" # Clean out result from last test. Retried tests will have multiple begin calls.
}

log_system_out() {
  if [[ -n "$_system_out_log" ]]; then
    _system_out_log="${_system_out_log}
$1"
  else
    _system_out_log="$1"
  fi
}

finish_file() {
  if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
    file_header
    printf "%s\n" "${_buffer}"
    file_footer
    class=''
    name=''
  fi
}

finish_suite() {
  flush_log
  suite_header
  suite_flush
  finish_file # must come after suite flush to not print the last file before the others
  suite_footer
}

bats_tap_stream_plan() { #  <number of tests>
  :
}

init_suite
trap finish_suite EXIT
trap '' INT

bats_tap_stream_begin() { # <test index> <test name>
  flush_log
  # set after flushing to avoid overriding name of test
  name="$2"
}

bats_tap_stream_ok() { # <test index> <test name>
  test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
  ((file_count += 1))
  test_result_state='ok'
  file_exec_time="$((file_exec_time + test_exec_time))"
  suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}

bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
  test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
  ((file_count += 1))
  ((file_skipped += 1))
  test_result_state='skipped'
  test_exec_time=0
  test_skip_message="$3"
}

bats_tap_stream_not_ok() { # <test index> <test name>
  if [[ -z "${name:-}" ]]; then
    # Can be called (after bats_tap_stream_plan) before anything else if the test fails in setup_suite
    bats_tap_stream_suite "setup_suite"
    bats_tap_stream_begin "$1" "$2"
  fi
  test_exec_time=${BATS_FORMATTER_TEST_DURATION:-0}
  ((file_count += 1))
  ((file_failures += 1))
  test_result_state=not_ok
  file_exec_time="$((file_exec_time + test_exec_time))"
  suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}

bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
  local comment="$1" scope="$2"
  case "$scope" in
  begin)
    # everything that happens between begin and [not] ok is FD3 output from the test
    log_system_out "$comment"
    ;;
  ok)
    # non failed tests can produce FD3 output
    log_system_out "$comment"
    ;;
  *)
    # everything else is considered error output
    log "$1"
    ;;
  esac
}

bats_tap_stream_suite() { # <file name>
  flush_log
  suite_buffer finish_file
  init_file
  class="${1/$BASE_PATH/}"
}

bats_tap_stream_unknown() { # <full line>
  :
}

bats_parse_internal_extended_tap