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

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