Testing
A rewrite is only worth it if nobody can tell from the HTML. Slim itself is the reference: wherever possible, a test renders through both engines and requires the same bytes.
Slim’s own suites
Slim’s test suite and its literate specification, TESTS.md, run against SlimX unchanged: 295 tests pass. The others test what SlimX leaves out on purpose, or Markdown output that depends on which library Tilt picks — and one pins a Slim bug, a = reported one line too low after a ruby: block.
Fuzzer
A seeded generator writes random templates from the grammar — nested tags, shortcuts, every attribute form, splats, control flow, blocks, comments, embedded engines, whitespace markers — and renders each through Slim and SlimX. The HTML must match, or both must fail with the same error. A second pass formats each template and requires the same HTML and an idempotent result.
The suite runs a fixed seed; CI adds 10,000 templates from a new seed on every run, and prints it when they disagree.
Golden snapshots
Nineteen templates cover the grammar, its errors and Tailwind class names. Next to each, a snapshot holds the rendered HTML, the Ruby compiled for Tilt and for Rails, the formatted source and the lint offenses: any change shows up in review. Whatever its snapshot says, each template must also:
- render the HTML Slim renders, with and without
html_safe, or fail with Slim’s message; - render the same through Rails and through Tilt;
- compile to Ruby that parses, with every
-and=on its template line; - format idempotently, to the same HTML, passing strict mode.
Real templates
6,097 templates from 30 open-source projects, pinned to a commit in corpus/manifest.yml. rake corpus:fetch downloads only their .slim files; they are never copied into the repository. For each one:
- Slim and SlimX both compile it, or both refuse it;
- SlimX’s Ruby parses, with every line where it belongs;
slimx fmtis idempotent, and refuses any change to the compiled Ruby;- the linter runs, and its offenses go to a report.
Downloaded templates are never rendered: rendering runs their Ruby. A production Rails app of about 1,400 templates, whose code we trust, is rendered through both engines against a stub that answers every helper, before and after formatting, with identical HTML.
rake corpus:diff BASE=main compares two revisions over the whole corpus — compiled Ruby, formatting, lint offenses — so a refactoring that changes nothing proves it.
- fjordllc/bootcamp712
- shikimori/shikimori555
- DFE-Digital/teaching-vacancies541
- betagouv/rdv-service-public491
- benwbrum/fromthepage350
- solectrus/solectrus281
- fairmondo/fairmondo242
- tryzealot/zealot239
- catarse/catarse236
- nbgallery/nbgallery175
- brave-intl/publishers173
- openHPI/codeocean170
- freeats/freeats170
- wearefine/fae165
- weg-li/weg-li155
- pivorakmeetup/pivorak-web-app146
- windy/cywin145
- hpyhacking/peatio137
- noosfero/noosfero119
- ujh/fountainpencompanion112
- locomotivecms/engine110
- asciidoctor/asciidoctor-backends109
- amahi/platform109
- square/shuttle102
- rubytoolbox/rubytoolbox87
- chloerei/writings77
- annict/annict59
- Coursemology/coursemology255
- jirutka/asciidoctor-html5s44
- codetriage/CodeTriage31
Every commit
GitHub Actions runs the suite and Slim’s suites on every maintained Ruby (3.3, 3.4 and 4.0), ruby-head and macOS, the fuzzer with a new seed, and the corpus check; on a pull request, the corpus diff against its base too. It takes under a minute.
bundle exec rake test
FUZZ_SEED=42 FUZZ_CASES=20000 bundle exec ruby -Ilib -Itest test/fuzz_test.rb
bundle exec rake corpus:fetch corpus:check