OILS / mycpp / TEST.sh View on Github | oils.pub

448 lines, 300 significant
1#!/usr/bin/env bash
2#
3# Run tests in this directory.
4#
5# Usage:
6# mycpp/TEST.sh <function name>
7
8: ${LIB_OSH=stdlib/osh}
9source $LIB_OSH/bash-strict.sh
10source $LIB_OSH/task-five.sh
11
12REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
13
14source build/common.sh
15source build/ninja-rules-cpp.sh
16source devtools/common.sh
17source test/common.sh # run-test-bin, can-compile-32-bit
18
19# in case binaries weren't built
20shopt -s failglob
21
22# Will be needed to pass ASAN leak detector? Or only do this for the main binary?
23# export OILS_GC_ON_EXIT=1
24
25examples-variant() {
26 ### Run all examples using a variant -- STATS only
27
28 local compiler=${1:-cxx}
29 local variant=${2:-asan+gcalways}
30 local do_benchmark=${3:-}
31
32 banner "$0 examples-variant $compiler $variant"
33
34 ninja mycpp-examples-$compiler-$variant
35
36 local num_tests=0
37 local num_failed=0
38 local status=0
39
40 local log_dir=_test/$compiler-$variant/mycpp/examples
41 mkdir -p $log_dir
42
43 for b in _bin/$compiler-$variant/mycpp/examples/*; do
44 case $b in
45 *.pea) # for now, don't run pea_hello.pea, it fails on purpose
46 continue
47 ;;
48 *.stripped) # just run the unstripped binary
49 continue
50 ;;
51 esac
52
53 local prefix="$log_dir/$(basename $b)"
54
55 case $variant in
56 coverage)
57 export LLVM_PROFILE_FILE=$prefix.profraw
58 ;;
59 esac
60
61 local log="${prefix}${do_benchmark}.log"
62
63 log "RUN $b > $log"
64
65 local test_name=$(basename $b)
66 if test -n "$do_benchmark" && [[ $test_name == test_* ]]; then
67 log "Skipping $test_name in benchmark mode"
68 continue
69 fi
70
71 set +o errexit
72 BENCHMARK="$do_benchmark" $b >$log 2>&1
73 status=$?
74 set -o errexit
75
76 if test "$status" -eq 0; then
77 log 'OK'
78 else
79 log "FAIL with status $?"
80 log ''
81 #return $status
82 num_failed=$((num_failed + 1))
83 fi
84
85 num_tests=$((num_tests + 1))
86 done
87
88 log ''
89 log "$num_failed of $num_tests tests failed"
90 log ''
91
92 if test $num_failed -ne 0; then
93 echo "FAIL: Expected no failures, got $num_failed"
94 return 1
95 fi
96
97 return 0
98}
99
100#
101# 3 Variants x {test, benchmark}
102#
103
104ex-gcalways() {
105 local compiler=${1:-}
106 examples-variant "$compiler" asan+gcalways
107}
108
109# TOO SLOW to run. It's garbage collecting all the time.
110ex-gcalways-bench() {
111 local compiler=${1:-}
112 examples-variant "$compiler" asan+gcalways '.BENCHMARK'
113}
114
115ex-asan() {
116 local compiler=${1:-}
117 examples-variant "$compiler" asan
118}
119
120# 2 of 18 tests failed: cartesian, parse
121# So it does not catch the 10 segfaults that 'asan+gcalways' catches with a few
122# iterations!
123ex-asan-bench() {
124 local compiler=${1:-}
125 examples-variant "$compiler" asan '.BENCHMARK'
126}
127
128# PASS! Under both clang and GCC.
129ex-ubsan() {
130 local compiler=${1:-}
131 examples-variant "$compiler" ubsan
132}
133
134# same as ASAN: 2 of 18
135ex-ubsan-bench() {
136 local compiler=${1:-}
137 examples-variant "$compiler" ubsan '.BENCHMARK'
138}
139
140# PASS!
141ex-opt() {
142 local compiler=${1:-}
143 examples-variant "$compiler" opt
144}
145
146# 2 of 18 tests failed
147ex-opt-bench() {
148 local compiler=${1:-}
149 examples-variant "$compiler" opt '.BENCHMARK'
150}
151
152#
153# Unit Tests
154#
155
156run-unit-tests() {
157
158 local compiler=${1:-cxx}
159 local variant=${2:-asan+gcalways}
160
161 log ''
162 log "$0 run-unit-tests $compiler $variant"
163 log ''
164
165 ninja mycpp-unit-$compiler-$variant
166
167 local -a binaries=(_bin/$compiler-$variant/mycpp/*)
168
169 # Add these files if they exist in the variant
170 if test -d _bin/$compiler-$variant/mycpp/demo; then
171 binaries+=(_bin/$compiler-$variant/mycpp/demo/*)
172 fi
173
174 for b in "${binaries[@]}"; do
175 if ! test -f $b; then
176 continue
177 fi
178
179 local prefix=${b//_bin/_test/}
180 local log=$prefix.log
181 mkdir -p $(dirname $log)
182
183 local asan_options=''
184 case $b in
185 # leaks with malloc
186 */demo/hash_table|*/demo/target_lang|*/demo/gc_header|*/small_str_test)
187 asan_options='detect_leaks=0'
188 ;;
189 esac
190
191 ASAN_OPTIONS="$asan_options" run-test-bin $b
192
193 done
194}
195
196#
197# Test failures
198#
199
200translate-example() {
201 local ex=$1
202 shift
203
204 local mycpp=_bin/shwrap/mycpp_main
205 $mycpp '.:pyext' '' _tmp/mycpp-invalid $ex "$@"
206}
207
208test-invalid-examples() {
209 local mycpp=_bin/shwrap/mycpp_main
210 ninja $mycpp
211 for ex in mycpp/examples/invalid_*; do
212
213 banner "$ex"
214
215 set +o errexit
216 translate-example $ex
217 local status=$?
218 set -o errexit
219
220 local expected_status=1
221
222 case $ex in
223 */invalid_condition.py)
224 expected_status=8
225 ;;
226 */invalid_other.py)
227 expected_status=6
228 ;;
229 */invalid_default_args.py)
230 expected_status=5
231 ;;
232 */invalid_try_else.py)
233 expected_status=3
234 ;;
235 */invalid_except.py)
236 expected_status=4
237 ;;
238 */invalid_global.py)
239 expected_status=2
240 ;;
241 */invalid_python.py)
242 expected_status=5
243 ;;
244 */invalid_switch.py)
245 expected_status=5
246 ;;
247 */invalid_partialtype.py)
248 expected_status=2
249 ;;
250 esac
251
252 if test $status -ne $expected_status; then
253 die "mycpp $ex: expected status $expected_status, got $status"
254 fi
255
256 done
257}
258
259test-control-flow-graph() {
260 local mycpp=_bin/shwrap/mycpp_main
261 ninja $mycpp
262 for ex in mycpp/examples/*.py; do
263 local data_dir=testdata/control-flow-graph/$(basename -s .py $ex)
264 if ! test -d $data_dir; then
265 # Only test some examples
266 continue
267 fi
268
269 banner "$ex"
270 local facts_dir=_tmp/mycpp-facts
271 mkdir -p $facts_dir
272 translate-example $ex --facts-out-dir $facts_dir
273
274 for fact_path in $data_dir/*.facts; do
275 local fact_file=$(basename $fact_path)
276
277 set +o errexit
278 diff -u $data_dir/$fact_file $facts_dir/$fact_file
279 local status=$?
280 set -o errexit
281
282 if test "$status" != 0; then
283 echo "FAIL $ex $fact_path"
284 return 1
285 fi
286
287 done
288 done
289}
290
291# TODO: Run with Clang UBSAN in CI as well
292readonly UBSAN_COMPILER=cxx
293
294unit() {
295 ### Run by test/cpp-unit.sh
296
297 # Run other unit tests, e.g. the GC tests
298
299 if can-compile-32-bit; then
300 run-unit-tests '' asan32+gcalways # ASAN on 32-bit
301 else
302 log ''
303 log "*** Can't compile 32-bit binaries (gcc-multilib g++-multilib needed on Debian)"
304 log ''
305 fi
306
307 # Run other tests with all variants
308
309 run-unit-tests $UBSAN_COMPILER ubsan
310
311 run-unit-tests '' asan
312 run-unit-tests '' asan+gcalways
313 run-unit-tests '' opt
314 run-unit-tests '' asan+bigint
315
316 bump-leak-heap-test
317}
318
319bump-leak-heap-test() {
320 for config in cxx-asan+bumpleak $UBSAN_COMPILER-ubsan+bumpleak; do
321 local bin=_bin/$config/mycpp/bump_leak_heap_test
322 ninja $bin
323 run-test-bin $bin
324 done
325}
326
327#
328# Translator
329#
330
331test-translator() {
332 ### Invoked by soil/worker.sh
333
334 examples-variant '' asan
335
336 # Test with more collections
337 examples-variant '' asan+gcalways
338
339 run-test-func test-invalid-examples _test/mycpp/test-invalid-examples.log
340
341 run-test-func test-control-flow-graph _test/mycpp/test-cfg-examples.log
342
343 # Runs tests in cxx-asan variant, and benchmarks in cxx-opt variant
344 if ! ninja mycpp-logs-equal; then
345 log 'FAIL mycpp-logs-equal'
346 return 1
347 fi
348}
349
350soil-run() {
351 set +o errexit
352 $0 test-translator
353 local status=$?
354 set -o errexit
355
356 return $status
357}
358
359unit-test-coverage() {
360 ### Invoked by Soil
361
362 local bin=_bin/clang-coverage+bumpleak/mycpp/bump_leak_heap_test
363 ninja $bin
364 run-test-bin $bin
365
366 run-unit-tests clang coverage
367
368 local out_dir=_test/clang-coverage/mycpp
369 test/coverage.sh html-report $out_dir \
370 clang-coverage/mycpp clang-coverage+bumpleak/mycpp
371}
372
373examples-coverage() {
374 ### Invoked by Soil
375
376 examples-variant clang coverage
377
378 local out_dir=_test/clang-coverage/mycpp/examples
379 test/coverage.sh html-report $out_dir clang-coverage/mycpp/examples
380}
381
382files() {
383 wc -l mycpp/*.py | sort -n
384}
385
386copy-golden() {
387 local dir=testdata/mycpp
388 mkdir -p $dir
389 cp -v _gen/bin/oils_for_unix.mycpp.cc $dir
390}
391
392compare-golden() {
393 local -a files=(
394 testdata/mycpp/oils_for_unix.mycpp.cc _gen/bin/oils_for_unix.mycpp.cc
395 )
396
397 wc -l "${files[@]}"
398 echo
399
400 if diff -u "${files[@]}"; then
401 echo EQUAL
402 else
403 echo 'NOT EQUAL'
404 fi
405}
406
407compare-souffle() {
408 # Show less rooting in examples
409 ninja _bin/cxx-asan/mycpp/examples/test_iterators.mycpp{,-souffle}
410
411 local -a files=(
412 _gen/mycpp/examples/test_iterators.mycpp{,-souffle}.cc
413 )
414 if diff -u "${files[@]}"; then
415 die 'Should not be equal'
416 fi
417
418 ninja _bin/cxx-asan/mycpp-souffle/osh
419 local -a files=(
420 _gen/bin/oils_for_unix.mycpp{,-souffle}.cc
421 )
422 if diff -u "${files[@]}"; then
423 die 'Should not be equal'
424 fi
425}
426
427const-pass() {
428 python3 mycpp/const_pass.py "$@"
429}
430
431str-hash-demo() {
432 local file=benchmarks/testdata/configure-coreutils
433
434 # We have ~1600 strings - let's say it doubles
435 #
436 # 1613 unique strings -> 34 collisions of length 2, 1 of length 3
437 # 2618 unique strings -> 108 collisions of length 2, 6 of length 3
438 #
439 # So yes 3 is good
440
441 for n in 180 1800 3600 18000; do
442 echo "=== Number of strings $n ==="
443 head -n $n $file | const-pass
444 echo
445 done
446}
447
448task-five "$@"