OILS / test / common.sh View on Github | oilshell.org

271 lines, 173 significant
1# Usage:
2# source test/common.sh
3
4# Include guard.
5test -n "${__TEST_COMMON_SH:-}" && return
6readonly __TEST_COMMON_SH=1
7
8# Used by test/{gold,osh-usage,stateful,wild-runner}
9OSH=${OSH:-'bin/osh'}
10
11# For xargs -P in spec-runner.sh, wild-runner.sh.
12# If we have 2 cores or less (as on CI machines), use them all. Otherwise save
13# 1 for multitasking.
14nproc=$(nproc)
15MAX_PROCS=${MAX_PROCS:-"$(( nproc <= 2 ? nproc : nproc - 1 ))"}
16
17# Like PYTHONPATH, but for running R scripts
18# Fallback in build/dev-shell.sh
19readonly R_PATH=~/R
20
21log() {
22 echo "$@" 1>&2
23}
24
25die() {
26 log "$@"
27 exit 1
28}
29
30fail() {
31 echo 'TEST FAILURE ' "$@"
32 exit 1
33}
34
35assert() {
36 ### Must be run with errexit off
37
38 if ! test "$@"; then
39 # note: it's extremely weird that we use -1 and 0, but that seems to be how
40 # bash works.
41 die "${BASH_SOURCE[-1]}:${BASH_LINENO[0]}: assert '$@' failed"
42 fi
43}
44
45run-task-with-status() {
46 ### Run a process and write a file with status and time
47
48 # Used by test/{spec,wild}-runner.sh
49
50 local out_file=$1
51 shift
52
53 benchmarks/time_.py \
54 --tsv \
55 --output $out_file \
56 -- "$@" || true # suppress failure
57
58 # TODO: Use rows like this in YSH
59 # '{"status": %x, "wall_secs": %e, "user_secs": %U, "kernel_secs": %S}' \
60}
61
62list-test-funcs() {
63 ### Shell funcs that start with 'test-' are cases that will pass or fail
64 compgen -A function | egrep '^test-'
65}
66
67run-test-funcs() {
68 ### EXIT on failure
69
70 # for correct error handling, and to mutate $i
71 #
72 # Note: when I ran $t rather than $0 t, I seemed to tickle a bash lastpipe bug like this:
73 # https://www.spinics.net/lists/dash/msg01918.html
74 # I got a 127 exit code with no explanation.
75 shopt -s lastpipe
76
77 local i=0
78 local status=0
79
80 list-test-funcs | while read -r t; do
81 echo "*** Running $t"
82
83 set +o errexit
84 $0 $t
85 status=$?
86 set -o errexit
87
88 if test $status -ne 0; then
89 log "FAIL $t"
90 exit 1
91 fi
92
93 log "OK $t"
94 i=$((i + 1))
95 done
96
97 echo
98 echo "$0: $i tests passed."
99}
100
101run-test-bin() {
102 ### Run a binary in _bin/ and log output to a file in _test/
103
104 # Compare with run-test-funcs
105 local bin=$1
106 local working_dir=${2:-}
107 local log_base_dir=${3:-'_test'} # used by test/unit.sh
108
109 local rel_path=${bin#'_bin/'} # for C++ only
110 local log_dir="$log_base_dir/$(dirname $rel_path)"
111 mkdir -p $REPO_ROOT/$log_dir # abs path
112
113 local name=$(basename $bin)
114 export LLVM_PROFILE_FILE="$REPO_ROOT/$log_dir/$name.profraw"
115
116 local log=$log_dir/$name.log
117 log "RUN $bin > $log"
118
119 if test -n "$working_dir"; then
120 pushd $working_dir
121 fi
122
123 set +o errexit
124 $REPO_ROOT/$bin > $REPO_ROOT/$log 2>&1
125 local status=$?
126 set -o errexit
127
128 if test -n "$working_dir"; then
129 popd
130 fi
131
132 if test $status -eq 0; then
133 log 'OK'
134 else
135 echo
136 echo "=== $REPO_ROOT/$log ==="
137 echo
138 cat $REPO_ROOT/$log
139 echo
140 log "FAIL: $bin with code $status"
141 return 1
142 fi
143}
144
145run-one-test() {
146 local rel_path=$1
147 local compiler=${2:-cxx}
148 local variant=${3:-dbg}
149
150 local bin=_bin/$compiler-$variant/$rel_path
151
152 ninja $bin
153
154 run-test-bin $bin
155}
156
157run-test-func() {
158 ### Similar to above
159 local func_name=$1
160 local log=$2
161 shift 2
162
163 mkdir -p $(dirname $log)
164 log "RUN $0 $func_name > $log"
165
166 set +o errexit
167
168 # Reinvoke $0 so errexit is on in the function
169 $0 $func_name "$@" > $log 2>&1
170 local status=$?
171
172 set -o errexit
173
174 if test $status -eq 0; then
175 log 'OK'
176 else
177 echo
178 cat $log
179 echo
180 log "FAIL: $func_name with code $status"
181 return 1
182 fi
183}
184
185# A quick and dirty function to show logs
186run-other-suite-for-release() {
187 local suite_name=$1
188 local func_name=$2
189
190 local out="_tmp/suite-logs/${suite_name}.txt"
191 mkdir -p $(dirname $out)
192
193 echo
194 echo "*** Running test suite '$suite_name' ***"
195 echo
196
197 # I want to handle errors in $func_name while NOT changing its semantics.
198 # This requires a separate shell interpreter starts with $0, not just a
199 # separate process. I came up with this fix in gold/errexit-confusion.sh.
200
201 local status=0
202
203 set +o errexit
204 time $0 $func_name >$out 2>&1
205 status=$? # pipefail makes this work.
206 set -o errexit
207
208 if test $status -eq 0; then
209 echo
210 log "Test suite '$suite_name' ran without errors. Wrote '$out'"
211 else
212 echo
213 die "Test suite '$suite_name' failed (running $func_name, wrote '$out')"
214 fi
215}
216
217date-and-git-info() {
218 date
219 echo
220
221 if test -d .git; then
222 local branch
223 branch=$(git rev-parse --abbrev-ref HEAD)
224 local hash
225 hash=$(git rev-parse $branch)
226
227 echo "oil repo: $hash on branch $branch"
228 else
229 echo "(not running from git repository)"
230 fi
231 echo
232}
233
234html-head() {
235 PYTHONPATH=. doctools/html_head.py "$@"
236}
237
238escape-html() {
239 # Annoying that & has to be escaped in substitution!
240 sed -e 's|&|\&amp;|g' -e 's|<|\&lt;|g' -e 's|>|\&gt;|g' "$@"
241}
242
243export-osh-cpp() {
244 ### Export $OSH var to value in tarball root, repo root
245
246 # Also build it with shell script, or Ninja
247
248 local tar_root=${1:-} # e.g. _tmp/native-tar-test
249 local variant=${2:-opt}
250
251 if test -n "$tar_root" && test -d "$tar_root"; then
252 log "Using binary in $tar_root"
253
254 OIL_VERSION=$(head -n 1 oil-version.txt)
255 local repo_like=$tar_root/oils-for-unix-$OIL_VERSION
256
257 pushd $repo_like
258 _build/oils.sh '' $variant SKIP_REBUILD
259 osh=$PWD/_bin/cxx-$variant-sh/osh
260 popd
261
262 else
263 osh=_bin/cxx-$variant/osh
264 ninja $osh
265 fi
266
267 # So we can find it
268 export OSH=$osh
269 log "Exported OSH=$OSH"
270}
271