

# Testing

This document specifies the initial **language-level testing** surface for
Silk.

The goal is a Zig-like authoring experience (tests live next to the code they
exercise) with a simple CLI runner that emits modern TAP output for downstream
consumption.

## `test` declarations

A `test` declaration is a top-level block of statements that the compiler can
compile and execute under `silk test`.

Syntax:

```silk
test "name" {
  // statements...
}
```

The string name is optional:

```silk
test {
  // statements...
}
```

Rules:

- `test` declarations MAY appear:
 - at top level (like `fn` and `let`), and
 - nested inside another `test` block (scoped subtests).
- A `test` block introduces its own scope (like a function body).
- Nested `test` blocks are executed inline, in source order, as part of the
 enclosing test’s execution. They may be used for hierarchical grouping and
 shared setup.
- `test` blocks may use `let`, `var`, control flow, and call functions/methods
 using the same expression subset as normal code.
- Top-level `test` blocks may use `await`. When a test body contains `await`,
 `silk test` runs that generated test wrapper as async and awaits it from the
 generated runner.
- `return;` is allowed inside a `test` block (equivalent to ending the test
 early). `return <expr>;` is not allowed.

`silk test` executable runners use the native host target when Silk has a
host-backed executable backend for it, and otherwise fall back to
`linux-x86_64` (or `linux-x86_64-musl` on musl x86_64 Linux hosts). Formal
Silk target metadata in `silk test` reflects that selected execution target.

Doc comments:

- Doc comments (`/** ... */` and `/// ...`) attach to a `test` declaration the
 same way they attach to other top-level declarations.

## Running tests (`silk test`)

The `silk test` command:

- loads a module set (like `silk check` / `silk build`),
- discovers all `test` declarations in the module set, and
- executes them, emitting TAP output.

### TAP output

The initial runner uses TAP version 13 formatting:

- `TAP version 13`
- `1..N`
- `ok <n> - <name>`
- `not ok <n> - <name>`

### Nested test progress output

When a `test` block contains nested `test` blocks, the runner emits subtest
progress lines to stderr as each nested test completes:

- `ok - a/b`
- `not ok - a/b`

The `a/b` path reflects the active nested test name stack (including the
outermost test name) joined with `/`. This keeps TAP output on stdout stable
while making long nested suites easier to follow in an interactive terminal.

### Assertions inside tests

In `silk test` builds, failed assertions do not abort the process. Instead:

- A failed `assert` records a test failure and execution continues.
- If the assertion has no explicit message, the compiler uses the assertion
 condition text as the message (e.g. `assert value != 123;` uses `value != 123`).
- Failed assertions also emit a one-line detail message to stderr so failures are
 visible in `silk test` output without requiring `--debug`, formatted like:
 - `assertion failed: <message>` when not inside any `test` block, or
 - `assertion failed [test: a]: <message>` when inside a `test` block, or
 - `assertion failed [test: a/b]: <message>` when inside nested `test` blocks.

 The `a/b` path reflects the active nested test name stack (including the
 outermost `test` declaration name).
- The test executable exits non-zero if any failures were recorded so TAP output
 reflects failures.

The current runner still isolates top-level tests in separate processes, but a
single test case may now accumulate multiple failures.

## [`std::test`](/silk/docs/std/test/) (standard test helpers)

The standard library provides [`std::test`](/silk/docs/std/test/) helpers for test-only assertions that
record failures without aborting:

- `expect(ok: bool, message: string? = None);`
- `expect_equal(expected: X, actual: Y) -> bool;`
- `expect_error(err: E?) -> bool;`

See [test](/silk/docs/std/test/) for the detailed API.

Note: [`std::test`](/silk/docs/std/test/) helpers carry a Formal Silk contract requiring
`BUILD_MODE == "test"` via [`std::test::requires_test_mode()`](/silk/docs/std/test/) so downstream
verification can model them as test-only APIs.

## Notes

- parsing of `test` declarations and `silk test` runner with TAP
 output.
- [`std::test`](/silk/docs/std/test/) helpers and non-aborting assertions in test builds.
