https://github.com/jamesgober/test-lang
Compiler snapshot test harness for tokens, ASTs, and errors.
https://github.com/jamesgober/test-lang
compiler language rust snapshot testing
Last synced: 1 day ago
JSON representation
Compiler snapshot test harness for tokens, ASTs, and errors.
- Host: GitHub
- URL: https://github.com/jamesgober/test-lang
- Owner: jamesgober
- License: apache-2.0
- Created: 2026-06-19T10:51:32.000Z (21 days ago)
- Default Branch: main
- Last Pushed: 2026-07-01T12:41:10.000Z (8 days ago)
- Last Synced: 2026-07-01T13:25:15.149Z (8 days ago)
- Topics: compiler, language, rust, snapshot, testing
- Language: Rust
- Size: 61.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE-APACHE
Awesome Lists containing this project
README
test-lang
SNAPSHOT HARNESS
test-lang is a snapshot test harness for language front-ends. Give it source, run that source through a stage — a lexer, a parser, a diagnostics renderer — and assert the rendered result against a known-good block of text. When the output changes, you get a line-level diff pointing at exactly what moved, and accepting the new behavior is a copy-paste.
It owns no grammar and takes no runtime dependencies. The harness works over anything that renders itself to text — a Display value, a Debug tree, or an iterator of displayable items — so the same two types serve a hand-written lexer, a generated parser, or a diagnostics layer without coupling to any of them. It is no_std + alloc capable and normalizes line endings and trailing whitespace so a snapshot captured on Windows matches one written on Linux.
MSRV is 1.85+ (Rust 2024 edition). Part of the -lang language-construction family.
Stable — API frozen at 1.0.0. The public surface will not change in a breaking way before a 2.0; 1.x releases are additive. See docs/API.md for the frozen surface and CHANGELOG.md for history.
## Performance First
Latest local Criterion means (`cargo bench`, Windows x86_64, Rust stable). The workload that matters in a test suite is the check on the matching path — the case a green suite runs on every pass:
- **Capture** (`per_line`, 16 lines): ~0.5 µs
- **Check, matching** (16 lines): ~1.2 µs
- **Check, matching** (256 lines): ~19 µs
The check is a line-level diff with a common prefix/suffix fast path: identical leading and trailing lines are matched in linear time before the quadratic LCS engine runs, so a near-miss snapshot only pays for the region that actually changed.
## Installation
```toml
[dependencies]
test-lang = "1"
```
`no_std` + `alloc` (drops the `std::error::Error` anchor, keeps everything else):
```toml
[dependencies]
test-lang = { version = "1", default-features = false }
```
## Quick Start
Snapshot a token stream, one token per line, and assert it:
```rust
use test_lang::Snapshot;
// Whatever your lexer produces — here a stand-in that yields display strings.
fn lex(source: &str) -> Vec {
source.split_whitespace().map(str::to_string).collect()
}
let snapshot = Snapshot::per_line(lex("let x = 1"));
snapshot.check("let\nx\n=\n1").expect("token stream matches");
```
When the output drifts, the returned `Mismatch` shows precisely what changed:
```rust
use test_lang::Snapshot;
let snapshot = Snapshot::per_line(["let", "y", "=", "1"]);
let err = snapshot.check("let\nx\n=\n1").unwrap_err();
// `-x` was expected; `+y` was produced in its place.
assert!(err.to_string().contains("-x"));
assert!(err.to_string().contains("+y"));
```
## Features
- **Three capture modes** — `Snapshot::display` for a `Display` value, `Snapshot::debug` for a pretty-printed `Debug` tree, and `Snapshot::per_line` for a token stream (one item per line, so the diff points at the exact token).
- **Cross-platform normalization** — CRLF/CR collapse to LF, trailing whitespace is stripped, and trailing blank lines are trimmed. A snapshot written by hand in a test matches output captured on any platform.
- **Line-level diffs** — a failed `check` returns a `Mismatch` whose `Display` is a unified `-expected`/`+actual` diff; `Mismatch::diff` exposes the `Diff` for programmatic inspection.
- **No runtime dependencies** — built on `core::fmt` and `alloc` only. `no_std` capable.
- **No panics** — `check` returns `Result`; the test author decides whether to `unwrap`, `expect`, or propagate.
## API Overview
For a complete reference with examples, see [`docs/API.md`](./docs/API.md).
- [`Snapshot`](./docs/API.md#snapshot) — a normalized, comparable rendering of compiler output; `new` / `display` / `debug` / `per_line` / `check`.
- [`Diff`](./docs/API.md#diff) & [`Change`](./docs/API.md#change) — the line-level edit script, rendered as a unified diff.
- [`Mismatch`](./docs/API.md#mismatch) — the error returned by `Snapshot::check`, carrying the `Diff`.
Runnable examples: [`examples/tokens.rs`](./examples/tokens.rs), [`examples/ast.rs`](./examples/ast.rs), [`examples/diagnostics.rs`](./examples/diagnostics.rs).
```bash
cargo run --example tokens
cargo run --example ast
cargo run --example diagnostics
```
## Contributing
See [`REPS.md`](./REPS.md) for engineering standards and the definition of done. Before a PR: `cargo fmt --all`, `cargo clippy --all-targets --all-features -- -D warnings`, and `cargo test --all-features` must be clean.
License
Licensed under either of
-
Apache License, Version 2.0 — LICENSE-APACHE
-
MIT License — LICENSE-MIT
at your option.
COPYRIGHT © 2026 James Gober .