Skip to content

Commit f9f9e3c

Browse files
authored
Switch to "integration" tests (exercism#96)
1 parent 14b3785 commit f9f9e3c

27 files changed

+573
-226
lines changed

Diff for: .github/workflows/test.yml

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,20 @@ jobs:
1616
- name: Run linters
1717
uses: golangci/golangci-lint-action@v3
1818
with:
19-
version: v1.46
19+
version: v1.51
2020

2121
test:
2222
strategy:
2323
matrix:
2424
go-version: [1.18.x]
25-
# platform: [ubuntu-latest, macos-latest, windows-latest]
26-
platform: [ubuntu-latest]
25+
platform: [ubuntu-latest, windows-latest]
2726
runs-on: ${{ matrix.platform }}
2827
steps:
2928
- name: Install Go
3029
if: success()
3130
uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923
3231
with:
33-
go-version: ${{ matrix.go-version }}
32+
go-version: 1.18.x
3433
- name: Checkout code
3534
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
3635
- name: Run tests

Diff for: go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
module github.com/exercism/go-test-runner
22

33
go 1.18
4+
5+
require (
6+
github.com/davecgh/go-spew v1.1.1 // indirect
7+
github.com/pmezard/go-difflib v1.0.0 // indirect
8+
github.com/stretchr/testify v1.8.2 // indirect
9+
gopkg.in/yaml.v3 v3.0.1 // indirect
10+
)

Diff for: go.sum

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
8+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
9+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
10+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
11+
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
12+
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
13+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
14+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
16+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Diff for: integration_test.go

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"go/build"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"regexp"
10+
"runtime"
11+
"strings"
12+
"testing"
13+
14+
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
var regexReplacements = []struct {
19+
regexp *regexp.Regexp
20+
replaceStr string
21+
}{
22+
{
23+
// Test duration
24+
regexp: regexp.MustCompile(`\\t[0-9]+\.[0-9]+s`),
25+
replaceStr: "",
26+
},
27+
{
28+
// Test duration in brackets
29+
regexp: regexp.MustCompile(`\([0-9]+\.[0-9]+s\)`),
30+
replaceStr: "",
31+
},
32+
{
33+
// Pointer
34+
regexp: regexp.MustCompile(`\+?\(?0x[0-9a-f]+\)?`),
35+
replaceStr: "",
36+
},
37+
{
38+
// Goroutine
39+
regexp: regexp.MustCompile(`goroutine [0-9]+`),
40+
replaceStr: "goroutine x",
41+
},
42+
{
43+
// Line number
44+
regexp: regexp.MustCompile(`\.go:[0-9]+(:[0-9]+)?`),
45+
replaceStr: ".go",
46+
},
47+
}
48+
49+
func TestIntegration(t *testing.T) {
50+
tests := []struct {
51+
inputDir string
52+
expected string
53+
}{
54+
{
55+
// This test case covers the case the code under test does not compile,
56+
// i.e. "go build ." would fail.
57+
inputDir: "./testrunner/testdata/practice/broken",
58+
expected: "./testrunner/testdata/expected/broken.json",
59+
},
60+
{
61+
// This test case covers the case that the test code does not compile,
62+
// i.e. "go build ." would succeed but "go test" returns compilation errors.
63+
inputDir: "./testrunner/testdata/practice/missing_func",
64+
expected: "./testrunner/testdata/expected/missing_func.json",
65+
},
66+
{
67+
inputDir: "./testrunner/testdata/practice/broken_import",
68+
expected: "./testrunner/testdata/expected/broken_import.json",
69+
},
70+
{
71+
inputDir: "./testrunner/testdata/practice/passing",
72+
expected: "./testrunner/testdata/expected/passing.json",
73+
},
74+
{
75+
inputDir: "./testrunner/testdata/practice/pkg_level_error",
76+
expected: "./testrunner/testdata/expected/pkg_level_error.json",
77+
},
78+
{
79+
inputDir: "./testrunner/testdata/practice/failing",
80+
expected: "./testrunner/testdata/expected/failing.json",
81+
},
82+
{
83+
inputDir: "./testrunner/testdata/concept/auto_assigned_task_ids",
84+
expected: "./testrunner/testdata/expected/auto_assigned_task_ids.json",
85+
},
86+
{
87+
inputDir: "./testrunner/testdata/concept/explicit_task_ids",
88+
expected: "./testrunner/testdata/expected/explicit_task_ids.json",
89+
},
90+
{
91+
inputDir: "./testrunner/testdata/concept/missing_task_ids",
92+
expected: "./testrunner/testdata/expected/missing_task_ids.json",
93+
},
94+
}
95+
96+
goExe, err := exec.LookPath("go")
97+
require.NoError(t, err, "failed to find go executable")
98+
99+
goRoot := os.Getenv("GOROOT")
100+
if goRoot == "" {
101+
goRoot = build.Default.GOROOT
102+
}
103+
104+
currentDir, err := os.Getwd()
105+
require.NoError(t, err, "failed to determine current directory")
106+
107+
for _, tt := range tests {
108+
t.Run(tt.inputDir, func(t *testing.T) {
109+
err := os.RemoveAll("./outdir")
110+
require.NoError(t, err, "failed to clean up output directory")
111+
112+
var stdout, stderr bytes.Buffer
113+
cmd := &exec.Cmd{
114+
Path: goExe,
115+
Args: []string{goExe, "run", ".", tt.inputDir, "outdir"},
116+
Stdout: &stdout,
117+
Stderr: &stderr,
118+
}
119+
err = cmd.Run()
120+
require.NoErrorf(t, err, "failed to execute test runner: %s %s", stdout.String(), stderr.String())
121+
122+
resultBytes, err := os.ReadFile("./outdir/results.json")
123+
require.NoError(t, err, "failed to read results")
124+
125+
result := sanitizeResult(string(resultBytes), []string{goExe, currentDir, goRoot})
126+
127+
expected, err := os.ReadFile(tt.expected)
128+
require.NoError(t, err, "failed to read expected result file")
129+
130+
assert.JSONEq(t, string(expected), result)
131+
})
132+
}
133+
}
134+
135+
func sanitizeResult(s string, paths []string) string {
136+
result := s
137+
138+
for _, p := range pathVariations(paths) {
139+
result = strings.ReplaceAll(result, p, "PATH_PLACEHOLDER")
140+
}
141+
142+
if runtime.GOOS == "windows" {
143+
result = strings.ReplaceAll(result, `\n.//`, `\n./`)
144+
result = strings.ReplaceAll(result, `\n.\\`, `\n./`)
145+
result = strings.ReplaceAll(result, `\n.\`, `\n./`)
146+
}
147+
148+
for _, replacement := range regexReplacements {
149+
result = replacement.regexp.ReplaceAllString(result, replacement.replaceStr)
150+
}
151+
152+
return result
153+
}
154+
155+
func pathVariations(paths []string) []string {
156+
result := []string{}
157+
for _, p := range paths {
158+
normalizedPath := filepath.ToSlash(p)
159+
result = append(result, normalizedPath)
160+
161+
if runtime.GOOS == "windows" {
162+
// On windows, the paths that are included in the test results can have
163+
// various formats. We try to include all variants here so we catch
164+
// everything when we do the replace later.
165+
result = append(result, strings.ReplaceAll(normalizedPath, "/", "//"))
166+
result = append(result, strings.ReplaceAll(normalizedPath, "/", `\`))
167+
result = append(result, strings.ReplaceAll(normalizedPath, "/", `\\`))
168+
}
169+
}
170+
171+
return result
172+
}

0 commit comments

Comments
 (0)