testing/runner/docs/cli-usage.md
This document describes the command-line interface for the test runner.
# Build from source
cargo build --release
# The binary will be at:
./target/release/test-runner
run - Execute TestsRun SQL tests from files or directories.
test-runner run <PATHS>... [OPTIONS]
<PATHS>... - One or more test files (.sqltest) or directories to scan| Option | Short | Default | Description |
|---|---|---|---|
--binary <PATH> | tursodb | Path to the tursodb CLI binary | |
--filter <PATTERN> | -f | Filter tests by name (glob pattern) | |
--jobs <N> | -j | CPU count | Maximum concurrent test jobs |
--output <FORMAT> | -o | pretty | Output format: pretty or json |
--timeout <SECS> | 30 | Timeout per query in seconds |
# Run all tests in a directory
test-runner run tests/
# Run a specific test file
test-runner run tests/select.sqltest
# Run multiple paths
test-runner run tests/select.sqltest tests/insert/
# Custom binary path
test-runner run tests/ --binary ./target/release/tursodb
# Filter by test name
test-runner run tests/ -f "select-*"
test-runner run tests/ -f "*-join-*"
# Limit concurrent jobs
test-runner run tests/ -j 4
# JSON output for CI
test-runner run tests/ -o json
check - Validate SyntaxValidate test file syntax without executing tests.
test-runner check <PATHS>...
# Check a single file
test-runner check tests/select.sqltest
# Check all files in a directory
test-runner check tests/
Human-readable colored output with progress indication:
Running tests...
tests/select.sqltest
[PASS] select-const-1 (2ms)
[PASS] select-users (5ms)
[FAIL] select-join (3ms)
expected: 1|Alice
actual: 1|Bob
[SKIP] select-buggy (known issue #123)
tests/insert.sqltest
[PASS] insert-basic (4ms)
[ERROR] insert-readonly (1ms)
setup 'users' failed: table users already exists
Summary:
4 passed, 1 failed, 1 skipped, 1 error
Total time: 127ms
Machine-readable output for CI integration:
{
"files": [
{
"path": "tests/select.sqltest",
"results": [
{
"name": "select-const-1",
"outcome": "passed",
"duration_ms": 2
},
{
"name": "select-join",
"outcome": "failed",
"reason": "expected: 1|Alice\nactual: 1|Bob",
"duration_ms": 3
}
],
"duration_ms": 15
}
],
"summary": {
"total": 6,
"passed": 4,
"failed": 1,
"skipped": 1,
"errors": 0,
"duration_ms": 127
}
}
| Code | Meaning |
|---|---|
| 0 | All tests passed (or skipped) |
| 1 | One or more tests failed or errored |
| 2 | Invalid arguments or configuration error |
The --filter option supports simple glob patterns:
| Pattern | Matches |
|---|---|
select-* | Tests starting with "select-" |
*-test | Tests ending with "-test" |
*join* | Tests containing "join" |
select-const-1 | Exact match |
| Variable | Description |
|---|---|
TURSO_TEST_BINARY | Default path to tursodb binary (overridden by --binary) |
NO_COLOR | Disable colored output |
- name: Run SQL tests
run: |
test-runner run tests/ \
--binary ./target/release/tursodb \
--output json \
> test-results.json
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: test-results
path: test-results.json
Currently, all tests run to completion. A --fail-fast option may be added in the future.
-j to match your CPU count for maximum throughput-fsrc/main.rs)Uses clap with derive macros for argument parsing:
#[derive(Parser)]
#[command(name = "test-runner")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Run { /* ... */ },
Check { /* ... */ },
}
The main function is async using #[tokio::main]:
#[tokio::main]
async fn main() -> ExitCode {
let cli = Cli::parse();
match cli.command {
Commands::Run { .. } => run_tests(...).await,
Commands::Check { paths } => check_files(paths),
}
}
src/output/)The output module provides a trait-based abstraction for formatting:
pub trait OutputFormat {
fn write_test(&mut self, result: &TestResult);
fn write_file(&mut self, result: &FileResult);
fn write_summary(&mut self, summary: &RunSummary);
fn flush(&mut self);
}
pub enum Format {
Pretty,
Json,
}
src/output/pretty.rs)colored crate for terminal colorsNO_COLOR environment variablesrc/output/json.rs)serde and serde_json for serializationreason field when null (using #[serde(skip_serializing_if)])1. Parse CLI arguments (clap)
2. Create CliBackend with binary path and timeout
3. Create RunnerConfig with jobs and filter
4. Create TestRunner<CliBackend>
5. Call runner.run_paths(&paths)
6. Create output formatter (Pretty or Json)
7. Write file results
8. Write summary
9. Return exit code based on success
The check command validates syntax without running tests:
fn check_files(paths: Vec<PathBuf>) -> ExitCode {
for path in paths {
if path.is_dir() {
// Glob for *.sqltest and check each
} else {
// Parse single file
}
}
// Return success if no parse errors
}
if summary.is_success() {
ExitCode::SUCCESS // 0
} else {
ExitCode::from(1) // 1 for test failures
}
// 2 is returned for invalid arguments (before running)