Testing & Error Reporting
Running Tests
All language and error tests live in tests/ and run via the TestDang suite:
# Run all tests go test ./tests/ -v # Run a specific test by name go test ./tests/ -run "TestDang/TestLanguage/test_foo" -v go test ./tests/ -run "TestDang/TestErrorMessages/some_error" -v go test ./tests/ -run "TestDang/TestFormatLanguage/test_foo" -v
Test Suites
The DangSuite (in tests/integration_test.go) has three sub-suites:
- •TestLanguage: Runs each
tests/test_*.dangfile and expects it to succeed. - •TestFormatLanguage: Formats each
tests/test_*.dangfile, re-parses the formatted output, runs it, and checks it still produces the same result. - •TestErrorMessages: Runs each
tests/errors/*.dangfile, expects an error, and compares the output againsttests/testdata/<name>.golden.
Error Message Golden Files
Error golden files live in tests/testdata/ and contain the exact error output including ANSI escape codes for colored/highlighted source annotations.
To update golden files after changing error messages:
go test ./tests/ -run "TestDang/TestErrorMessages" -update
Adding a New Error Test
- •Create
tests/errors/my_error.dangwith code that should produce an error. - •Run with
-updateto generate the golden file:bashgo test ./tests/ -run "TestDang/TestErrorMessages/my_error" -update
- •Review
tests/testdata/my_error.goldento confirm the error message is correct.
Dagger SDK Tests
The Dagger SDK has its own integration test suite in dagger-sdk/tests/ that exercises modules against a real Dagger engine via dagger call.
Running SDK Tests
# Run all SDK tests go test ./dagger-sdk/tests/ -v # Run a specific test go test ./dagger-sdk/tests/ -run "TestDaggerSDK/TestMismatch" -v
Test Structure
- •Test suite:
DaggerSDKSuiteindagger-sdk/tests/integration_test.go - •Test modules: Each test has a Dagger module in
dagger-sdk/testdata/<module-name>/containing:- •
main.dang— the module source - •
dagger.json— module config with"sdk": "../.."pointing to the SDK
- •
Adding a New SDK Test
- •Create
dagger-sdk/testdata/my-test/main.dangwith a Dagger module. - •Create
dagger-sdk/testdata/my-test/dagger.json:json{ "name": "my-test", "engineVersion": "v0.19.11", "sdk": "../.." } - •Add a test method on
DaggerSDKSuiteindagger-sdk/tests/integration_test.go:gofunc (DaggerSDKSuite) TestMyTest(ctx context.Context, t *testctx.T) { t.Run("sub test", func(ctx context.Context, t *testctx.T) { out := requireDagger(ctx, t, "my-test", "some-function", "--arg", "value") require.Contains(t, out, "expected output") }) }
Helpers
- •
runDagger(ctx, module, args...)— runsdagger -m <testdata/module> call <args>, returns stdout, stderr, error - •
requireDagger(ctx, t, module, args...)— same but fails the test on error, returns stdout
Reporting Nice Errors
Dang has two error types for attaching source locations:
InferError (type checking phase)
Use NewInferError in Infer() methods to attach source location to type errors. These get converted to SourceError with full source highlighting when displayed.
return nil, NewInferError(fmt.Errorf("descriptive message"), node)
WrapInferError avoids double-wrapping if the error is already an InferError:
return nil, WrapInferError(err, node)
SourceError (display/eval phase)
SourceError holds the error, source location, and source text, and renders with highlighted code snippets:
return nil, NewSourceError(fmt.Errorf("message"), location, sourceCode)
During eval, use the EvalContext helper:
return nil, evalCtx.CreateSourceError(err, node)
Conversion Flow
InferError → ConvertInferError() → SourceError (reads the source file and produces highlighted output). This happens automatically in RunFile.
WithInferErrorHandling / WithEvalErrorHandling
Wrapper helpers that automatically attach source location to errors that don't already have one:
func (n *MyNode) Infer(ctx context.Context, env hm.Env, fresh hm.Fresher) (hm.Type, error) {
return WithInferErrorHandling(n, func() (hm.Type, error) {
// ... your logic here
})
}
PEG Grammar
The parser is defined in pkg/dang/dang.peg (pigeon PEG parser). After editing:
go generate ./pkg/dang/
This regenerates pkg/dang/dang.peg.go.
Formatter
The formatter lives in pkg/dang/format.go. When adding a new AST node type, update:
- •
formatNode()— add a case to format the node - •
nodeLocation()— return the node's*SourceLocation - •Any relevant classification helpers (e.g.
isFunctionDef) for blank line insertion logic