(tests
 (names test_flan test_acceptance test_reload test_agent test_session test_dev test_emacs test_repl test_cider)
 ; Explicit because test_sanitize lives in this directory and is not one of
 ; these: two stanzas in one directory have to say which modules are whose.
 ; watchdog is every binary's clock: a hanging test reports nothing, so each
 ; of these arms an alarm that turns "for ever" into a failing run.
 (modules test_flan test_acceptance test_reload test_agent test_session
          test_dev test_emacs test_repl test_cider watchdog)
 (libraries flan unix)
 ; The acceptance programs are part of the test corpus: if the reader, the
 ; parser or the checker regresses on them we want to know here, not at the CLI.
 (deps
  (file %{workspace_root}/calc-me.flan)
  (file %{workspace_root}/sand.flan)
  ; The brush sheet. sand.flan no longer embeds it — the front-end was cut back
  ; to what lisp/sand.lisp and sand.jank have — so nothing in the corpus reads
  ; it today. Kept as a dependency because it is still in the workspace and an
  ; embed is read by the *checker*, relative to the file the form is written
  ; in, which is the rule any program picking it up again would meet.
  (file %{workspace_root}/brush.png)
  ; The raylib bindings, because sand.flan and the FFI case import them and an
  ; import reads the directory at build time. sand.flan itself is above: the
  ; headless case imports it as a single-file package.
  (glob_files %{workspace_root}/vendor/raylib/*)
  ; The dev agent package: its Flan declarations and the C that implements them.
  (glob_files %{workspace_root}/vendor/agent/*)
  ; The EDN tokenizer, which programs/edn.flan imports.
  (glob_files %{workspace_root}/vendor/edn/*)
  ; The ported raylib examples. Only one of them has a headless acceptance
  ; case, but it imports its example as a package and that example imports
  ; examples/digits.flan, so the directory has to be here whole.
  (glob_files %{workspace_root}/examples/*)
  (glob_files programs/*.flan)
  ; The package tree the multi-level cases import: pkg-diamond reaches shape
  ; through area and draw, and pkg-cycle reaches a ring. Each directory is a
  ; package, so each comes whole — a glob per directory rather than one over
  ; programs/pkgs/*, because dune's glob does not descend.
  (glob_files programs/pkgs/shape/*)
  (glob_files programs/pkgs/area/*)
  (glob_files programs/pkgs/draw/*)
  (glob_files programs/pkgs/ring-a/*)
  (glob_files programs/pkgs/ring-b/*)
  (glob_files programs/pkgs/ring-c/*)
  ; The packages that declare macros: one whose macros a program calls
  ; qualified, and the two whose macros do not terminate — a ring, and one
  ; that never settles. Each is its own directory, so each needs its own glob.
  (glob_files programs/pkgs/mac/*)
  (glob_files programs/pkgs/macring/*)
  (glob_files programs/pkgs/macspin/*)
  ; The synthetic C header the importer's table reads. Committed rather than
  ; reached for on the machine: the raylib case needs raylib installed, at the
  ; right version, with a variable set, so it skips everywhere and covers
  ; nothing. This one does not move.
  (glob_files headers/*.h)
  ; The files programs/embed.flan bakes in. An embed reads them at *compile*
  ; time, so they are a dependency of the checker run and not of the program.
  (glob_files programs/assets/*)
  ; The reload primitive's host: a C main that dlopens what Build.shared made.
  (file reload_host.c)
  ; A shared object that is not a redefinition module, for the agent's refusal
  ; path. Its destructor is what proves the handle was closed rather than lost.
  (file noinstall.c)
  ; The other C main: flan_dev.c's two fixed limits, which no Flan program
  ; reaches, driven directly.
  (file dev_limits.c)
  ; test_dev runs the compiler itself: flan dev launches and owns a program.
  (file %{workspace_root}/bin/main.exe)
  ; The Emacs client, which test_emacs drives against a real daemon.
  (glob_files %{workspace_root}/emacs/*.el)
  ; The WASI host the wasm32 case runs its module under, when no wasmtime or
  ; wasmer is installed.
  (file wasm-run.mjs)))

; The web target, in its own stanza rather than in the table above because it
; is the one case whose toolchain is a separate install: emscripten, and a
; raylib archive built by vendor/raylib/build-web.sh. It probes for both and
; skips with the reason, so it is green on a machine that has neither.
(test
 (name test_web)
 (modules test_web)
 (libraries flan unix)
 (deps
  (glob_files programs/*.flan)
  (glob_files programs/assets/*)
  ; The raylib bindings and the ported example the raylib case builds. The
  ; example imports examples/digits.flan, so the directory comes whole.
  (glob_files %{workspace_root}/vendor/raylib/*)
  (glob_files %{workspace_root}/examples/*)
  ; sand.flan for the browser, with the sheet it embeds and the dev agent it
  ; imports — the agent's directory has to be whole, because the file that
  ; makes a web build possible is the one Build selects out of it.
  (file %{workspace_root}/sand.flan)
  (file %{workspace_root}/brush.png)
  (glob_files %{workspace_root}/vendor/agent/*)
  ; flan run --target=web is refused by the CLI, so the CLI has to be here.
  (file %{workspace_root}/bin/main.exe)))

; The corpus a second time under ASan and UBSan. Its own alias and not part of
; `dune test`: a sanitized build is a statically linked 1.8MB binary that takes
; tens of seconds to produce, so the sweep is minutes against the existing
; suite's seconds, and a test nobody will wait for is a test nobody runs.
;
;     dune build --root . @sanitize
; An executable plus a rule rather than a (test ...): a test stanza attaches
; to the @runtest alias and offers no way to be attached to another one, which
; is the whole point here.
(executable
 (name test_sanitize)
 (modules test_sanitize watchdog)
 (libraries flan unix))

(rule
 (alias sanitize)
 (deps
  test_sanitize.exe
  (file %{workspace_root}/calc-me.flan)
  (file %{workspace_root}/sand.flan)
  (file %{workspace_root}/brush.png)
  (glob_files %{workspace_root}/vendor/raylib/*)
  (glob_files %{workspace_root}/vendor/agent/*)
  (glob_files %{workspace_root}/vendor/edn/*)
  (glob_files %{workspace_root}/examples/*)
  (glob_files programs/*.flan)
  (glob_files programs/assets/*))
 (action (run ./test_sanitize.exe)))

; The corpus a third time, under Valgrind's memcheck. Its own alias for the
; same reason @sanitize has one, only more so: memcheck runs the program on a
; synthetic CPU, so the corpus is tens of minutes rather than seconds.
;
;     dune build --root . @valgrind
;
; Why a third sweep when @sanitize exists: ASan answers "is this address
; mine", and cannot answer "were these bytes ever written". That second
; question is MSan's, MSan needs every dependency instrumented and raylib
; settles it, and memcheck answers both while needing no instrumentation at
; all. NEXT.md asked for exactly this.
(executable
 (name test_valgrind)
 (modules test_valgrind watchdog)
 (libraries flan unix str))

(rule
 (alias valgrind)
 (deps
  test_valgrind.exe
  ; The suppression file, which is all reasons and no suppressions; its own
  ; header says why that is the finding rather than an oversight.
  (file valgrind.supp)
  (file %{workspace_root}/calc-me.flan)
  (file %{workspace_root}/sand.flan)
  (file %{workspace_root}/brush.png)
  (glob_files %{workspace_root}/vendor/raylib/*)
  (glob_files %{workspace_root}/vendor/agent/*)
  (glob_files %{workspace_root}/vendor/edn/*)
  (glob_files %{workspace_root}/examples/*)
  (glob_files programs/*.flan)
  (glob_files programs/assets/*)
  ; The package tree the multi-level cases import, as in the test stanza
  ; above: a glob per directory, because dune's glob does not descend.
  (glob_files programs/pkgs/shape/*)
  (glob_files programs/pkgs/area/*)
  (glob_files programs/pkgs/draw/*)
  (glob_files programs/pkgs/ring-a/*)
  (glob_files programs/pkgs/ring-b/*)
  (glob_files programs/pkgs/ring-c/*)
  ; And the macro-declaring packages, for the same reason.
  (glob_files programs/pkgs/mac/*)
  (glob_files programs/pkgs/macring/*)
  (glob_files programs/pkgs/macspin/*))
 (action (run ./test_valgrind.exe)))
