Testing Strategy¶
OpenSysML uses a multi-layer test contract to ensure correctness and prevent regressions across parsing, semantic analysis, and execution.
Test Organization¶
internal/core/
├── parser/
│ ├── golden_test.go # Golden AST snapshots
│ ├── negative_test.go # Malformed input handling
│ └── testdata/parse/ # Test fixtures + goldens
├── runtime/
│ ├── conformance_test.go # Execution outcome verification
│ ├── trace_test.go # Execution ordering/scheduling
│ ├── robustness_test.go # Failure mode handling
│ └── testdata/conformance/ # Behavioral test cases
└── libs/
└── stdlib_conformance_test.go # Standard library gate
Parser Test Contract¶
New grammar features require a four-layer test contract:
1. Conformance Gate¶
Purpose: Ensure standard library continues to parse cleanly
- Test:
TestStdlibConformance(internal/core/libs/) - Coverage: 95/95 bundled library files — the 94 official SysML v2 standard library files and the non-normative OpenSysML extension
- Acceptance: All stdlib files parse without errors
- Allowlist:
testdata/stdlib_known_failures.txt(currently empty)
2. Golden AST Snapshots¶
Purpose: Verify AST structure matches expected output
- Test:
TestGolden(internal/core/parser/) - Fixtures:
testdata/parse/*.sysmland*.kerml(one representative file per construct) - Goldens:
testdata/parse/*.golden(AST dumps) - Acceptance: Parse output matches golden file
Update goldens after intentional changes:
Coverage includes: - Package/namespace declarations - Part/attribute definitions and usages - Connections and relationships - Requirements and constraints - State machines and transitions - Actions (control flow, parameters, nested) - Calculations and expressions - Enumerations and metadata
3. Round-Trip Serialization¶
Status: Explicitly deferred (no faithful SysML printer exists)
Future work: If SysML printer added, verify parse(print(parse(input))) == parse(input)
4. Negative Test Suite¶
Purpose: Verify parser rejects malformed input gracefully
- Test:
TestNegative(internal/core/parser/) - Coverage: one subtest per malformed input — count in the measured counts
- Acceptance: Each case produces diagnostics (never panics)
Examples: - Unclosed blocks - Unexpected tokens - Invalid syntax - Incomplete behavioral members
Behavioral Test Contract¶
New behavioral features (actions, states, calc, constraints, requirements) require a four-layer test contract:
1. Golden AST Fixtures¶
Purpose: Lock in parse structure before execution changes
- Location:
internal/core/parser/testdata/parse/(behavioral fixtures) - Coverage: the behavioral fixtures among the whole set — count in the measured counts
- Acceptance:
TestGoldenpasses, AST dumps match expectations
Behavioral fixtures:
- action_control_flow.sysml, action_if_branch_body.sysml, action_mixed_params.sysml, action_send_port.sysml
- state_full.sysml, state_transition_variants.sysml, state_call_trigger.sysml, state_def_region_pseudostate.sysml, state_defer.sysml, state_fork_join.sysml, state_history_entry_exit.sysml, state_timed_triggers.sysml, state.sysml
- calc.sysml, calc_defaults_and_invocation.sysml, calc_return.sysml, calc_return_parameter.sysml
- constraint_assert_assume.sysml
- requirement.sysml, requirement_members.sysml
2. Execution Conformance Gate¶
Purpose: Verify behavioral execution produces expected outcomes
- Test:
TestExecutionConformance(internal/core/runtime/) - Format:
.sysml+.expected.jsonpairs - Schema:
internal/core/runtime/testdata/conformance/README.md - Allowlist:
known_failures.txt(currently empty)
Coverage (all passing, by fixture prefix; counts in the measured counts):
- Calc: parameter binding, return values, defaults, inherited parameters, unary ops, qualified names, type coercion, body-local usages, statement bodies, nested and from-constraint invocation
- Action: token flow, outputs, nested invocation, send/accept, port communication, perform reference and shorthand, accept...then, flows, loops and decisions
- State: simple, do behavior, concurrent do, transition effect, choice/junction/fork-join pseudostates, orthogonal regions and region pseudostates, shallow/deep history, entry/exit points, deferred/undeferred events, call and timed triggers, signal discrimination/unmatched, self signal
- Requirement: require, subject, actor, assume, nested
- Instance, unit and quantity, constraint assert/assume/negation, satisfy, variation, redefinition, variant, feature chains, string operations, nested behaviors, element filters, ball-and-chain, and one each of attribute, connector, cubesat and view
3. Golden Execution Traces¶
Purpose: Verify how execution proceeds (ordering, scheduling), not just final result
- Test:
TestExecutionTrace(internal/core/runtime/) - Format:
.trace.goldenfiles - Determinism: Token sorting by ID, fixed event queue tie-breaking
- Coverage:
.trace.goldenfiles for action, calc, state, constraint, accept and string execution
Trace format examples:
- Action: step 1: token T1@node1, token T2@node2 (sorted)
- State: entry: StateName [hasEntryAction], transition: From -> To [event], exit: StateName [hasExitAction]
Generate traces:
4. Runtime Robustness Tests¶
Purpose: Verify malformed/pathological behaviors fail gracefully
- Test:
TestRuntimeRobustness(internal/core/runtime/) - Coverage: one subtest per failure mode — count in the measured counts
- Acceptance: Typed errors, never panic, 60s timeout guard
Failure modes:
- Deadlocked action (join starvation)
- Decision with no satisfied guard
- State machine with dangling transition
- Sourceless accept...then at top level
- Calc with unbound parameter, surplus or unknown-named arguments, no result, non-calc target, direct or mutual recursion
- Constraint referencing missing feature
- Step budget exceeded
- Fork/join misuse (branches sharing a region, join with one incoming branch)
- Region pseudostate with no satisfied guard, or a cycle
- Non-numeric time trigger
- Send that reaches only its addressee, accept of an unsent type, send through an unconnected port
- History outside a composite state, or without a record or default
- Defer of a non-deferrable trigger
- Non-terminating do behavior
- Call of an unhandled operation, call argument of the wrong type
- perform of a missing action, perform reference cycle
Unit & Integration Tests¶
- Unit tests: Per-package coverage (lexer, parser, semantics, runtime)
- Integration tests: End-to-end workspace/REPL scenarios (internal/core/model/)
- Test fixtures:
testdata/*.sysml,testdata/*.kerml - Golden files: Expected parse/resolve/diagnostic outputs
Run all tests:
Run tests with coverage:
Contributing New Features¶
Grammar Features¶
When adding parser support for new SysML v2 constructs:
- ✅ Add representative example to
testdata/parse/*.sysml - ✅ Run
go test -run TestGolden -updateto generate golden - ✅ Verify
TestStdlibConformancestill passes (no regressions) - ✅ Add negative test case if construct has error conditions
Behavioral Features¶
When adding execution support for actions, states, calc, constraints, requirements:
- ✅ Add golden AST fixture to
internal/core/parser/testdata/parse/(if not already covered) - ✅ Implement semantics in
internal/core/runtime/(executor or evaluator) - ✅ Add conformance case:
.sysml+.expected.jsonininternal/core/runtime/testdata/conformance/ - ✅ Add golden trace case:
.trace.goldenfor ordering-sensitive features - ✅ Add robustness test for failure modes (deadlock, unbound params, missing refs)
- ✅ Update
docs/project/spec-compliance.mdwith semantic rule → implementation → test → status - ✅ Verify all tests pass:
go test ./internal/core/parser/ ./internal/core/runtime/
Test Coverage Policy¶
- Parser: Golden ASTs + negative tests + stdlib conformance
- Behavioral execution: Conformance + traces + robustness
- Semantics: Unit tests for resolution, type system, validation
- No coverage target: Quality over percentage (each feature has explicit test contract)
Rationale: Test contracts (what must pass) > coverage metrics (% lines hit). Each feature has defined acceptance criteria.