refactor: modernize stdlib usage and move matchers into dsl
All checks were successful
build-dsl-smoke / Build judge (push) Successful in 16s
build-dsl-smoke / debug / clang / linux (push) Successful in 6s
build-dsl-smoke / debug / gcc / linux (push) Successful in 7s
build-dsl-smoke / release / clang / linux (push) Successful in 8s
build-dsl-smoke / release / gcc / linux (push) Successful in 9s
build-dsl-smoke / sanitized / gcc / linux (push) Successful in 6s
build-dsl-smoke / sanitized / clang / linux (push) Successful in 9s
build-dsl-smoke / debug-valgrind / gcc / linux (push) Successful in 15s
build-dsl-smoke / debug / clang / windows (push) Successful in 15s
build-dsl-smoke / debug / msvc / windows (push) Successful in 19s
build-dsl-smoke / release / clang / windows (push) Successful in 18s
build-dsl-smoke / release / msvc / windows (push) Successful in 18s
build-dsl-smoke / SUMMARY (push) Successful in 4s

- Move Matcher types and matching logic from runner/matcher.go into
  the dsl package as methods on the Matcher types. Runner now calls
  t.Stdout.Match(label, actual) instead of type-switching via a
  package-level applyMatcher helper.
- Replace custom contains/containsString helpers with slices.Contains
  in dsl/build.go and runner/compiler.go.
- Use maps.Copy instead of manual map copy in BuildConfig.MergeFrom.
- Adopt strings.SplitSeq, strings.CutPrefix and the `for range N` loop
  form in runner/limiter_linux.go.
- Ignore example/imdb build artifact.
This commit is contained in:
2026-04-15 21:24:11 +03:00
parent 52202abb53
commit c85c65ed49
8 changed files with 123 additions and 139 deletions

View File

