(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, which sand.flan now (embed ...)s rather than opening by
  ; path. An embed is read by the *checker*, relative to the file the form is
  ; written in, so it is a dependency of every build of sand.flan including the
  ; headless one — which reaches sand.flan through ../../ from programs/ and
  ; would otherwise find nothing at _build/default/brush.png.
  (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 package that declares a macro, which a package may not do yet.
  (glob_files programs/pkgs/mac/*)
  ; 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)))
