xref: /aosp_15_r20/external/bazelbuild-rules_android/test/bashunit/unittest_utils.sh (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# 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# Support for unittest.bash
16
17#### Set up the test environment.
18
19set -euo pipefail
20
21cat_jvm_log () {
22  if [[ "$log_content" =~ \
23      "(error code:".*", error message: '".*"', log file: '"(.*)"')" ]]; then
24    echo >&2
25    echo "Content of ${BASH_REMATCH[1]}:" >&2
26    cat "${BASH_REMATCH[1]}" >&2
27  fi
28}
29
30# Print message in "$1" then exit with status "$2"
31die () {
32  # second argument is optional, defaulting to 1
33  local status_code=${2:-1}
34  # Stop capturing stdout/stderr, and dump captured output
35  if [[ "$CAPTURED_STD_ERR" -ne 0 || "$CAPTURED_STD_OUT" -ne 0 ]]; then
36    restore_outputs
37    if [[ "$CAPTURED_STD_OUT" -ne 0 ]]; then
38        cat "${TEST_TMPDIR}/captured.out"
39        CAPTURED_STD_OUT=0
40    fi
41    if [[ "$CAPTURED_STD_ERR" -ne 0 ]]; then
42        cat "${TEST_TMPDIR}/captured.err" 1>&2
43        cat_jvm_log "$(cat "${TEST_TMPDIR}/captured.err")"
44        CAPTURED_STD_ERR=0
45    fi
46  fi
47
48  if [[ -n "${1-}" ]] ; then
49      echo "$1" 1>&2
50  fi
51  if [[ -n "${BASH-}" ]]; then
52    local caller_n=0
53    while [[ $caller_n -lt 4 ]] && \
54        caller_out=$(caller $caller_n 2>/dev/null); do
55      test $caller_n -eq 0 && echo "CALLER stack (max 4):"
56      echo "  $caller_out"
57      let caller_n=caller_n+1
58    done 1>&2
59  fi
60  if [[ -n "${status_code}" && "${status_code}" -ne 0 ]]; then
61      exit "$status_code"
62  else
63      exit 1
64  fi
65}
66
67# Print message in "$1" then record that a non-fatal error occurred in
68# ERROR_COUNT
69ERROR_COUNT="${ERROR_COUNT:-0}"
70error () {
71  if [[ -n "$1" ]] ; then
72      echo "$1" 1>&2
73  fi
74  ERROR_COUNT=$(($ERROR_COUNT + 1))
75}
76
77# Die if "$1" != "$2", print $3 as death reason
78check_eq () {
79  [[ "$1" = "$2" ]] || die "Check failed: '$1' == '$2' ${3:+ ($3)}"
80}
81
82# Die if "$1" == "$2", print $3 as death reason
83check_ne () {
84  [[ "$1" != "$2" ]] || die "Check failed: '$1' != '$2' ${3:+ ($3)}"
85}
86
87# The structure of the following if statements is such that if '[[' fails
88# (e.g., a non-number was passed in) then the check will fail.
89
90# Die if "$1" > "$2", print $3 as death reason
91check_le () {
92  [[ "$1" -gt "$2" ]] || die "Check failed: '$1' <= '$2' ${3:+ ($3)}"
93}
94
95# Die if "$1" >= "$2", print $3 as death reason
96check_lt () {
97  [[ "$1" -lt "$2" ]] || die "Check failed: '$1' < '$2' ${3:+ ($3)}"
98}
99
100# Die if "$1" < "$2", print $3 as death reason
101check_ge () {
102  [[ "$1" -ge "$2" ]] || die "Check failed: '$1' >= '$2' ${3:+ ($3)}"
103}
104
105# Die if "$1" <= "$2", print $3 as death reason
106check_gt () {
107  [[ "$1" -gt "$2" ]] || die "Check failed: '$1' > '$2' ${3:+ ($3)}"
108}
109
110# Die if $2 !~ $1; print $3 as death reason
111check_match ()
112{
113  expr match "$2" "$1" >/dev/null || \
114    die "Check failed: '$2' does not match regex '$1' ${3:+ ($3)}"
115}
116
117# Run command "$1" at exit. Like "trap" but multiple atexits don't
118# overwrite each other. Will break if someone does call trap
119# directly. So, don't do that.
120ATEXIT="${ATEXIT-}"
121atexit () {
122  if [[ -z "$ATEXIT" ]]; then
123      ATEXIT="$1"
124  else
125      ATEXIT="$1 ; $ATEXIT"
126  fi
127  trap "$ATEXIT" EXIT
128}
129
130## TEST_TMPDIR
131if [[ -z "${TEST_TMPDIR:-}" ]]; then
132  export TEST_TMPDIR="$(mktemp -d ${TMPDIR:-/tmp}/bazel-test.XXXXXXXX)"
133fi
134if [[ ! -e "${TEST_TMPDIR}" ]]; then
135  mkdir -p -m 0700 "${TEST_TMPDIR}"
136  # Clean TEST_TMPDIR on exit
137  atexit "rm -fr ${TEST_TMPDIR}"
138fi
139
140# Functions to compare the actual output of a test to the expected
141# (golden) output.
142#
143# Usage:
144#   capture_test_stdout
145#   ... do something ...
146#   diff_test_stdout "$TEST_SRCDIR/path/to/golden.out"
147
148# Redirect a file descriptor to a file.
149CAPTURED_STD_OUT="${CAPTURED_STD_OUT:-0}"
150CAPTURED_STD_ERR="${CAPTURED_STD_ERR:-0}"
151
152capture_test_stdout () {
153  exec 3>&1 # Save stdout as fd 3
154  exec 4>"${TEST_TMPDIR}/captured.out"
155  exec 1>&4
156  CAPTURED_STD_OUT=1
157}
158
159capture_test_stderr () {
160  exec 6>&2 # Save stderr as fd 6
161  exec 7>"${TEST_TMPDIR}/captured.err"
162  exec 2>&7
163  CAPTURED_STD_ERR=1
164}
165
166# Force XML_OUTPUT_FILE to an existing path
167if [[ -z "${XML_OUTPUT_FILE:-}" ]]; then
168  XML_OUTPUT_FILE=${TEST_TMPDIR}/output.xml
169fi
170
171# Functions to provide easy access to external repository outputs in the sibling
172# repository layout.
173#
174# Usage:
175#   bin_dir <repository name>
176#   genfiles_dir <repository name>
177#   testlogs_dir <repository name>
178
179testlogs_dir() {
180  echo $(bazel info bazel-testlogs | sed "s|bazel-out|bazel-out/$1|")
181}
182