diff --git a/runner/runner.go b/runner/runner.go index 540a559..6a35abb 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -407,6 +407,8 @@ func (r *Runner) runGroup(g *dsl.Group) *GroupResult { t.Wrapper = g.Wrapper } + r.normalizeExpectations(t) + tr := r.runTest(t) gr.Tests = append(gr.Tests, tr) if tr.Status == StatusPass { @@ -609,6 +611,40 @@ func buildExecCmd(ctx context.Context, wrapper, binary string, args []string) *e return exec.CommandContext(ctx, full[0], full[1:]...) } +// normalizeExpectations applies the suite's CRLF/whitespace normalization to a +// test's *expected* values, mirroring what normalizeOutput does to the actual +// output. Without this, an expected value loaded from disk on Windows (e.g. an +// answer file checked out with CRLF) would never match LF-normalized actual +// output even with normalize_crlf enabled. +func (r *Runner) normalizeExpectations(t *dsl.Test) { + if !r.file.NormalizeCRLF && !r.file.TrimTrailingWS { + return + } + t.Stdout = r.normalizeMatcher(t.Stdout) + t.Stderr = r.normalizeMatcher(t.Stderr) + for k, v := range t.OutFiles { + t.OutFiles[k] = normalizeOutput(v, r.file) + } +} + +// normalizeMatcher normalizes the expected payload of the matchers that compare +// whole-output text. Regex and numeric matchers are left untouched: their +// patterns are authored deliberately and must not be rewritten. +func (r *Runner) normalizeMatcher(m dsl.Matcher) dsl.Matcher { + switch x := m.(type) { + case dsl.ExactMatcher: + return dsl.ExactMatcher{Value: normalizeOutput(x.Value, r.file)} + case dsl.AnyOrderMatcher: + lines := make([]string, len(x.Lines)) + for i, l := range x.Lines { + lines[i] = normalizeOutput(l, r.file) + } + return dsl.AnyOrderMatcher{Lines: lines} + default: + return m + } +} + func normalizeOutput(s string, f *dsl.File) string { if f.NormalizeCRLF { s = strings.ReplaceAll(s, "\r\n", "\n")