Skip to content

Commit dda78b9

Browse files
authored
test: rework snapshot normalizes (#353)
1 parent 016ef51 commit dda78b9

File tree

3 files changed

+138
-34
lines changed

3 files changed

+138
-34
lines changed

.golangci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ linters:
3030
regexp:
3131
files:
3232
- '!**/internal/cachedregexp/**'
33-
- '!**/main_test.go'
33+
- '!**/main_normalize_test.go'
3434
deny:
3535
- pkg: regexp
3636
desc:

main_normalize_test.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"regexp"
7+
"runtime"
8+
"strings"
9+
"testing"
10+
11+
"github.com/g-rath/osv-detector/internal/cachedregexp"
12+
)
13+
14+
// normalizeFilePaths attempts to normalize any file paths in the given `output`
15+
// so that they can be compared reliably regardless of the file path separator
16+
// being used.
17+
//
18+
// Namely, escaped forward slashes are replaced with backslashes.
19+
func normalizeFilePaths(t *testing.T, output string) string {
20+
t.Helper()
21+
22+
return strings.ReplaceAll(strings.ReplaceAll(output, "\\\\", "/"), "\\", "/")
23+
}
24+
25+
// normalizeRootDirectory attempts to replace references to the current working
26+
// directory with "<rootdir>", in order to reduce the noise of the cmp diff
27+
func normalizeRootDirectory(t *testing.T, str string) string {
28+
t.Helper()
29+
30+
cwd, err := os.Getwd()
31+
if err != nil {
32+
t.Fatalf("failed to get current directory: %v", err)
33+
}
34+
35+
cwd = normalizeFilePaths(t, cwd)
36+
37+
// file uris with Windows end up with three slashes, so we normalize that too
38+
str = strings.ReplaceAll(str, "file:///"+cwd, "file://<rootdir>")
39+
str = strings.ReplaceAll(str, cwd, "<rootdir>")
40+
41+
// Replace versions without the root as well
42+
str = strings.ReplaceAll(str, pathWithoutRoot(t, cwd), "<rootdir>")
43+
44+
return str
45+
}
46+
47+
// normalizeUserCacheDirectory attempts to replace references to the current working
48+
// directory with "<tempdir>", in order to reduce the noise of the cmp diff
49+
func normalizeUserCacheDirectory(t *testing.T, str string) string {
50+
t.Helper()
51+
52+
cacheDir, err := os.UserCacheDir()
53+
if err != nil {
54+
t.Errorf("could not get user cache (%v) - results and diff might be inaccurate!", err)
55+
}
56+
57+
cacheDir = normalizeFilePaths(t, cacheDir)
58+
59+
// file uris with Windows end up with three slashes, so we normalize that too
60+
str = strings.ReplaceAll(str, "file:///"+cacheDir, "file://<tempdir>")
61+
62+
return strings.ReplaceAll(str, cacheDir, "<tempdir>")
63+
}
64+
65+
// normalizeTempDirectory attempts to replace references to the temp directory
66+
// with "<tempdir>", to ensure tests pass across different OSs
67+
func normalizeTempDirectory(t *testing.T, str string) string {
68+
t.Helper()
69+
70+
//nolint:gocritic // ensure that the directory doesn't end with a trailing slash
71+
tempDir := normalizeFilePaths(t, filepath.Join(os.TempDir()))
72+
re := cachedregexp.MustCompile(regexp.QuoteMeta(tempDir+`/osv-detector-test-`) + `\d+`)
73+
str = re.ReplaceAllString(str, "<tempdir>")
74+
75+
// Replace versions without the root as well
76+
re = cachedregexp.MustCompile(regexp.QuoteMeta(pathWithoutRoot(t, tempDir)+`/osv-detector-test-`) + `\d+`)
77+
78+
return re.ReplaceAllString(str, "<tempdir>")
79+
}
80+
81+
// normalizeDatabaseStats attempts to replace references to database stats (such as
82+
// the number of vulnerabilities and the time that the database was last updated)
83+
// in the output with %% wildcards, in order to reduce the noise of the cmp diff
84+
func normalizeDatabaseStats(t *testing.T, str string) string {
85+
t.Helper()
86+
87+
re := cachedregexp.MustCompile(`(\w+) \(\d+ vulnerabilities, including withdrawn - last updated \w{3}, \d\d \w{3} \d{4} [012]\d:\d\d:\d\d GMT\)`)
88+
89+
return re.ReplaceAllString(str, "$1 (%% vulnerabilities, including withdrawn - last updated %%)")
90+
}
91+
92+
// normalizeErrors attempts to replace error messages on alternative OSs with their
93+
// known linux equivalents, to ensure tests pass across different OSs
94+
func normalizeErrors(t *testing.T, str string) string {
95+
t.Helper()
96+
97+
str = strings.ReplaceAll(str, "The system cannot find the path specified.", "no such file or directory")
98+
str = strings.ReplaceAll(str, "The system cannot find the file specified.", "no such file or directory")
99+
100+
return str
101+
}
102+
103+
// normalizeSnapshot applies a series of normalizes to the buffer from a std stream like stdout and stderr
104+
func normalizeSnapshot(t *testing.T, str string) string {
105+
t.Helper()
106+
107+
for _, normalizer := range []func(t *testing.T, str string) string{
108+
normalizeFilePaths,
109+
normalizeRootDirectory,
110+
normalizeTempDirectory,
111+
normalizeUserCacheDirectory,
112+
normalizeDatabaseStats,
113+
normalizeErrors,
114+
} {
115+
str = normalizer(t, str)
116+
}
117+
118+
return str
119+
}
120+
121+
func pathWithoutRoot(t *testing.T, str string) string {
122+
t.Helper()
123+
124+
// Replace versions without the root as well
125+
var root string
126+
if runtime.GOOS == "windows" {
127+
root = filepath.VolumeName(str) + "\\"
128+
}
129+
130+
if strings.HasPrefix(str, "/") {
131+
root = "/"
132+
}
133+
134+
return str[len(root):]
135+
}

