Compare commits
2 Commits
1fec40fc9e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cf2584ce5 | |||
| b44cccf8d1 |
@@ -19,6 +19,10 @@ type File struct {
|
||||
Binary string
|
||||
Sources string
|
||||
|
||||
// MaxOutput caps captured stdout/stderr per test (bytes). Zero means use the
|
||||
// runner default. Raise it for suites whose expected output is large.
|
||||
MaxOutput int64
|
||||
|
||||
NormalizeCRLF bool
|
||||
TrimTrailingWS bool
|
||||
|
||||
|
||||
@@ -307,6 +307,17 @@ func (p *Parser) parseFile() (*File, error) {
|
||||
}
|
||||
f.MemoryLimit = n
|
||||
|
||||
case "max_output":
|
||||
p.advance()
|
||||
if _, err := p.expect(TOKEN_ASSIGN); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := p.parseSize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.MaxOutput = n
|
||||
|
||||
case "group":
|
||||
g, err := p.parseGroup(f.Timeout, f.MemoryLimit)
|
||||
if err != nil {
|
||||
|
||||
@@ -37,6 +37,24 @@ group("g") {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMaxOutput(t *testing.T) {
|
||||
src := `
|
||||
build "make"
|
||||
max_output = 64M
|
||||
group("g") {
|
||||
weight = 1.0
|
||||
test("t") { stdout = "ok\n" }
|
||||
}
|
||||
`
|
||||
f, _, err := Parse(src)
|
||||
if err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if f.MaxOutput != 64*1024*1024 {
|
||||
t.Errorf("MaxOutput = %d, want %d", f.MaxOutput, 64*1024*1024)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePatternStageAllMode(t *testing.T) {
|
||||
src := `
|
||||
build "make"
|
||||
|
||||
96
runner/normalize_test.go
Normal file
96
runner/normalize_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Mond1c/judge/dsl"
|
||||
)
|
||||
|
||||
// Under a wrapper, only args that name a staged file become absolute; numeric
|
||||
// and flag args must be passed through verbatim (regression: a numeric arg "1"
|
||||
// was being turned into "<tmpdir>/1", breaking programs that take non-path
|
||||
// arguments).
|
||||
func TestAbsoluteArgsOnlyRewritesStagedFiles(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "01.in"), []byte("x"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := absoluteArgs(dir, []string{"$.txt", "1", "1", "3", "01.in", "--flag"})
|
||||
want := []string{"$.txt", "1", "1", "3", filepath.Join(dir, "01.in"), "--flag"}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Errorf("arg[%d] = %q, want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A staged/expected value loaded from disk on Windows can carry CRLF. With
|
||||
// normalize_crlf the runner must normalize the EXPECTED side too, otherwise it
|
||||
// can never match the LF-normalized actual output.
|
||||
func TestNormalizeExpectationsCRLF(t *testing.T) {
|
||||
r := &Runner{file: &dsl.File{NormalizeCRLF: true}}
|
||||
|
||||
test := &dsl.Test{
|
||||
Stdout: dsl.ExactMatcher{Value: "1\r\n2\r\n"},
|
||||
Stderr: dsl.NoMatcher{},
|
||||
OutFiles: map[string]string{"out.txt": "a\r\nb\r\n"},
|
||||
}
|
||||
r.normalizeExpectations(test)
|
||||
|
||||
m, ok := test.Stdout.(dsl.ExactMatcher)
|
||||
if !ok {
|
||||
t.Fatalf("stdout matcher type changed: %T", test.Stdout)
|
||||
}
|
||||
if m.Value != "1\n2\n" {
|
||||
t.Errorf("expected stdout normalized to %q, got %q", "1\n2\n", m.Value)
|
||||
}
|
||||
if got := test.OutFiles["out.txt"]; got != "a\nb\n" {
|
||||
t.Errorf("expected outfile normalized to %q, got %q", "a\nb\n", got)
|
||||
}
|
||||
|
||||
// And the normalized expected must match normalized actual end-to-end.
|
||||
if fails := test.Stdout.Match("stdout", normalizeOutput("1\r\n2\r\n", r.file)); len(fails) != 0 {
|
||||
t.Errorf("normalized expected should match normalized actual, got %v", fails)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeExpectationsTrimTrailingWS(t *testing.T) {
|
||||
r := &Runner{file: &dsl.File{TrimTrailingWS: true}}
|
||||
test := &dsl.Test{
|
||||
Stdout: dsl.AnyOrderMatcher{Lines: []string{"foo ", "bar\t"}},
|
||||
Stderr: dsl.NoMatcher{},
|
||||
}
|
||||
r.normalizeExpectations(test)
|
||||
|
||||
m := test.Stdout.(dsl.AnyOrderMatcher)
|
||||
if m.Lines[0] != "foo" || m.Lines[1] != "bar" {
|
||||
t.Errorf("anyOrder lines not trimmed: %q", m.Lines)
|
||||
}
|
||||
}
|
||||
|
||||
// Regex matchers must never be rewritten: a literal \r in the pattern is
|
||||
// meaningful and normalization would corrupt it.
|
||||
func TestNormalizeExpectationsLeavesRegexAlone(t *testing.T) {
|
||||
r := &Runner{file: &dsl.File{NormalizeCRLF: true}}
|
||||
test := &dsl.Test{
|
||||
Stdout: dsl.RegexMatcher{Pattern: "a\r\nb"},
|
||||
Stderr: dsl.NoMatcher{},
|
||||
}
|
||||
r.normalizeExpectations(test)
|
||||
if m := test.Stdout.(dsl.RegexMatcher); m.Pattern != "a\r\nb" {
|
||||
t.Errorf("regex pattern was modified: %q", m.Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeExpectationsNoopWhenDisabled(t *testing.T) {
|
||||
r := &Runner{file: &dsl.File{}}
|
||||
want := "1\r\n2\r\n"
|
||||
test := &dsl.Test{Stdout: dsl.ExactMatcher{Value: want}, Stderr: dsl.NoMatcher{}}
|
||||
r.normalizeExpectations(test)
|
||||
if m := test.Stdout.(dsl.ExactMatcher); m.Value != want {
|
||||
t.Errorf("value changed with normalization disabled: %q", m.Value)
|
||||
}
|
||||
}
|
||||
@@ -488,8 +488,12 @@ func (r *Runner) runTest(t *dsl.Test) *TestResult {
|
||||
cmd.Stdin = strings.NewReader(*t.Stdin)
|
||||
}
|
||||
|
||||
stdout := &cappedBuffer{limit: MaxOutputBytes}
|
||||
stderr := &cappedBuffer{limit: MaxOutputBytes}
|
||||
outLimit := MaxOutputBytes
|
||||
if r.file.MaxOutput > 0 {
|
||||
outLimit = int(r.file.MaxOutput)
|
||||
}
|
||||
stdout := &cappedBuffer{limit: outLimit}
|
||||
stderr := &cappedBuffer{limit: outLimit}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
@@ -589,14 +593,22 @@ func (r *Runner) runTest(t *dsl.Test) *TestResult {
|
||||
return tr
|
||||
}
|
||||
|
||||
// absoluteArgs rewrites only those arguments that name a file staged in the
|
||||
// test's working directory to an absolute path. This helps wrappers (gdb,
|
||||
// valgrind) that may resolve paths from a different directory, without
|
||||
// corrupting non-path arguments — a numeric arg like "1" must stay "1", not
|
||||
// become "<tmpdir>/1". The process runs with Dir=dir anyway, so relative paths
|
||||
// already resolve there; this is purely a safety net for genuine file args.
|
||||
func absoluteArgs(dir string, args []string) []string {
|
||||
out := make([]string, len(args))
|
||||
for i, a := range args {
|
||||
if !filepath.IsAbs(a) {
|
||||
out[i] = filepath.Join(dir, a)
|
||||
} else {
|
||||
out[i] = a
|
||||
if a != "" && !filepath.IsAbs(a) {
|
||||
if _, err := os.Stat(filepath.Join(dir, a)); err == nil {
|
||||
out[i] = filepath.Join(dir, a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
out[i] = a
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user