@@ -3,6 +3,7 @@ package runner
import (
"fmt"
"runtime"
"slices"
"sort"
"strings"
@@ -161,7 +162,7 @@ func compileMSVC(cfg dsl.BuildConfig, tc Toolchain, outputPath string) []string
argv = append(argv, "/W4", "/permissive-")
}
if containsString(cfg.Sanitize, "address") && cfg.Profile != dsl.ProfileSanitized {
if slices.Contains(cfg.Sanitize, "address") && cfg.Profile != dsl.ProfileSanitized {
argv = append(argv, "/fsanitize=address")
}
@@ -195,12 +196,3 @@ func sortedKeys(m map[string]string) []string {
sort.Strings(keys)
return keys
}
func containsString(xs []string, x string) bool {
for _, v := range xs {
if v == x {
return true
}
}
return false
}

View File

@@ -102,9 +102,9 @@ func createScopeCgroup() (string, error) {
return "", fmt.Errorf("read /proc/self/cgroup: %w", err)
}
var rel string
for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") {
if strings.HasPrefix(line, "0::") {
rel = strings.TrimPrefix(line, "0::")
for line := range strings.SplitSeq(strings.TrimSpace(string(data)), "\n") {
if after, ok := strings.CutPrefix(line, "0::"); ok {
rel = after
break
}
}
@@ -200,7 +200,7 @@ func (l *linuxLimiter) collect() limitStats {
}
}
if data, err := os.ReadFile(filepath.Join(l.cgPath, "memory.events")); err == nil {
for _, line := range strings.Split(string(data), "\n") {
for line := range strings.SplitSeq(string(data), "\n") {
fields := strings.Fields(line)
if len(fields) != 2 {
continue
@@ -217,7 +217,7 @@ func (l *linuxLimiter) cleanup() {
if l.cgPath == "" {
return
}
for i := 0; i < 10; i++ {
for range 10 {
err := os.Remove(l.cgPath)
if err == nil || os.IsNotExist(err) {
l.cgPath = ""

View File

@@ -1,134 +0,0 @@
package runner
import (
"fmt"
"math"
"regexp"
"sort"
"strconv"
"strings"
"github.com/Mond1c/judge/dsl"
)
func applyMatcher(label string, m dsl.Matcher, actual string) []string {
switch m := m.(type) {
case dsl.NoMatcher:
return nil
case dsl.ExactMatcher:
if actual != m.Value {
return []string{fmt.Sprintf(
"%s mismatch:\n expected: %q\n actual: %q",
label, m.Value, actual,
)}
}
return nil
case dsl.ContainsMatcher:
if !strings.Contains(actual, m.Substr) {
return []string{fmt.Sprintf(
"%s: expected to contain %q, got %q",
label, m.Substr, actual,
)}
}
return nil
case dsl.RegexMatcher:
re, err := regexp.Compile(m.Pattern)
if err != nil {
return []string{fmt.Sprintf("%s: invalid regex %q: %v", label, m.Pattern, err)}
}
if !re.MatchString(actual) {
return []string{fmt.Sprintf(
"%s: %q does not match regex %q",
label, actual, m.Pattern,
)}
}
return nil
case dsl.NumericEpsMatcher:
errs := matchNumericEps(label, m, actual)
return errs
case dsl.AnyOrderMatcher:
return matchAnyOrder(label, m, actual)
default:
return []string{fmt.Sprintf("unknown matcher type %T", m)}
}
}
func matchNumericEps(label string, m dsl.NumericEpsMatcher, actual string) []string {
expectedNums, err := parseNumbers(m.Value)
if err != nil {
return []string{fmt.Sprintf("%s: cannot parse expected numbers %q: %v", label, m.Value, err)}
}
actualNums, err := parseNumbers(actual)
if err != nil {
return []string{fmt.Sprintf("%s: cannot parse actual numbers %q: %v", label, actual, err)}
}
if len(expectedNums) != len(actualNums) {
return []string{fmt.Sprintf(
"%s: expected %d numbers, got %d (expected=%q, actual=%q)",
label, len(expectedNums), len(actualNums), m.Value, actual,
)}
}
var errs []string
for i, exp := range expectedNums {
act := actualNums[i]
if math.Abs(exp-act) > m.Epsilon {
errs = append(errs, fmt.Sprintf(
"%s: number[%d] expected %.10g ± %.10g, got %.10g",
label, i, exp, m.Epsilon, act,
))
}
}
return errs
}
func parseNumbers(s string) ([]float64, error) {
fields := strings.Fields(s)
nums := make([]float64, 0, len(fields))
for _, f := range fields {
n, err := strconv.ParseFloat(f, 64)
if err != nil {
return nil, fmt.Errorf("not a number: %q", f)
}
nums = append(nums, n)
}
return nums, nil
}
func matchAnyOrder(label string, m dsl.AnyOrderMatcher, actual string) []string {
actualLines := splitLines(actual)
expectedLines := make([]string, len(m.Lines))
copy(expectedLines, m.Lines)
sort.Strings(actualLines)
sort.Strings(expectedLines)
if len(actualLines) != len(expectedLines) {
return []string{fmt.Sprintf(
"%s anyOrder: expected %d lines, got %d",
label, len(expectedLines), len(actualLines),
)}
}
var errs []string
for i := range expectedLines {
if actualLines[i] != expectedLines[i] {
errs = append(errs, fmt.Sprintf(
"%s anyOrder: line mismatch: expected %q, got %q",
label, expectedLines[i], actualLines[i],
))
}
}
return errs
}
func splitLines(s string) []string {
s = strings.TrimRight(s, "\n")
if s == "" {
return []string{}
}
return strings.Split(s, "\n")
}

View File

@@ -558,10 +558,10 @@ func (r *Runner) runTest(t *dsl.Test) *TestResult {
tr.addFailure("exit code: expected %d, got %d", *t.ExitCode, actualCode)
}
for _, f := range applyMatcher("stdout", t.Stdout, tr.ActualStdout) {
for _, f := range t.Stdout.Match("stdout", tr.ActualStdout) {
tr.addFailure("%s", f)
}
for _, f := range applyMatcher("stderr", t.Stderr, tr.ActualStderr) {
for _, f := range t.Stderr.Match("stderr", tr.ActualStderr) {
tr.addFailure("%s", f)
}