Annotated tests

The smoke-test suite for dottests/test.sh. 26 tests over 7 sections: build, help, preprocessor, demo runner, errors, runtime, printer. Each test is a tiny usage snippet you can lift verbatim.

Run yourself:

cd dot
./tests/test.sh
# All 26 tests passed.

1. Build — the binary assembles and is executable

Sanity gate: if the build step itself fails, nothing else is meaningful.

out=$(./build.sh 2>&1)
contains "build.sh runs"           "built dot" "$out"
[ -x "$DOT" ]                       && ok "dot is executable" || bad ...

Asserts: build.sh emits the line built dot ...; the resulting dot file has the executable bit set. Catches missing chmod +x at the end of the build, or the heredoc termination going off the rails.

2. Help & discovery — the dispatcher works

The dispatcher is a one-shot bash case. These tests verify each branch is reachable and prints something sensible.

contains "no args -> usage"        "Usage:" "$($DOT 2>&1)"
contains "--help has Usage"        "Usage:" "$($DOT --help)"
contains "--help lists --demo"     "--demo" "$($DOT --help)"
contains "--demos lists hello"     "hello" "$($DOT --demos)"

Asserts: bare dot shows usage; --help shows usage and lists the --demo subcommand; --demos finds demos/hello/ on disk and prints it. The last test exercises the filesystem-driven demo discovery — if you add a demos/foo/ directory, that command will list it without any code change.

3. Preprocessor — the regex rewrites are correct

The heart of dot. Each eq compares input source against expected rewritten output. dot -c FILE prints the rewrite without running gawk.

eq "field rewrite"       'HEAP[it]["n"]++'         "$(echo '.it.n++' | $DOT -c /dev/stdin)"
eq "object rewrite"      'HEAP[d]'                 "$(echo '.d' | $DOT -c /dev/stdin)"
eq "nested rewrite"      'HEAP[d]["rows"][r][i]'   "$(echo '.d.rows[r][i]' | $DOT -c /dev/stdin)"
eq "explicit dot ok"     '"fred" "." "csv"'        "$(echo '"fred" "." "csv"' | $DOT -c /dev/stdin)"
eq "no dots = passthrough"  'NAME[i] = $i'         "$(echo 'NAME[i] = $i' | $DOT -c /dev/stdin)"

Asserts:

4. Demo runner — --demo handles all three input modes

The --demo subcommand has three input-resolution rules. Each gets a test.

eq "--demo hello (sample.txt)"     "n=5 mean=30.000"   "$($DOT --demo hello)"
eq "--demo hello (- means stdin)"  "n=3 mean=20.000"   "$(printf '10\n20\n30\n' | $DOT --demo hello -)"
eq "--demo hello (DATA arg)"       "n=2 mean=15.000"   "$($DOT --demo hello <(printf '10\n20\n'))"

Asserts:

If the sample-fallback ever silently consumes piped stdin (a real bug we caught earlier), the second test fails immediately.

5. Errors — bad input fails loudly, not silently

out=$($DOT --demo nonesuch 2>&1 || true)
contains "missing demo errors"     "no such demo" "$out"
out=$($DOT -c 2>&1 || true)
contains "-c needs file"           "need FILE.awk" "$out"

Asserts: requesting a non-existent demo prints no such demo on stderr (not a cryptic gawk error); calling -c with no filename prints need FILE.awk. Both exit non-zero.

6. Runtime — new, arr, zap, .field sugar

Each test inlines a tiny awk program (via the run_inline helper) that exercises one runtime function.

eq "new + .field"   "7 1.5" "$(run_inline '
  BEGIN { N = new("plain"); .N.n = 7; .N.mu = 1.5
          printf "%d %.1f\n", .N.n, .N.mu }')"

eq "zap drops slot" "gone"  "$(run_inline '
  BEGIN { N = new("x"); .N.n = 1; zap(N)
          printf "%s\n", ((N in HEAP) ? "kept" : "gone") }')"

eq "arr + nested key" "2"   "$(run_inline '
  BEGIN { N = new("y"); arr(.N.has); .N.has["a"] = 2
          printf "%d\n", .N.has["a"] }')"

Asserts:

7. Printer — o + _oo formatting

One o(x) call per case, capturing the raw printf output.

eq "o(int)"                "5"            "$(run_inline 'BEGIN{o(5); print ""}')"
eq "o(5.0) collapses"      "5"            "$(run_inline 'BEGIN{o(5.0); print ""}')"
eq "o(5.123)"              "5.123"        "$(run_inline 'BEGIN{o(5.123); print ""}')"
eq "o(string)"             "hi"           "$(run_inline 'BEGIN{o("hi"); print ""}')"
eq "o(list)"               "[1, 2, 3]"    "$(run_inline 'BEGIN{a[1]=1;a[2]=2;a[3]=3; o(a); print ""}')"
eq "o(dict sorted)"        "{a: 1, b: 2}" "$(run_inline 'BEGIN{b["b"]=2; b["a"]=1; o(b); print ""}')"
eq "o(list numeric sort)"  "[3, 2, 1]"    "$(run_inline 'BEGIN{a[10]=1;a[2]=2;a[1]=3; o(a); print ""}')"

Asserts each format rule:

What this proves

If you change anything in lib/, run ./tests/test.sh. Two minutes round-trip, end to end. The tests double as the smallest possible usage examples for every public function.