Annotated tests

19 smoke tests for dotlearn. Verifies dot + dotcols runtimes are bundled, exercises distance functions, then runs all three ML demos end-to-end.

cd dotlearn
./tests/test.sh
# All 19 tests passed.

1. Build & help

contains "build.sh runs"     "built dotlearn" "$out"
contains "no args -> usage"   "Usage:"        "$($DL 2>&1)"
contains "--help has --demo"  "--demo"        "$($DL --help)"
contains "--demos lists tree"   "tree"        "$($DL --demos)"
contains "--demos lists nb"     "nb"          "$($DL --demos)"
contains "--demos lists acquire" "acquire"    "$($DL --demos)"

Asserts: binary builds; CLI dispatcher works; auto-discovery finds all three demo dirs (tree, nb, acquire) on disk.

2. Inherits dot + dotcols

dotlearn bundles both lower layers. These tests prove their public functions are reachable.

eq "new + .field"   "7"   "$(run_inline 'BEGIN{N=new("plain"); .N.n=7; printf "%d\n", .N.n}')"
eq "Num via add()"  "30"  "$(run_inline 'BEGIN{N=new("num"); for(i=1;i<=5;i++) add(N,i*10,1); printf "%d\n", mid(N)}')"
eq "Data ingests"   "303" "$(run_inline '
  BEGIN { d = new("data"); FS = " *, *" }
  NR==1 { data_head(d, $0); next }
        { data_read(d, 1) }
  END   { printf "%d\n", .d.nrows }
  ' demos/tree/sample.csv)"

Asserts: from dot — new() + .field sugar work. From dotcols — add dispatches to num_add, and Data ingests CSV. All three layers wired correctly.

3. Distance (dist.awk)

out=$(run_inline '
  BEGIN { d = new("data"); FS = " *, *" }
  NR==1 { data_head(d, $0); next }
        { data_read(d, 1) }
  END   { print disty(d, .d.rows[1]) }
  ' demos/tree/sample.csv)
nonempty "disty(row 1)" "$out"

Asserts: after ingesting heart.c.csv, the first row has a finite distance-to-heaven across its y-columns. Doesn't lock to a specific number (depends on data); just verifies the function runs and returns something. Catches missing num_norm, broken sigil parsing, etc.

4. Demo: tree

out=$($DL --demo tree)
nonempty   "tree produces output"        "$out"
contains   "tree output is pred,actual"  "<50" "$out"
n=$(printf '%s\n' "$out" | wc -l | tr -d ' ')
[ "$n" -gt 50 ] && ok "tree output >50 rows" || bad ...

Asserts: the tree demo emits raw pred,actual lines containing the expected class label (<50); produces >50 rows (one per held-out test row). End-to-end: config → load → train tree → predict → print.

5. Demo: nb

out=$($DL --demo nb)
nonempty "nb produces output"      "$out"
contains "nb output is pred,actual" "," "$out"

Asserts: Naive Bayes demo emits comma-separated pred,actual lines. Same pipeline shape as tree, different model. If nb_train, nb_test, or the m-estimate smoothing breaks, the output disappears or crashes.

6. Demo: acquire

out=$($DL --demo acquire 2>&1)
contains "acquire prints RESULT block" "RESULT"   "$out"
contains "acquire reports labelled"    "labelled" "$out"

Asserts: the active learner runs the full pipeline (warm-start → acquire loop → tree on labelled rows → predict test set → score top-5 against percentiles) and reaches the === RESULT === block with a labelled : N line. Verifies all of: shuffle, data_clone, distx/disty, tree_train, tree_leaf, wins scorer.

7. Errors

out=$($DL --demo nonesuch 2>&1 || true)
contains "missing demo errors" "no such demo" "$out"

What this proves

Tests don't pin specific accuracies (those depend on shuffle seeds and dataset details). They verify the full pipeline runs and produces output of the expected shape. For accuracy regressions, eyeball ./dotlearn --demo tree | gawk -f tools/metrics.awk and compare against the published numbers in example.html.