Relux is a Rust reimplementation of hawk/lux (LUcid eXpect scripting) — an Expect-style test automation framework. It controls interactive shell programs by sending input and matching output against expected patterns (regex or literal). The goal is a fast, standalone binary with no Erlang dependency.
Working prototype — tests can be authored and executed end-to-end.
A hands-on guide to writing integration tests with Relux, from first test to full test suite.
- Introduction — what Relux is, the problem it solves, and the core mental model of Expect-style testing
- Installation — building Relux from source
- Getting Started — scaffolding a project, configuring the shell, and running the first test
- Send, Match, and Logs — the fundamental operators for sending input, matching output, and debugging failures
- The Output Buffer — how shell output accumulates and the cursor model behind matching
- Built-in Functions — the echo problem,
match_prompt(), and the full built-in function toolkit - Variables — declaring variables, string interpolation, reassignment, and scoping
- Regex Matching — matching output with regex patterns and extracting capture groups
- Functions — defining reusable test logic with parameters and arity-based dispatch
- Timeouts — controlling timing at every level with tolerance and assertion timeouts
- Fail Patterns — continuous error monitoring that catches problems anywhere in the test
- Effects and Dependencies — reusable test infrastructure with dependency graphs and overlay variables
- Pure Functions — functions that compute values without touching a shell
- Cleanup — teardown blocks for removing files, collecting artifacts, and undoing side effects
- Modules and Imports — organizing a multi-file test suite with shared effects and functions
- Condition Markers — conditionally skipping or running tests based on environment
- The CLI — complete coverage of
relux new,check,run, andhistory
A1. Best Practices — all best-practices guidelines from the series in one place
- Per-shell command override: per-shell executable override via shell block attributes (global shell command now configurable in
Relux.toml) - Custom scaffold templates: user-defined templates for
relux new --testandrelux new --effectviaRelux.toml, replacing the built-in defaults - Make
sleep,log, andannotateimpure BIFs: these have observable side effects (time delay, output) and don't belong in pure context — move them fromlookup_puretolookupso they require a shell context
- Interactive debugger: step through test scripts interactively with breakpoints
- Multiple marker semantics: define AND/OR combination semantics when multiple condition markers are stacked on a single test or effect
- Cleanup ordering: all test and effect shells should be terminated before any cleanup block runs. Currently cleanup runs interleaved with shell termination. The correct order is: terminate all test shells, terminate all effect shells, then run cleanup blocks (test cleanup first, then effect cleanup in reverse topological order).
- Cleanup variable scope: test-level and effect-level
letvariables should be available in their respective cleanup blocks. Currently cleanup blocks get a fresh empty scope and can only see overlay variables (for effects) and environment variables.
- Language: Rust (edition 2024)
- Lexer:
logos(~0.16) - Parser:
chumsky(~0.12)
- Lexer (logos): tokenizes
.reluxfiles - Parser (chumsky): tokens → AST
- Resolver: AST → IR, module imports, effect dependency graph
- Runtime: walks IR, spawns PTY shells via tokio, executes send/match with timeouts
- Reporter: test results with ariadne-powered diagnostic output
- File extension:
.relux - Examples go in
examples/ - Follow Rust 2024 edition idioms