Skip to content

Commit 7ee57d6

Browse files
authored
tests: Fix test flake from timestamp sanitization (#2713)
* Fix test flake from timestamp sanitization When timestamps "overlap", sanitization did not work correctly. For example: in 1s ... in 2.1s The first substitution would replace 1s => 0s in 0s ... in 2.0s The second substitution is predetermined, and replaces 2.1s => 0s, but 2.1s is no longer in the string. * Use new sanitization function in tests, remove old function
1 parent cd75985 commit 7ee57d6

File tree

3 files changed

+90
-7
lines changed

3 files changed

+90
-7
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.16
55
require (
66
github.com/cpuguy83/go-md2man/v2 v2.0.0
77
github.com/go-errors/errors v1.4.0
8+
github.com/google/go-cmp v0.5.6
89
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
910
github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac
1011
github.com/otiai10/copy v1.6.0

pkg/test/runner/runner.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import (
2020
"os"
2121
"os/exec"
2222
"path/filepath"
23-
"regexp"
2423
"strings"
2524
"testing"
25+
26+
"github.com/google/go-cmp/cmp"
2627
)
2728

2829
// Runner runs an e2e test
@@ -247,16 +248,22 @@ func (r *Runner) runFnEval() error {
247248
return nil
248249
}
249250

250-
// Match strings starting with [PASS] or [FAIL] and ending with " in N...". We capture the duration portion
251-
var timestampRegex = regexp.MustCompile(`\[(?:PASS|FAIL)].* in ([0-9].*)`)
252-
253251
func sanitizeTimestamps(stderr string) string {
254252
// Output will have non-deterministic output timestamps. We will replace these to static message for
255253
// stable comparison in tests.
256-
for _, m := range timestampRegex.FindAllStringSubmatch(stderr, -1) {
257-
stderr = strings.ReplaceAll(stderr, m[1], "0s")
254+
var sanitized []string
255+
for _, line := range strings.Split(stderr, "\n") {
256+
// [PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" in 2.0s
257+
if strings.HasPrefix(line, "[PASS]") || strings.HasPrefix(line, "[FAIL]") {
258+
tokens := strings.Fields(line)
259+
if len(tokens) == 4 && tokens[2] == "in" {
260+
tokens[3] = "0s"
261+
line = strings.Join(tokens, " ")
262+
}
263+
}
264+
sanitized = append(sanitized, line)
258265
}
259-
return stderr
266+
return strings.Join(sanitized, "\n")
260267
}
261268

262269
// IsFnResultExpected determines if function results are expected for this testcase.
@@ -467,10 +474,12 @@ func (r *Runner) compareResult(cnt int, exitErr error, stdout string, stderr str
467474
func (r *Runner) compareOutput(stdout string, stderr string) error {
468475
expectedStderr := r.testCase.Config.StdErr
469476
if !strings.Contains(stderr, expectedStderr) {
477+
r.t.Logf("stderr diff is %s", cmp.Diff(expectedStderr, stderr))
470478
return fmt.Errorf("wanted stderr %q, got %q", expectedStderr, stderr)
471479
}
472480
expectedStdout := r.testCase.Config.StdOut
473481
if !strings.Contains(stdout, expectedStdout) {
482+
r.t.Logf("stdout diff is %s", cmp.Diff(expectedStdout, stdout))
474483
return fmt.Errorf("wanted stdout %q, got %q", expectedStdout, stdout)
475484
}
476485
return nil

pkg/test/runner/sanitize_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package runner
2+
3+
import (
4+
"testing"
5+
6+
"github.com/google/go-cmp/cmp"
7+
)
8+
9+
func TestSanitizeTimestamps(t *testing.T) {
10+
grid := []struct {
11+
Name string
12+
Input string
13+
Output string
14+
}{
15+
{
16+
Name: "Prefix match: 12s and 12.1s",
17+
Input: `
18+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
19+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 12s
20+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" on 1 resource(s)
21+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" in 12.1s
22+
`,
23+
Output: `
24+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
25+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 0s
26+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" on 1 resource(s)
27+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" in 0s
28+
`,
29+
},
30+
{
31+
Name: "Suffix match: 1s and 0.1s",
32+
Input: `
33+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
34+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 1s
35+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" on 1 resource(s)
36+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" in 0.1s
37+
`,
38+
Output: `
39+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
40+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 0s
41+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" on 1 resource(s)
42+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" in 0s
43+
`,
44+
},
45+
{
46+
Name: "Only substitute matching lines",
47+
Input: `
48+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
49+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 1s
50+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:1s\" on 1 resource(s)
51+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" notin 1s
52+
`,
53+
Output: `
54+
[RUNNING] \"gcr.io/kpt-fn/starlark:v0.2.1\"
55+
[PASS] \"gcr.io/kpt-fn/starlark:v0.2.1\" in 0s
56+
[RUNNING] \"gcr.io/kpt-fn/set-namespace:1s\" on 1 resource(s)
57+
[PASS] \"gcr.io/kpt-fn/set-namespace:v0.1.3\" notin 1s
58+
`,
59+
},
60+
}
61+
62+
for _, g := range grid {
63+
g := g // Avoid range go-tcha
64+
t.Run(g.Name, func(t *testing.T) {
65+
got := sanitizeTimestamps(g.Input)
66+
want := g.Output
67+
68+
if diff := cmp.Diff(got, want); diff != "" {
69+
t.Errorf("unexpected results (-want, +got): %s", diff)
70+
}
71+
})
72+
}
73+
}

0 commit comments

Comments
 (0)