Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# Coverage artifacts (cargo-llvm-cov)
/lcov.info

# Generated stress/perf fixture — regenerate on demand, never commit (see docs/TESTING.md)
/fixtures/specialized/large.md

*.swp
*.swo
*~
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help fmt fmt-check lint test build build-release check all coverage coverage-html coverage-lcov
.PHONY: help fmt fmt-check lint test build build-release check all coverage coverage-html coverage-lcov large-fixture

CARGO ?= cargo

Expand All @@ -15,6 +15,7 @@ help:
@echo " coverage - test coverage summary in the terminal (cargo-llvm-cov)"
@echo " coverage-html - generate an HTML coverage report under target/llvm-cov/html"
@echo " coverage-lcov - emit lcov.info for external tooling"
@echo " large-fixture - (re)generate the gitignored stress fixture for manual perf runs (see docs/TESTING.md)"

fmt:
$(CARGO) fmt --all
Expand Down Expand Up @@ -46,3 +47,7 @@ coverage-html:

coverage-lcov:
$(CARGO) llvm-cov --all-targets --lcov --output-path lcov.info

large-fixture:
./scripts/gen-large-fixture.sh > fixtures/specialized/large.md
@echo "Generated fixtures/specialized/large.md (gitignored). Remove it when done perf-testing: rm fixtures/specialized/large.md"
75 changes: 75 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Testing

How termdown's tests are organized and run. Every command goes through the
[`Makefile`](../Makefile) so local runs match CI exactly (see the project's
`CLAUDE.md` for the rule — don't call `cargo test`/`clippy` directly).

## Running

| Command | What it does |
|---|---|
| `make test` | Run the whole suite |
| `make check` | `fmt-check` + `lint` + `test` — the CI gate; run before pushing |
| `make coverage` | Local coverage summary via `cargo-llvm-cov` (on-demand, not a CI gate) |

## Test layout

- **Unit tests** — inline `#[cfg(test)]` modules in `src/*.rs` (config parsing, ANSI/width helpers, font ranges, frontmatter parsing, layout, …).
- **`tests/cli.rs`** — black-box CLI: `--help`/`--version`, stdin/file input, missing-file errors, the unsupported-terminal warning.
- **`tests/snapshots.rs`** — byte-level snapshot of cat-mode stdout for each fixture, with Kitty image payloads collapsed to `<IMG>` (PNG bytes are font/OS-dependent, so only the *position* of an image is compared, not its pixels). Background: [`TERMINAL_PROTOCOLS.md`](TERMINAL_PROTOCOLS.md).
- **`tests/headings.rs`** — parses the Kitty APC frames out of stdout, decodes the heading PNGs, and asserts dimensions / non-blank pixels / H1 > H2 > H3 scaling.

Tests drive the compiled binary through `tests/common/mod.rs::run_termdown`,
which forces a ghostty-like terminal (`TERM_PROGRAM=ghostty`, so Kitty
emission is on), `--theme dark`, and clears `HOME`/`XDG_CONFIG_HOME` so a
developer's own config can't leak in.

### Fixtures

- `fixtures/*.md` and `fixtures/specialized/*.md` — rendering inputs. Their snapshot expectations live alongside in `fixtures/expected/**/*.ansi`.
- `fixtures/links/` — a small `.md` link graph for **manual** QA of TUI link-following; `index.md` documents the steps. Not wired into automated tests.

### Regenerating snapshots

When a rendering change is intentional, the snapshot test fails and prints two
paths: the expected `.ansi` file and a temp file holding the *actual* output
(`actual written to: …` — the exact temp dir is OS-dependent). Review the diff
intent-first, then accept it by copying the printed temp path over the expected
file:

```sh
# use the exact path the test printed after "actual written to:"
cp <printed-temp-path> fixtures/expected/supported-syntax.ansi
make test # confirm green
```

## Performance / stress testing

`fixtures/specialized/large.md` is a deterministic stress fixture — 1 H1 + 500
H2 sections (mixed CN/EN paragraphs, nested lists, 3×3 tables, fenced Rust code)
plus a ~200-row tail table: ~14.7k lines / ~460 KB. It exists only to eyeball
that termdown stays snappy on a large document; it is **not** wired into the
automated suite.

Because it is generated build output with no automated consumer, it is **not
committed** — it's gitignored and produced on demand. The full workflow is
**generate → test → delete**:

```sh
# 1. Generate (~1.7s; deterministic, so a clean re-run is byte-identical)
make large-fixture
# or directly: ./scripts/gen-large-fixture.sh > fixtures/specialized/large.md

# 2. Test against it manually
cargo build --release
time ./target/release/termdown --cat fixtures/specialized/large.md > /dev/null # cat throughput
./target/release/termdown fixtures/specialized/large.md # TUI: scroll / search / heading-jump feel

# 3. Delete when done (it's large and gitignored anyway)
rm fixtures/specialized/large.md
```

Size/shape is tunable via env vars read by the script: `SECTIONS` (default
500), `H3_EVERY` (50), `TAIL_TABLE_ROWS` (200) — e.g.
`SECTIONS=2000 make large-fixture` for an even larger document. After changing
`scripts/gen-large-fixture.sh`, re-run it to regenerate.
Loading
Loading