File: integration.bats

package info (click to toggle)
firebuild 0.8.5.1-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 2,552 kB
  • sloc: cpp: 13,693; ansic: 6,086; python: 2,313; sh: 261; makefile: 40; awk: 33
file content (549 lines) | stat: -rwxr-xr-x 22,595 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
#!/usr/bin/env bats

load test_helper

setup() {
  rm -rf test_cache_dir
}

@test "--help, --version" {
  result=$(./run-firebuild --help)
  echo "$result" | grep -q "in case of failure"
  result=$(./run-firebuild --version)
  echo "$result" | grep -q "Firebuild Inc."
  assert_streq "$(strip_stderr stderr)" ""
}

@test "empty-config" {
  result=$(./run-firebuild -c empty-config.conf -- ls integration.bats)
    assert_streq "$result" "integration.bats"
    assert_streq "$(strip_stderr stderr)" ""
}

@test "bash -c ls" {
  for i in 1 2; do
    result=$(./run-firebuild -D foo -o 'processes.dont_shortcut -= "ls"'  -- bash -c "ls integration.bats")
    assert_streq "$result" "integration.bats"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "bash -c grep ok" {
  for i in 1 2; do
    result=$(echo -e "foo\nok\nbar" | ./run-firebuild -- bash -c "grep ok")
    assert_streq "$result" "ok"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "regex env vars" {
  for i in 1 2; do
    result=$(! env FOO_BAR=1 ./run-firebuild -- bash -c "set" | grep FOO_BAR=1)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    result=$(! env FOO_BAR=1 ./run-firebuild -o 'env_vars.pass_through += "FOO"' -- bash -c "set" | grep FOO_BAR=1)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    result=$(! env FOO_BAR=1 ./run-firebuild -o 'env_vars.pass_through += "BAR"' -- bash -c "set" | grep FOO_BAR=1)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    result=$(env FOO_BAR=1 ./run-firebuild -o 'env_vars.pass_through += "FOO.*BAR"' -- bash -c "set" | grep FOO_BAR=1)
    assert_streq "$result" "FOO_BAR=1"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "debugging with trace markers and report generation" {
  for i in 1 2; do
    result=$(./run-firebuild -r -q -- bash -c "echo ok")
    assert_streq "$result" "ok"
    assert_streq "$(strip_stderr stderr)" ""

    # clean up before running the test
    rm -rf test_directory/ foo-dir/
    result=$(./run-firebuild -o 'processes.dont_shortcut -= "ls"' -C . --generate-report=firebuild-build-report.html -d all -i -- bash -c "ls integration.bats; bash -c ls | tee dirlist > /dev/null && ./test_file_ops")
    assert_streq "$result" "$(printf 'integration.bats\nFIREBUILD: Generated report: firebuild-build-report.html')"
  done
}

@test "bash exec chain" {
  for i in 1 2; do
    result=$(./run-firebuild -q -o 'processes.skip_cache -= "head"' -- bash -c "exec bash -c exec\\ bash\\ -c\\ head\\\\\ -n1\\\\\ integration.bats")
    assert_streq "$result" "#!/usr/bin/env bats"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "dash bash exec chain with allow-list" {
  for i in 1 2; do
    result=$(./run-firebuild -d cache -o 'processes.skip_cache -= "head"' -o 'processes.shortcut_allow_list += "bash"' -- dash -c "exec bash -c exec\\ bash\\ -c\\ head\\\\\ -n1\\\\\ integration.bats")
    assert_streq "$result" "#!/usr/bin/env bats"
    assert_streq "$(strip_stderr stderr)" ""
    assert_streq "$(grep -h 'original_executed_path' test_cache_dir/objs/*/*/*/%_directory_debug.json | sed 's|/.*/||' | uniq -c | sed 's/  */ /g')" ' 2 "original_executed_path": "bash",'
  done
}

@test "simple pipe" {
  for i in 1 2; do
    result=$(./run-firebuild -- bash -c 'seq 30000 | (sleep 0.01 && grep ^9)')
    assert_streq "$result" "$(seq 10000 | grep ^9)"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "yes | head" {
  for i in 1 2; do
    result=$(./run-firebuild -- bash -c 'yes 2> /dev/null | head -n 10000000 | tail -n 1')
    assert_streq "$result" "y"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "parallel make" {
  for i in 1 2; do
    # clean up previous run
    make -s -f test_parallel_make.Makefile clean
    result=$(./run-firebuild -d proc -- make -s -j8 -f test_parallel_make.Makefile)
    assert_streq "$result" "ok"
    if [ "$(uname)" = "Darwin" ]; then
      # this fails when xcrun_db is not populated, see https://github.com/firebuild/firebuild/issues/1297
      strip_stderr stderr | grep 'Command ' | grep -E -v '(set to be not shortcut|matches skip_cache|Shortcut parent .* instead)' | sed 's/^/# /' >&3
    else
      assert_streq "$(strip_stderr stderr | grep 'Command ' | grep -E -v '(set to be not shortcut|matches skip_cache|Shortcut parent .* instead)')" ""
    fi
    result=$(./run-firebuild -s)
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "orphan processes" {
  # Orphan process detection does not work in WSL1
  [ "$(uname)" = "Linux" ] && [ "$(systemd-detect-virt)" != "wsl" ] || skip
  for i in 1 2; do
    if ! with_valgrind; then
      result=$(./run-firebuild -o 'processes.dont_shortcut += "sleep"' -- bash -c 'for i in $(seq 10); do (sleep 0.3; ls integration.bats; false)& done; /bin/echo foo' | sort)
    else
      result=$(./run-firebuild -o 'processes.dont_shortcut += "sleep"' -- bash -c 'for i in $(seq 10); do (sleep 1; ls integration.bats; false)& done; /bin/echo foo' | sort)
    fi
    assert_streq "$result" "foo"
    if [ "$(uname)" = "Linux" ]; then
      assert_streq "$(strip_stderr stderr | uniq -c)" "     10 Orphan process has been killed by signal 15"
    else
      assert_streq "$(strip_stderr stderr)" ""
    fi

    result=$(./run-firebuild -- ./test_orphan)
    assert_streq "$result" ""
    if [ "$(uname)" = "Linux" ]; then
      # there may be one or two detected orphan processes
      assert_streq "$(strip_stderr stderr | uniq)" "Orphan process has been killed by signal 15"
    else
      assert_streq "$(strip_stderr stderr)" ""
    fi
  done
}

@test "system()" {
  for i in 1 2; do
    result=$(./run-firebuild -- ./test_system)
    assert_streq "$result" "ok"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "exec()" {
  for i in 1 2; do
    result=$(./run-firebuild -- ./test_exec)
    assert_streq "$result" "ok"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "closedir() inside an rm -r" {
  for i in 1 2; do
    result=$(./run-firebuild -- bash -c 'mkdir -p TeMp/FoO; rm -r TeMp')
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "file operations" {
  for i in 1 2; do
    # clean up before running the test
    rm -rf test_empty_1.txt test_empty_2.txt test_nonempty_1.txt test_nonempty_2.txt test_maybe_exists_1.txt test_directory/ test_exclusive.txt
    result=$(./run-firebuild -- ./test_file_ops)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""

    # clean up before running ./test_file_ops again
    rm -rf test_empty_1.txt test_empty_2.txt test_nonempty_1.txt test_nonempty_2.txt test_maybe_exists_1.txt test_directory/ test_exclusive.txt
    # Due to the "again" parameter the 1st level cannot be shortcut in
    # the first iteration, but the 2nd level (./test_file_ops_2) can,
    # it should fetch the cached entries stored in the previous run.
    result=$(./run-firebuild -- ./test_file_ops again)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""

    # test sendfile and friends
    result=$(./run-firebuild -- bash -c './test_sendfile < integration.bats > foo')
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    rm -f foo

    # The process can find a directory missing then can create a file in it due to the directory
    # having been created by an other parallel process
    rm -rf foo-dir
    result=$(./run-firebuild bash -c "(bash -c 'sh -c \"echo -x > foo-dir/bar1\" 2> /dev/null; sleep 0.2; sh -c \"echo x > foo-dir/bar2\" 2> /dev/null') & (sleep 0.1; [ $i == 2 ] || mkdir foo-dir); wait")
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""

    # Existing directories are not created again
    rm -rf foo-dir; mkdir foo-dir
    result=$(./run-firebuild bash -c "rm -rf foo-dir ; mkdir foo-dir")
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""

    # Two processes write a file in parallel
    # 1. One  executed process writer is an ancestor of the other.
    result=$(./run-firebuild -- bash -c '( (sleep 0.1; echo 1) >> foo)& sh -c "(sleep 0.11; echo 1) >> foo" ; wait')
    # 2. The writers have a common ancestor.
    result=$(./run-firebuild -- bash -c 'for i in 1 2; do sh -c "(sleep 0.1$i; echo 1) >> foo" & done; wait')
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    rm -f foo
  done
}

@test "randomness handling" {
  for i in 1 2; do
    result=$(./run-firebuild -o 'ignore_locations -= "/dev/urandom"' -- ./test_random)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
    result=$(./run-firebuild -- ./test_random again)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "waiting for a child" {
  for i in 1 2; do
    result=$(./run-firebuild -o 'processes.skip_cache -= "touch"' -- ./test_wait)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""

    # Due to the "again" parameter the outer process cannot be shortcut
    # in the first iteration, but the children can, they should fetch
    # the cached entries stored in the previous run.
    result=$(./run-firebuild -o 'processes.skip_cache -= "touch"' -- ./test_wait again)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "pthreads interception" {
  for i in 1 2; do
    result=$(./run-firebuild -i -- ./test_pthreads)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "err()" {
  stderr_expected=$'test_err: warn1: No such file or directory\ntest_err: warn2: Permission denied\ntest_err: err1: No such file or directory\natexit_handler'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_err || true)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" "$stderr_expected"
  done
}

@test "error()" {
  [ -x ./test_error ] || skip
  stderr_expected=$'./test_error: error1: No such file or directory\n./test_error: error2: Permission denied\n./test_error: error3: No such file or directory\natexit_handler'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_error || true)
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" "$stderr_expected"
  done
}

@test "env fixup" {
  for i in 1 2; do
    result=$(./run-firebuild -- ./test_env_fixup $LIBFIREBUILD)
    echo "$result" | grep -qx "BBB=bbb"
    if [ "$(uname)" = "Linux" ]; then
      echo "$result" | grep -qx "LD_PRELOAD=  LIBXXX.SO  $LIBFIREBUILD  LIBYYY.SO"
      strip_stderr stderr | grep -q "ERROR: ld.so: object 'LIBXXX.SO' from LD_PRELOAD cannot be preloaded"
      strip_stderr stderr | grep -q "ERROR: ld.so: object 'LIBYYY.SO' from LD_PRELOAD cannot be preloaded"
    else
      echo "$result" | grep -qx "DYLD_INSERT_LIBRARIES=.*libfirebuild.dylib"
    fi
    # Valgrind finds an error in fakeroot https://bugs.debian.org/983272
    if fakeroot ls > /dev/null 2>&1; then
      if ! with_valgrind; then
        result=$(fakeroot ./run-firebuild -o 'env_vars.pass_through += "LD_LIBRARY_PATH" ' -- id -u)
        assert_streq "$result" "0"
        result=$(./run-firebuild -- fakeroot id -u)
        assert_streq "$result" "0"
      fi
      result=$(./run-firebuild -- fakeroot id -u)
      assert_streq "$result" "0"
    fi
  done
}

@test "popen() a statically linked binary and a normal one" {
  [ -x ./test_static ] || skip
  ldd ./test_static 2>&1 | grep -Eq '(not a dynamic executable|statically linked)'

  for i in 1 2; do
    if [ -x ./test_static ]; then
      result=$(./run-firebuild -- ./test_cmd_popen ./test_static r)
      assert_streq "$result" "I am statically linked."
      assert_streq "$(strip_stderr stderr)" ""
    fi
    result=$(./run-firebuild -- bash -c "echo -e 'bar\nfoo\nbar' | ./test_cmd_popen 'grep foo' w")
    assert_streq "$result" "foo"
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "fork() + exec() a statically linked binary" {
  [ -x ./test_static ] || skip
  ldd ./test_static 2>&1 | grep -Eq '(not a dynamic executable|statically linked)'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_cmd_fork_exec ./test_static)
    assert_streq "$result" "I am statically linked."
    assert_streq "$(strip_stderr stderr)" ""
  done

  # command substitution with statically linked binary
  for i in 1 2; do
    result=$(timeout 10 ./run-firebuild -- sh -c 'echo `./test_static`')
    assert_streq "$result" "I am statically linked."
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "posix_spawn() a binary" {
  for i in 1 2; do
    result=$(./run-firebuild -r -q -- ./test_cmd_posix_spawn bash -c 'echo ok')
    assert_streq "$result" "ok"
    assert_streq "$(strip_stderr stderr)" ""
  done
  result=$(./run-firebuild -s | grep Hits)
  assert_streq "$result" "  Hits:             1 / 3 (33.33 %)"
  assert_streq "$(strip_stderr stderr)" ""
}

@test "posix_spawn() a static binary" {
  [ -x ./test_static ] || skip
  ldd ./test_static 2>&1 | grep -Eq '(not a dynamic executable|statically linked)'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_cmd_posix_spawn ./test_static)
    assert_streq "$result" "I am statically linked."
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "clone() a statically linked binary" {
  [ -x ./test_cmd_clone ] || skip
  ldd ./test_static 2>&1 | grep -Eq '(not a dynamic executable|statically linked)'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_cmd_clone ./test_static | sort |uniq -c)
    assert_streq "$result" "      2 I am statically linked."
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "system() a statically linked binary" {
  [ -x ./test_static ] || skip
  ldd ./test_static 2>&1 | grep -Eq '(not a dynamic executable|statically linked)'

  for i in 1 2; do
    result=$(./run-firebuild -- ./test_cmd_system './test_static 3' | tr '\n' ' ')
    assert_streq "$result" "I am statically linked. end "
    assert_streq "$(strip_stderr stderr)" ""
  done

  # test when the statically linked binary runs as an exec child
  for i in 1 2; do
    result=$(./run-firebuild -- ./test_cmd_system 'exec ./test_static 4' | tr '\n' ' ')
    assert_streq "$result" "I am statically linked. end "
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "pipe replaying" {
  result=$(./run-firebuild -o 'processes.skip_cache -= "echo"' -o 'max_inline_blob_size = 0' -o 'min_cpu_time = -1.0' -- echo foo)
  assert_streq "$result" "foo"
  assert_streq "$(strip_stderr stderr)" ""

  # Poison the cache, pretending that the output was "quux" instead of "foo".
  # (Bash supports wildcard redirection to a single matching file.)
  echo quux > test_cache_dir/blobs/*/*/*

  # Replaying the "echo foo" command now needs to print "quux".
  result=$(./run-firebuild -o 'processes.skip_cache -= "echo"' -- echo foo)
  assert_streq "$result" "quux"
  assert_streq "$(strip_stderr stderr)" ""
}

@test "parallel sleeps" {
  for i in 1 2; do
    # Valgrind ignores the limit bumped internally in firebuild
    # See: https://bugs.kde.org/show_bug.cgi?id=432508
    result=$(with_valgrind && ulimit -S -n 8000 ; ./run-firebuild -- bash -c 'for i in $(if [ "$(uname)" = "Darwin" ]; then seq 200; else seq 2000; fi); do sleep 1 & done;  wait')
    assert_streq "$result" ""
    assert_streq "$(strip_stderr stderr)" ""
  done
}

@test "max entry size" {
  result=$(./run-firebuild -d caching -o 'processes.skip_cache -= "dd"' dd if=/dev/zero of=foo bs=1M count=251)
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr | grep max_entry)" "FIREBUILD: Could not store blob in cache because it would exceed max_entry_size"
  result=$(./run-firebuild -d caching -o 'processes.skip_cache -= "dd"' -o 'max_entry_size = 0.0042' dd if=/dev/zero of=foo bs=4k count=1)
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr | grep max_entry)" "FIREBUILD: Could not store entry in cache because it would exceed max_entry_size"
  rm -f foo
}

@test "gc" {
  rm -f foo
  result=$(./run-firebuild -d cache -o 'max_inline_blob_size = 0' -- bash -c 'echo foo > foo')
  assert_streq "$result" ""
  if [ "$SKIP_GC_INVALID_ENTRIES_TEST" != 1 ]; then
    echo foo > test_cache_dir/blobs/invalid_blob_name
    echo bar > test_cache_dir/objs/invalid_obj_name
    ln -s invalid_blob_name test_cache_dir/blobs/unexpected_symlink
    ln -s invalid_obj_name test_cache_dir/objs/unexpected_symlink
  fi
  mkdir test_cache_dir/blobs/to_be_removed test_cache_dir/objs/to_be_removed
  touch test_cache_dir/objs/to_be_removed/%_directory_debug.json
  mkdir test_cache_dir/objs/many-entries
  for i in $(seq -w 30); do
    cp test_cache_dir/objs/?/??/*/??????????? test_cache_dir/objs/many-entries/12345678${i}+
  done
  # update cache size
  if [ "$(uname)" = "Linux" ]; then
    du="du --apparent-size -b"
  else
    du="stat -f %z"
  fi
  new_cache_size=$((cat test_cache_dir/size | tr '\n' ' ' ; printf '+ 30 *' ; ($du test_cache_dir/objs/?/??/*/??????????? | cut -f1) ) | bc)
  echo $new_cache_size > test_cache_dir/size

  result=$(./run-firebuild -o 'shortcut_tries = 18' -d cache --gc)
  assert_streq "$result" ""
  if [ "$SKIP_GC_INVALID_ENTRIES_TEST" != 1 ]; then
    assert_streq "$(grep 'invalid_.*_name' stderr | wc -l | sed 's/ *//g')" "2"
    assert_streq "$(strip_stderr stderr | grep -v 'invalid_.*_name' | grep -v 'type is unexpected')" "FIREBUILD ERROR: There are 8 bytes in the cache stored in files with unexpected name."
  fi
  # debug files are kept with "-d cache"
  [ -f test_cache_dir/objs/*/*/*/%_directory_debug.json ]
  # there is a non-directory debug json file as well
  debug_json=$(ls test_cache_dir/objs/*/*/*/*_debug.json | grep -v %_directory)
  [ -f "${debug_json}" ]
  [ -f test_cache_dir/blobs/*/*/*_debug.txt ]
  # empty dirs were removed from blobs/, the one in objs/ is kept due to %_directory_debug.json
  result=$(find test_cache_dir/blobs -name 'to_be_removed')
  assert_streq "$result" ""
  assert_streq "$(ls test_cache_dir/objs/many-entries/ | wc -l | sed 's/ *//g')" "18"

  result=$( ./run-firebuild --gc)
  assert_streq "$result" ""
  if [ "$SKIP_GC_INVALID_ENTRIES_TEST" != 1 ]; then
    assert_streq "$(grep 'invalid_.*_name' stderr | wc -l | sed 's/ *//g')" "2"
  fi
  assert_streq "$(strip_stderr stderr | grep -v 'invalid_.*_name' | grep -v ' unexpected')" ""
  # debug files are deleted without "-d cache"
  result=$(find test_cache_dir/ -name '*debug*')
  assert_streq "$result" ""
  result=$(find test_cache_dir/ -name 'to_be_removed')
  assert_streq "$result" ""

  result=$( ./run-firebuild -o 'max_cache_size = 0.00002' --gc)
  cp -r test_cache_dir test_cache_dir.bak
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr | grep -v 'invalid_.*_name' | grep -v ' unexpected')" ""
  rm -f foo
}

@test "cache-format" {
  result=$(./run-firebuild -d cache -- bash -c 'echo foo > foo')
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr)" ""
  assert_streq "$(cat test_cache_dir/cache-format)" "2"

  # older cache versions are OK (assuming they are handled)
  echo 0 > test_cache_dir/cache-format
  result=$(./run-firebuild -d cache -- bash -c 'echo foo > foo')
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr)" ""
  assert_streq "$(cat test_cache_dir/cache-format)" "0"

  # future cache versions prevent using the cache
  echo 9 > test_cache_dir/cache-format
  result=$(./run-firebuild -d cache -- bash -c 'echo foo > foo')
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr)" "FIREBUILD ERROR: Cache format version is not supported, not reading or writing the cache"

  # unknown cache versions prevent using the cache, too
  echo foo > test_cache_dir/cache-format
  result=$(./run-firebuild -d cache -- bash -c 'echo foo > foo')
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr)" "FIREBUILD ERROR: Cache format version is not supported, not reading or writing the cache"
}

@test "stats" {
  # Populate the cache
  result=$(./run-firebuild -z -- bash -c 'ls integration.bats')
  result=$(./run-firebuild -o 'processes.skip_cache = []' -- bash -c 'head -n1 integration.bats')
  # Use stats for current run
  result=$(./run-firebuild -s bash -c 'head -n1 integration.bats' | sed 's/  */ /g;s/seconds/ms/;s/[0-9-][0-9\.]* ms/N ms/;s/[0-9-][0-9\.]* kB/N kB/')
  assert_streq "$result" "$(printf '#!/usr/bin/env bats\n\nStatistics of current run:\n Hits: 1 / 1 (100.00 %%)\n Misses: 0\n Uncacheable: 0\n GC runs: 0\nNewly cached: N kB\nSaved CPU time: N ms\n')"
  # use --show-stats together with --gc ...
  result=$(./run-firebuild --gc --show-stats | sed 's/  */ /g;s/seconds/ms/;s/[0-9-][0-9\.]* ms/N ms/;s/[0-9-][0-9\.]* kB/N kB/')
  assert_streq "$result" "$(printf 'Statistics of stored cache:\n Hits: 1 / 4 (25.00 %%)\n Misses: 3\n Uncacheable: 1\n GC runs: 1\nCache size: N kB\nSaved CPU time: N ms\n')"
  # ... and without --gc
  result=$(./run-firebuild -s | sed 's/  */ /g;s/seconds/ms/;s/[0-9-][0-9\.]* ms/N ms/;s/[0-9-][0-9\.]* kB/N kB/')
  assert_streq "$result" "$(printf 'Statistics of stored cache:\n Hits: 1 / 4 (25.00 %%)\n Misses: 3\n Uncacheable: 1\n GC runs: 1\nCache size: N kB\nSaved CPU time: N ms\n')"
  result=$(./run-firebuild -z)
  assert_streq "$result" ""
  assert_streq "$(strip_stderr stderr)" ""
  result=$(./run-firebuild -s | sed 's/  */ /g;s/seconds/ms/;s/[0-9-][0-9\.]* ms/N ms/;s/[0-9-][0-9\.]* kB/N kB/')
  assert_streq "$result" "$(printf 'Statistics of stored cache:\n Hits: 0 / 0 (0.00 %%)\n Misses: 0\n Uncacheable: 0\n GC runs: 0\nCache size: N kB\nSaved CPU time: N ms\n')"
}

@test "clang pch" {
  # this test is very slow under valgrind
  ! with_valgrind || skip
  for cc1_param in "" "-cc1"; do
    if [ -z "${cc1_param}" ]; then
      xclang_param="-Xclang"
    else
      xclang_param=""
    fi
    for no_pch_param in "" "${xclang_param} -fno-pch-timestamp"; do
      for i in 1 2; do
        rm -f test_pch.h.pch test_pch.*.s
        result=$(./run-firebuild -- clang ${cc1_param} ${no_pch_param} $TEST_SOURCE_DIR/test_pch.h ${xclang_param} -emit-pch -o test_pch.h.pch)
        assert_streq "$result" ""
        assert_streq "$(strip_stderr stderr)" ""
        # not reproducible to check test_pch.h.pch's embedded timestamp again
        result=$(./run-firebuild -- clang ${cc1_param} -include-pch test_pch.h.pch $TEST_SOURCE_DIR/test_pch.c -o test_pch.$i.s)
        assert_streq "$result" ""
        assert_streq "$(strip_stderr stderr)" ""
        sleep 0.01
        touch test_pch.h
        rm -f test_pch.h.pch test_pch.*.s
      done
    done
  done
}