main_test.go

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,6 @@ type cliTestCase struct {
3737
around func(t *testing.T) func()
3838
}
3939

40-
// Attempts to normalize any file paths in the given `output` so that they can
41-
// be compared reliably regardless of the file path separator being used.
42-
//
43-
// Namely, escaped forward slashes are replaced with backslashes.
44-
func normalizeFilePaths(output string) string {
45-
return strings.ReplaceAll(strings.ReplaceAll(output, "\\\\", "/"), "\\", "/")
46-
}
47-
48-
// wildcardDatabaseStats attempts to replace references to database stats (such as
49-
// the number of vulnerabilities and the time that the database was last updated)
50-
// in the output with %% wildcards, in order to reduce the noise of the cmp diff
51-
func wildcardDatabaseStats(str string) string {
52-
re := cachedregexp.MustCompile(`(\w+) \(\d+ vulnerabilities, including withdrawn - last updated \w{3}, \d\d \w{3} \d{4} [012]\d:\d\d:\d\d GMT\)`)
53-
54-
return re.ReplaceAllString(str, "$1 (%% vulnerabilities, including withdrawn - last updated %%)")
55-
}
56-
57-
// normalizeWindowsErrors attempts to replace Windows versions of errors with their Linux versions
58-
func normalizeWindowsErrors(str string) string {
59-
str = strings.ReplaceAll(str, "The system cannot find the path specified.", "no such file or directory")
60-
str = strings.ReplaceAll(str, "The system cannot find the file specified.", "no such file or directory")
61-
62-
return str
63-
}
64-
6540
func testCli(t *testing.T, tc cliTestCase) {
6641
t.Helper()
6742

@@ -70,14 +45,8 @@ func testCli(t *testing.T, tc cliTestCase) {
7045

7146
ec := run(tc.args, stdoutBuffer, stderrBuffer)
7247

73-
stdout := normalizeFilePaths(stdoutBuffer.String())
74-
stderr := normalizeFilePaths(stderrBuffer.String())
75-
76-
stdout = wildcardDatabaseStats(stdout)
77-
stderr = wildcardDatabaseStats(stderr)
78-
79-
stdout = normalizeWindowsErrors(stdout)
80-
stderr = normalizeWindowsErrors(stderr)
48+
stdout := normalizeSnapshot(t, stdoutBuffer.String())
49+
stderr := normalizeSnapshot(t, stderrBuffer.String())
8150

8251
if ec != tc.exit {
8352
t.Errorf("cli exited with code %d, not %d", ec, tc.exit)

0 commit comments

Comments
 (0)