test: expand dsl/runner coverage and add go-test CI workflow
All checks were successful
go-test / go test (push) Successful in 56s
All checks were successful
go-test / go test (push) Successful in 56s
- Add dsl/matcher_test.go covering ExactMatcher, ContainsMatcher,
RegexMatcher, NumericEpsMatcher, AnyOrderMatcher and NoMatcher —
previously 0% — including epsilon, count mismatch, unparsable
numbers, invalid regex, and splitLines edge cases.
- Add dsl/ast_test.go for the new Test.SetInputFile / SetStdin /
SetOutputFile / SetStdout helpers and Pattern.IsDirMode.
- Add dsl/build_string_test.go covering BuildProfile.String,
WarningLevel.String and BuildConfig.MergeFrom (wrapper, defines
into nil map, defines override existing, nil src).
- Add dsl/merge_test.go driving mergeFiles to 100%: legacy build
fields, duplicate toolchain/build/group from include, binary /
sources / normalize_crlf / trim_trailing_ws propagation, local
overrides of timeout and memory_limit.
- Add dsl/parser_features_test.go for parseTest / parseGroup happy
paths that were missing: file/outFile, env, wrapper, per-test
timeout/memory overrides, non-zero exitCode, scoring partial /
all_or_none and unknown scoring.
- Add dsl/parser_errors_test.go, a 54-case table-driven test that
hits every `expect(...)` error branch in parseGroup and parseTest
(missing LPAREN/RPAREN/LBRACE/RBRACE/ASSIGN, wrong token types on
weight/timeout/memory_limit/scoring/env/wrapper/file/outFile, and
unclosed blocks).
- Add dsl/parser_misc_test.go covering parsePattern dir-mode with
args, unknown pattern field, non-ident in pattern, top-level
binary / sources / normalize_crlf / trim_trailing_ws / bare-int
memory_limit, parseBool invalid ident and non-ident, matcher
without an operator, validateBuilds legacy+structured conflict.
- Add dsl/build_parser_test.go covering every BuildConfig field
(sources, includes, sanitize, link, extra, platforms, compilers,
defines), OS overrides on named builds, nested / duplicate OS
override errors, unknown build / profile / warnings / platform,
missing = on assign-string and assign-string-list, define(...)
error cases, and parseToolchainsBlock (duplicate name, missing
platforms, bad name token, unknown field, unknown compiler class,
binary and class propagation).
- Add dsl/lexer_test.go for Token.String, TokenType.String UNKNOWN
branch, line comments, unterminated string and heredoc, unknown
escape sequence, escape decoding, unexpected character, every
K/M/G/KiB/MiB/GiB size suffix, ms/s/m duration suffixes, negative
integer lexing and float literals.
- Extend runner/result_test.go with Status.String and
TestResult.addFailure (both previously 0%).
- Add .gitea/workflows/go-test.yml running `go vet` and
`go test -race -coverprofile=coverage.out ./...` on push,
pull_request and manual dispatch, uploading coverage.out as an
artifact.
Coverage: dsl 60.5% -> 85%+, runner 29.0% -> 30.5%.
This commit is contained in:
187
dsl/matcher_test.go
Normal file
187
dsl/matcher_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package dsl
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExactMatcherPass(t *testing.T) {
|
||||
m := ExactMatcher{Value: "hello\n"}
|
||||
if errs := m.Match("stdout", "hello\n"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExactMatcherMismatch(t *testing.T) {
|
||||
m := ExactMatcher{Value: "hello\n"}
|
||||
errs := m.Match("stdout", "world\n")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "stdout mismatch") {
|
||||
t.Errorf("error missing label: %q", errs[0])
|
||||
}
|
||||
if !strings.Contains(errs[0], "hello") || !strings.Contains(errs[0], "world") {
|
||||
t.Errorf("error missing values: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsMatcherPass(t *testing.T) {
|
||||
m := ContainsMatcher{Substr: "needle"}
|
||||
if errs := m.Match("stdout", "haystack with a needle inside"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsMatcherMissing(t *testing.T) {
|
||||
m := ContainsMatcher{Substr: "needle"}
|
||||
errs := m.Match("stdout", "only hay here")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "needle") {
|
||||
t.Errorf("error missing substring: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexMatcherPass(t *testing.T) {
|
||||
m := RegexMatcher{Pattern: `^hello .*!$`}
|
||||
if errs := m.Match("stdout", "hello world!"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexMatcherMismatch(t *testing.T) {
|
||||
m := RegexMatcher{Pattern: `^\d+$`}
|
||||
errs := m.Match("stdout", "not-a-number")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "does not match") {
|
||||
t.Errorf("error unexpected: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexMatcherInvalidPattern(t *testing.T) {
|
||||
m := RegexMatcher{Pattern: `[`}
|
||||
errs := m.Match("stdout", "anything")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "invalid regex") {
|
||||
t.Errorf("error unexpected: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherPassWithinEps(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1.0 2.0 3.0"}
|
||||
if errs := m.Match("stdout", "1.005 1.999 3.0"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherExceedsEps(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1.0 2.0"}
|
||||
errs := m.Match("stdout", "1.0 2.5")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "number[1]") {
|
||||
t.Errorf("error missing index: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherCountMismatch(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2 3"}
|
||||
errs := m.Match("stdout", "1 2")
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("want 1 error, got %d: %v", len(errs), errs)
|
||||
}
|
||||
if !strings.Contains(errs[0], "expected 3 numbers, got 2") {
|
||||
t.Errorf("error unexpected: %q", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherBadExpected(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 foo"}
|
||||
errs := m.Match("stdout", "1 2")
|
||||
if len(errs) != 1 || !strings.Contains(errs[0], "cannot parse expected") {
|
||||
t.Errorf("want expected-parse error, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherBadActual(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2"}
|
||||
errs := m.Match("stdout", "1 bar")
|
||||
if len(errs) != 1 || !strings.Contains(errs[0], "cannot parse actual") {
|
||||
t.Errorf("want actual-parse error, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEpsMatcherMultipleErrors(t *testing.T) {
|
||||
m := NumericEpsMatcher{Epsilon: 0.01, Value: "1 2 3"}
|
||||
errs := m.Match("stdout", "9 8 7")
|
||||
if len(errs) != 3 {
|
||||
t.Errorf("want 3 errors, got %d: %v", len(errs), errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyOrderMatcherPassReordered(t *testing.T) {
|
||||
m := AnyOrderMatcher{Lines: []string{"a", "b", "c"}}
|
||||
if errs := m.Match("stdout", "c\nb\na\n"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyOrderMatcherPassNoTrailingNewline(t *testing.T) {
|
||||
m := AnyOrderMatcher{Lines: []string{"x", "y"}}
|
||||
if errs := m.Match("stdout", "y\nx"); errs != nil {
|
||||
t.Errorf("expected pass, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyOrderMatcherCountMismatch(t *testing.T) {
|
||||
m := AnyOrderMatcher{Lines: []string{"a", "b"}}
|
||||
errs := m.Match("stdout", "a\nb\nc\n")
|
||||
if len(errs) != 1 || !strings.Contains(errs[0], "expected 2 lines, got 3") {
|
||||
t.Errorf("want count error, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyOrderMatcherLineMismatch(t *testing.T) {
|
||||
m := AnyOrderMatcher{Lines: []string{"a", "b"}}
|
||||
errs := m.Match("stdout", "a\nz\n")
|
||||
if len(errs) != 1 || !strings.Contains(errs[0], "line mismatch") {
|
||||
t.Errorf("want line mismatch, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyOrderMatcherEmptyBoth(t *testing.T) {
|
||||
m := AnyOrderMatcher{Lines: []string{}}
|
||||
if errs := m.Match("stdout", ""); errs != nil {
|
||||
t.Errorf("expected pass on empty, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoMatcherAlwaysPasses(t *testing.T) {
|
||||
m := NoMatcher{}
|
||||
if errs := m.Match("stdout", "anything at all\n"); errs != nil {
|
||||
t.Errorf("NoMatcher should never fail, got %v", errs)
|
||||
}
|
||||
if errs := m.Match("stderr", ""); errs != nil {
|
||||
t.Errorf("NoMatcher should never fail on empty, got %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitLinesEmpty(t *testing.T) {
|
||||
if got := splitLines(""); len(got) != 0 {
|
||||
t.Errorf("splitLines(\"\") = %v, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitLinesTrailingNewline(t *testing.T) {
|
||||
got := splitLines("a\nb\n")
|
||||
if len(got) != 2 || got[0] != "a" || got[1] != "b" {
|
||||
t.Errorf("splitLines trailing = %v, want [a b]", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user