name: write-tests description: Write spec-aligned Dada tests. Use when creating new test files, organizing tests to match the specification, or adding test coverage for language features.
Writing Spec-Aligned Dada Tests
Directory Structure
Tests mirror the spec directory structure under tests/:
spec/src/syntax/string-literals.md → tests/syntax/string_literals/
spec/src/syntax/literals.md → tests/syntax/literals/
Within each test directory, organize by spec section:
tests/syntax/string_literals/
├── delimiters/
│ └── quoted.dada # #:spec syntax.string-literals.delimiters.quoted
├── type.dada # #:spec syntax.string-literals.type
├── escape_sequences/
│ ├── backslash.dada # #:spec syntax.string-literals.escape-sequences.backslash
│ ├── invalid.dada # #:spec syntax.string-literals.escape-sequences.invalid
│ └── ...
└── interpolation/
└── brace_escaping.dada # #:spec syntax.string-literals.interpolation.brace-escaping
Ad-hoc tests that don't correspond to a spec paragraph go in tests/adhoc/.
Spec Paragraph ID Resolution
IDs are built from the spec file path and headings:
- File prefix:
spec/src/syntax/string-literals.md→syntax.string-literalsREADME.mdis special: only the parent directory becomes the prefix
- Heading segments: H2+ headings, lowercased, spaces/underscores → hyphens
- H1 is skipped (it's the page title, already in the file prefix)
- Block local name: From
:::{spec} local_namedirective- If the first token looks like a tag (
rfc0001,unimpl), there's no local name
- If the first token looks like a tag (
- Inline sub-paragraph: From
{spec}`name`inside a block
Examples
# String Literals ← H1: skipped (in file prefix)
## Escape Sequences ← H2: "escape-sequences"
:::{spec} rfc0001 ← Block: no local name (rfc0001 is a tag)
* {spec}`backslash` `\\` produces... ← Inline: "backslash"
:::
:::{spec} invalid rfc0001 ← Block: local name = "invalid"
:::
Resulting IDs:
syntax.string-literals.escape-sequences(the block)syntax.string-literals.escape-sequences.backslash(inline)syntax.string-literals.escape-sequences.invalid(named block)
Writing Test Files
Basic test structure
#:spec syntax.string-literals.delimiters.quoted
#:skip_codegen
async fn main() {
print("hello").await
print("").await
}
Error test (diagnostic expectations)
#:spec syntax.string-literals.escape-sequences.invalid
#:skip_codegen
async fn main() {
print("\a").await
#! ^ /invalid escape
}
The ^ must be at the exact column of the error span on the previous line. Use /pattern (NO closing /) for regex matching.
Type probe test
#:spec syntax.string-literals.type
#:skip_codegen
fn main() {
let x = "hello"
#? ^ VariableType: String
}
VariableType shows the declared type without permissions. Use ExprType for expression types. Multiple ^^ carets match multi-byte spans.
Known Issues
Brace escaping in strings
The tokenizer's delimited() function doesn't skip over string literal contents when scanning for matching braces. Unbalanced { or } inside strings will confuse brace-depth tracking. Workaround: Use balanced \{...\} pairs in test strings:
# Good — balanced braces
print("\{\}").await
print("hello\{world\}").await
# Bad — unbalanced brace causes parse errors
print("\{").await
This will be fixed when string interpolation is implemented.
Checklist for New Tests
- Identify the spec paragraph ID for the feature being tested
- Create the test file in the matching directory structure
- Add
#:spec <paragraph-id>annotation - Add
#:skip_codegenif the test doesn't need WebAssembly generation - Write test code exercising the feature
- Add
#!annotations for expected errors or#?probes for type checking - Run with
cargo dada test --porcelain <test-file>to verify - Check
.test-report.mdif the test fails