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

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