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

454 lines, 306 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 */invalid_switch_break.py)
251 expected_status=3
252 ;;
253 */invalid_format_strings.py)
254 expected_status=3
255 ;;
256 esac
257
258 if test $status -ne $expected_status; then
259 die "mycpp $ex: expected status $expected_status, got $status"
260 fi
261
262 done
263}
264
265test-control-flow-graph() {
266 local mycpp=_bin/shwrap/mycpp_main
267 ninja $mycpp
268 for ex in mycpp/examples/*.py; do
269 local data_dir=testdata/control-flow-graph/$(basename -s .py $ex)
270 if ! test -d $data_dir; then
271 # Only test some examples
272 continue
273 fi
274
275 banner "$ex"
276 local facts_dir=_tmp/mycpp-facts
277 mkdir -p $facts_dir
278 translate-example $ex --facts-out-dir $facts_dir
279
280 for fact_path in $data_dir/*.facts; do
281 local fact_file=$(basename $fact_path)
282
283 set +o errexit
284 diff -u $data_dir/$fact_file $facts_dir/$fact_file
285 local status=$?
286 set -o errexit
287
288 if test "$status" != 0; then
289 echo "FAIL $ex $fact_path"
290 return 1
291 fi
292
293 done
294 done
295}
296
297# TODO: Run with Clang UBSAN in CI as well
298readonly UBSAN_COMPILER=cxx
299
300unit() {
301 ### Run by test/cpp-unit.sh
302
303 # Run other unit tests, e.g. the GC tests
304
305 if can-compile-32-bit; then
306 run-unit-tests '' asan32+gcalways # ASAN on 32-bit
307 else
308 log ''
309 log "*** Can't compile 32-bit binaries (gcc-multilib g++-multilib needed on Debian)"
310 log ''
311 fi
312
313 # Run other tests with all variants
314
315 run-unit-tests $UBSAN_COMPILER ubsan
316
317 run-unit-tests '' asan
318 run-unit-tests '' asan+gcalways
319 run-unit-tests '' opt
320 run-unit-tests '' asan+bigint
321
322 bump-leak-heap-test
323}
324
325bump-leak-heap-test() {
326 for config in cxx-asan+bumpleak $UBSAN_COMPILER-ubsan+bumpleak; do
327 local bin=_bin/$config/mycpp/bump_leak_heap_test
328 ninja $bin
329 run-test-bin $bin
330 done
331}
332
333#
334# Translator
335#
336
337test-translator() {
338 ### Invoked by soil/worker.sh
339
340 examples-variant '' asan
341
342 # Test with more collections
343 examples-variant '' asan+gcalways
344
345 run-test-func test-invalid-examples _test/mycpp/test-invalid-examples.log
346
347 run-test-func test-control-flow-graph _test/mycpp/test-cfg-examples.log
348
349 # Runs tests in cxx-asan variant, and benchmarks in cxx-opt variant
350 if ! ninja mycpp-logs-equal; then
351 log 'FAIL mycpp-logs-equal'
352 return 1
353 fi
354}
355
356soil-run() {
357 set +o errexit
358 $0 test-translator
359 local status=$?
360 set -o errexit
361
362 return $status
363}
364
365unit-test-coverage() {
366 ### Invoked by Soil
367
368 local bin=_bin/clang-coverage+bumpleak/mycpp/bump_leak_heap_test
369 ninja $bin
370 run-test-bin $bin
371
372 run-unit-tests clang coverage
373
374 local out_dir=_test/clang-coverage/mycpp
375 test/coverage.sh html-report $out_dir \
376 clang-coverage/mycpp clang-coverage+bumpleak/mycpp
377}
378
379examples-coverage() {
380 ### Invoked by Soil
381
382 examples-variant clang coverage
383
384 local out_dir=_test/clang-coverage/mycpp/examples
385 test/coverage.sh html-report $out_dir clang-coverage/mycpp/examples
386}
387
388files() {
389 wc -l mycpp/*.py | sort -n
390}
391
392copy-golden() {
393 local dir=testdata/mycpp
394 mkdir -p $dir
395 cp -v _gen/bin/oils_for_unix.mycpp.cc $dir
396}
397
398compare-golden() {
399 local -a files=(
400 testdata/mycpp/oils_for_unix.mycpp.cc _gen/bin/oils_for_unix.mycpp.cc
401 )
402
403 wc -l "${files[@]}"
404 echo
405
406 if diff -u "${files[@]}"; then
407 echo EQUAL
408 else
409 echo 'NOT EQUAL'
410 fi
411}
412
413compare-souffle() {
414 # Show less rooting in examples
415 ninja _bin/cxx-asan/mycpp/examples/test_iterators.mycpp{,-souffle}
416
417 local -a files=(
418 _gen/mycpp/examples/test_iterators.mycpp{,-souffle}.cc
419 )
420 if diff -u "${files[@]}"; then
421 die 'Should not be equal'
422 fi
423
424 ninja _bin/cxx-asan/mycpp-souffle/osh
425 local -a files=(
426 _gen/bin/oils_for_unix.mycpp{,-souffle}.cc
427 )
428 if diff -u "${files[@]}"; then
429 die 'Should not be equal'
430 fi
431}
432
433const-pass() {
434 python3 mycpp/const_pass.py "$@"
435}
436
437str-hash-demo() {
438 local file=benchmarks/testdata/configure-coreutils
439
440 # We have ~1600 strings - let's say it doubles
441 #
442 # 1613 unique strings -> 34 collisions of length 2, 1 of length 3
443 # 2618 unique strings -> 108 collisions of length 2, 6 of length 3
444 #
445 # So yes 3 is good
446
447 for n in 180 1800 3600 18000; do
448 echo "=== Number of strings $n ==="
449 head -n $n $file | const-pass
450 echo
451 done
452}
453
454task-five "$@"