Grit uses the upstream Git test suite as ground truth. Harness files live under tests/ (ported from git/t/) and run through scripts/run-tests.sh with the grit binary copied into tests/grit and exposed as git via the harness.
The single source of truth for per-file harness status is the per-test TOML tree data/tests/<group>/<stem>.toml (e.g. data/tests/t0/t0000-basic.toml). There are no intermediate TSVs and no aggregate CSV. Dashboards — docs/index.html (homepage progress card), docs/progress/index.html (summary + progress by group), docs/testfiles.html (per-file table, filterable by group), and docs/test-progress.svg (overall pass-rate badge for the README) — are generated from that tree, but only when requested: pass --dashboard to run-tests.sh or run python3 scripts/generate-dashboard-from-test-files.py directly.
Build the binary first; the runner expects target/release/grit-git.
cargo build --release -p grit-git
# Single file
./scripts/run-tests.sh t3200-branch.sh
# Prefix run: `tests/<arg>*.sh` (e.g. `t1` matches all `t1*.sh` harness files)
./scripts/run-tests.sh t1
# Full suite (every status TOML with in_scope = "yes")
./scripts/run-tests.sh
# Options (can appear before or after the target)
./scripts/run-tests.sh --timeout 180 t0000-basic.sh
./scripts/run-tests.sh t0000-basic.sh --quiet
# Regenerate docs/ dashboards after the run (off by default)
./scripts/run-tests.sh t0000-basic.sh --dashboard
# Isolated run: write results under another directory, leave data/tests/ and docs/ untouched
./scripts/run-tests.sh --data-dir /tmp/iso t0000-basic.shEdit that test's TOML (data/tests/<group>/<stem>.toml): set in_scope = "skip". Skipped files are never executed (single-file, group, or full run). Their tests are excluded from the summary counts on docs/progress/index.html. They still appear on docs/testfiles.html with a skipped badge so you can see what was opted out.
Re-run python3 scripts/generate-test-files-catalog.py if you add or rename .sh files and want the status tree updated without running tests (otherwise the next run-tests.sh also refreshes the catalog; TOMLs for deleted test files are pruned).
| Script | Role |
|---|---|
scripts/test_status.py |
Shared helper: load/save data/tests/<group>/<stem>.toml files (atomic writes, TOML serialization, pruning). |
scripts/generate-test-files-catalog.py |
Scan tests/t*.sh, merge the data/tests/ tree (preserves in_scope and prior run results; prunes stale TOMLs). |
scripts/run-tests.sh |
Select files to run, execute harness, invoke apply (+ dashboard with --dashboard). |
scripts/apply-test-run-results.py |
Merge one batch of run lines into the matching data/tests/ TOMLs. |
scripts/generate-dashboard-from-test-files.py |
Read data/tests/ only; write docs/index.html, docs/progress/index.html, docs/testfiles.html, and docs/test-progress.svg. |
-
scripts/generate-test-files-catalog.py— Scanstests/t*.sh, countstest_expect_success/test_expect_failureper file, assignsgroup(t0–t9from the first digit of thetNNNN…prefix, matchinggit/t/READMEtest families), and writes or merges thedata/tests/<group>/<stem>.tomlfiles. Invoked automatically at the start ofrun-tests.sh. -
scripts/run-tests.sh— Copiestarget/release/grit-gittotests/grit, builds the file list (honoringin_scope), runs each selected script undertimeout, parses the# Tests:summary line, writes a small batch TSV forscripts/apply-test-run-results.py. -
scripts/apply-test-run-results.py— Updates the matchingdata/tests/TOMLs (passed_last,failing,fully_passing,status, etc.). Writes are atomic (temp file + rename), so parallel family runs never collide: each test file owns its own TOML. -
data/tests/<group>/<stem>.tomlkeys (fileandgroupare derived from the path, not stored):
| Key | Meaning |
|---|---|
in_scope |
"yes" or "skip" (manual) |
tests_total |
Count of test markers in the file |
passed_last |
Pass count from the last run |
failing |
Fail count from the last run |
fully_passing |
true if tests_total > 0 and failing == 0 |
status |
"ok", "timeout", or "error" from the harness |
expect_failure |
Count of test_expect_failure lines |
Example (data/tests/t0/t0000-basic.toml):
in_scope = "yes"
tests_total = 92
passed_last = 91
failing = 1
fully_passing = false
status = "ok"
expect_failure = 8- Pick a test file that is not fully passing.
- Run it:
./scripts/run-tests.sh t1234-foo.sh - Fix Rust in
grit//grit-lib/. - Re-run until green; the test's status TOML updates automatically.
- Plumbing (
t0xxx,t1xxx) - Index/checkout (
t2xxx) - Core commands (
t3xxx) - Diff (
t4xxx) - Transport (
t5xxx) - Rev machinery (
t6xxx) - Porcelain (
t7xxx) - External helpers (
t9xxx) last
When you fix known breakage, flip test_expect_failure → test_expect_success in the test file.
Do not modify tests/test-lib.sh casually — past changes caused regressions.
Before "fixing grit" for a failing file, rule this out first — it is a test-file bug, not a grit bug.
Symptom: only the setup test passes (≈1/N) and every later test fails with
./test-lib.sh: line NNNN: cd: repo: No such file or directory.
Cause: test-lib.sh persists the working directory across top-level test_expect_success
blocks (matching upstream git/t). If the setup test does git init repo && cd repo && … it
leaves the shell inside repo/. Every later block that starts with a bare cd repo then runs
before it is back at the trash root, so the cd fails and the block aborts before any git/grit
command runs.
Fix: wrap each test body in a subshell so the cd cannot leak:
test_expect_success 'desc' '
(
cd repo &&
…
)
'scripts/_wrap_cd_subshell.py <files…> does this mechanically (idempotent; only wraps bodies that
contain a cd). After wrapping, re-run only the files you changed and diff pass counts against
the previous recorded values — wrapping a body that a currently-passing test relied on for leaked cwd
can cost a test, so confirm no file regressed before committing.
Spotting candidates: low pass ratio and nearly every test_expect_success body starts with a
bare cd. Quick scan:
grep -c "test_expect_success" tests/tXXXX-*.sh # total blocks
grep -cE "^[[:space:]]+cd [^&]*&&" tests/tXXXX-*.sh # bare-cd blocks; ≈equal + low pass ⇒ this bugNot regenerated by default. Pass --dashboard to run-tests.sh, or refresh HTML only (no test run) with:
python3 scripts/generate-dashboard-from-test-files.pyThese do not update data/tests/ by default:
scripts/run-upstream-tests.sh/scripts/aggregate-upstream.sh— run upstreamgit/t/against grit in isolation (see AGENTS.md).tests/harness/run-all-count.sh— separate harness; not wired todata/tests/.