Skip to content

Commit 4693c9f

Browse files
committed
Make improvements based off comments
Create an execContainer and execGo to check output was expected and abstract container test calls Write to the image using testcontainers methods instead of cat Created go.mod through go commands Remove uneeded separated workspaces for go versions Each architecture is now a subtest "arch:amd64" for example
1 parent a189cd0 commit 4693c9f

File tree

1 file changed

+91
-109
lines changed

1 file changed

+91
-109
lines changed

internal/test/compilecheck/compile_check_test.go

Lines changed: 91 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,20 @@ func main() {
3535
}
3636
`
3737

38-
const goMod = `module compilecheck
39-
40-
go 1.19
41-
42-
require go.mongodb.org/mongo-driver/v2 v2.1.0
43-
44-
replace go.mongodb.org/mongo-driver/v2 => /mongo-go-driver
45-
`
46-
4738
// goVersions is the list of Go versions to test compilation against.
4839
// To run tests for specific version(s), use the -run flag:
4940
//
5041
// go test -v -run '^TestCompileCheck/go:1.19$'
5142
// go test -v -run '^TestCompileCheck/go:1\.(19|20)$'
52-
var goVersions = []string{"1.19", "1.20", "1.21", "1.22", "1.23", "1.24", "1.25"}
43+
var goVersions = []string{
44+
"1.19", // Minimum supported Go version for mongo-driver v2
45+
"1.20",
46+
"1.21",
47+
"1.22",
48+
"1.23",
49+
"1.24",
50+
"1.25", // Test suite Go Version
51+
}
5352
var architectures = []string{
5453
"386",
5554
"amd64",
@@ -65,21 +64,56 @@ var architectures = []string{
6564
"s390x",
6665
}
6766

68-
// goBuild constructs a build command that tries GOTOOLCHAIN=goX.Y.0 first, then falls back to goX.Y.
69-
func goBuild(ver, workdir string, env, extraFlags []string) string {
70-
baseEnv := "PATH=/usr/local/go/bin:$PATH"
71-
envStr := baseEnv
72-
if len(env) > 0 {
73-
envStr = strings.Join(env, " ") + " " + baseEnv
67+
// goExecConfig contains optional configuration for execGo.
68+
type goExecConfig struct {
69+
version string // Optional: Go version to use with GOTOOLCHAIN. If empty, uses default.
70+
env map[string]string // Optional: Additional environment variables.
71+
}
72+
73+
// execContainer executes a shell command in the container and validates its output.
74+
func execContainer(t *testing.T, c testcontainers.Container, cmd string) string {
75+
t.Helper()
76+
77+
exit, out, err := c.Exec(context.Background(), []string{"bash", "-lc", cmd})
78+
require.NoError(t, err)
79+
80+
b, err := io.ReadAll(out)
81+
require.NoError(t, err)
82+
require.Equal(t, 0, exit, "command failed: %s", b)
83+
84+
s := string(b)
85+
// Strip leading non-printable bytes (some Docker/TTY combos emit these).
86+
for len(s) > 0 && s[0] < 0x20 {
87+
s = s[1:]
88+
}
89+
return s
90+
}
91+
92+
// execGo runs a Go command, trying GOTOOLCHAIN=goX.Y.0 first, then goX.Y.
93+
func execGo(t *testing.T, c testcontainers.Container, cfg *goExecConfig, args ...string) string {
94+
t.Helper()
95+
96+
if cfg == nil {
97+
cfg = &goExecConfig{}
7498
}
7599

76-
flags := "-buildvcs=false"
77-
if len(extraFlags) > 0 {
78-
flags += " " + strings.Join(extraFlags, " ")
100+
envParts := []string{"PATH=/usr/local/go/bin:$PATH"}
101+
for k, v := range cfg.env {
102+
envParts = append(envParts, fmt.Sprintf("%s=%s", k, v))
103+
}
104+
envStr := strings.Join(envParts, " ")
105+
goArgs := strings.Join(args, " ")
106+
107+
var cmd string
108+
if cfg.version != "" {
109+
primaryCmd := fmt.Sprintf("%s GOTOOLCHAIN=go%s.0 go %s 2>&1", envStr, cfg.version, goArgs)
110+
fallbackCmd := fmt.Sprintf("%s GOTOOLCHAIN=go%s go %s 2>&1", envStr, cfg.version, goArgs)
111+
cmd = fmt.Sprintf("%s || %s", primaryCmd, fallbackCmd)
112+
} else {
113+
cmd = fmt.Sprintf("%s go %s 2>&1", envStr, goArgs)
79114
}
80115

81-
return fmt.Sprintf("cd %s && %s GOTOOLCHAIN=go%s.0 go build %s -o /dev/null main.go 2>&1 || %s GOTOOLCHAIN=go%s go build %s -o /dev/null main.go 2>&1",
82-
workdir, envStr, ver, flags, envStr, ver, flags)
116+
return execContainer(t, c, cmd)
83117
}
84118

85119
func TestCompileCheck(t *testing.T) {
@@ -95,6 +129,13 @@ func TestCompileCheck(t *testing.T) {
95129
Dockerfile: "Dockerfile",
96130
PrintBuildLog: true,
97131
},
132+
Files: []testcontainers.ContainerFile{
133+
{
134+
Reader: strings.NewReader(mainGo),
135+
ContainerFilePath: "/workspace/main.go",
136+
FileMode: 0o644,
137+
},
138+
},
98139
Entrypoint: []string{"tail", "-f", "/dev/null"},
99140
WorkingDir: "/workspace",
100141
}
@@ -108,115 +149,56 @@ func TestCompileCheck(t *testing.T) {
108149
require.NoError(t, container.Terminate(context.Background()))
109150
})
110151

111-
// Write main.go into the container.
112-
exitCode, outputReader, err := container.Exec(context.Background(), []string{"sh", "-c", fmt.Sprintf("cat > /workspace/main.go << 'GOFILE'\n%s\nGOFILE", mainGo)})
113-
require.NoError(t, err)
152+
// Initialize Go module and download dependencies using the test suite Go version.
153+
execGo(t, container, &goExecConfig{version: "1.25"}, "mod", "init", "compilecheck")
154+
execGo(t, container, nil, "mod", "edit", "-replace=go.mongodb.org/mongo-driver/v2=/mongo-go-driver")
155+
execGo(t, container, &goExecConfig{version: "1.25"}, "mod", "tidy")
114156

115-
output, err := io.ReadAll(outputReader)
116-
require.NoError(t, err)
117-
require.Equal(t, 0, exitCode, "failed to write main.go: %s", output)
118-
119-
// Write go.mod into the container.
120-
exitCode, outputReader, err = container.Exec(context.Background(), []string{"sh", "-c", fmt.Sprintf("cat > /workspace/go.mod << 'GOMOD'\n%s\nGOMOD", goMod)})
121-
require.NoError(t, err)
122-
123-
output, err = io.ReadAll(outputReader)
124-
require.NoError(t, err)
125-
require.Equal(t, 0, exitCode, "failed to write go.mod: %s", output)
126-
127-
exitCode, outputReader, err = container.Exec(context.Background(), []string{"sh", "-c", "cd /workspace && PATH=/usr/local/go/bin:$PATH go mod tidy 2>&1"})
128-
require.NoError(t, err)
129-
130-
output, err = io.ReadAll(outputReader)
131-
require.NoError(t, err)
132-
require.Equal(t, 0, exitCode, "failed to tidy dependencies: %s", output)
157+
// Set minimum Go version to what the driver claims (first version in our test list).
158+
execGo(t, container, nil, "mod", "edit", "-go="+goVersions[0])
133159

134160
for _, ver := range goVersions {
135161
ver := ver // capture
136162
t.Run("go:"+ver, func(t *testing.T) {
137163
t.Parallel()
138164

139-
t.Cleanup(func() {
140-
t.Logf("compilation checks passed for Go ver %s", ver)
141-
})
142-
143-
// Each version gets its own workspace to avoid conflicts when running in parallel.
144-
workspace := fmt.Sprintf("/workspace-%s", ver)
145-
146-
setupCmd := fmt.Sprintf("mkdir -p %[1]s && cp /workspace/main.go /workspace/go.mod /workspace/go.sum %[1]s/", workspace)
147-
exitCode, outputReader, err := container.Exec(context.Background(), []string{"sh", "-c", setupCmd})
148-
require.NoError(t, err)
149-
150-
output, err := io.ReadAll(outputReader)
151-
require.NoError(t, err)
152-
require.Equal(t, 0, exitCode, "failed to setup workspace: %s", output)
153-
154-
cmd := fmt.Sprintf("PATH=/usr/local/go/bin:$PATH GOTOOLCHAIN=go%[1]s.0+auto go version || PATH=/usr/local/go/bin:$PATH GOTOOLCHAIN=go%[1]s go version", ver)
165+
versionCfg := &goExecConfig{version: ver}
155166

156-
exit, out, err := container.Exec(context.Background(), []string{"bash", "-lc", cmd})
157-
require.NoError(t, err)
167+
// Verify the Go version is available.
168+
versionOutput := execGo(t, container, versionCfg, "version")
169+
require.Contains(t, versionOutput, "go"+ver, "unexpected go version: %s", versionOutput)
158170

159-
b, err := io.ReadAll(out)
160-
161-
require.NoError(t, err)
162-
require.Equal(t, 0, exit, "go version failed: %s", b)
163-
require.Contains(t, string(b), "go"+ver, "unexpected go version: %s", b)
164-
165-
// Standard build.
166-
exitCode, outputReader, err = container.Exec(context.Background(), []string{
167-
"sh", "-c", goBuild(ver, workspace, nil, nil),
168-
})
169-
require.NoError(t, err)
170-
171-
output, err = io.ReadAll(outputReader)
172-
require.NoError(t, err)
173-
174-
require.Equal(t, 0, exitCode, "standard build failed: %s", output)
171+
execGo(t, container, versionCfg, "build", "-buildvcs=false", "-o", "/dev/null", "main.go")
175172

176173
// Dynamic linking build.
177-
exitCode, outputReader, err = container.Exec(context.Background(), []string{
178-
"sh", "-c", goBuild(ver, workspace, nil, []string{"-buildmode=plugin"}),
179-
})
180-
require.NoError(t, err)
181-
182-
output, err = io.ReadAll(outputReader)
183-
require.NoError(t, err)
184-
185-
require.Equal(t, 0, exitCode, "dynamic linking build failed: %s", output)
174+
execGo(t, container, versionCfg, "build", "-buildvcs=false", "-buildmode=plugin", "-o", "/dev/null", "main.go")
186175

187176
// Build with build tags.
188-
cgoEnv := []string{
189-
"PKG_CONFIG_PATH=/root/install/libmongocrypt/lib/pkgconfig",
190-
"CGO_CFLAGS='-I/root/install/libmongocrypt/include'",
191-
"CGO_LDFLAGS='-L/root/install/libmongocrypt/lib -Wl,-rpath,/root/install/libmongocrypt/lib'",
192-
}
193-
exitCode, outputReader, err = container.Exec(context.Background(), []string{
194-
"sh", "-c", goBuild(ver, workspace, cgoEnv, []string{"-tags=cse,gssapi,mongointernal"}),
195-
})
196-
require.NoError(t, err)
197-
198-
output, err = io.ReadAll(outputReader)
199-
require.NoError(t, err)
200-
201-
require.Equal(t, 0, exitCode, "build with build tags failed: %s", output)
177+
execGo(t, container, &goExecConfig{
178+
version: ver,
179+
env: map[string]string{
180+
"PKG_CONFIG_PATH": "/root/install/libmongocrypt/lib/pkgconfig",
181+
"CGO_CFLAGS": "'-I/root/install/libmongocrypt/include'",
182+
"CGO_LDFLAGS": "'-L/root/install/libmongocrypt/lib -Wl,-rpath,/root/install/libmongocrypt/lib'",
183+
},
184+
}, "build", "-buildvcs=false", "-tags=cse,gssapi,mongointernal", "-o", "/dev/null", "main.go")
202185

203186
// Build for each architecture.
204187
for _, architecture := range architectures {
205188
architecture := architecture // capture
206189
t.Run("arch:"+architecture, func(t *testing.T) {
207190
t.Parallel()
208191

209-
archEnv := []string{"GOOS=linux", "GOARCH=" + architecture}
210-
exitCode, outputReader, err := container.Exec(
211-
context.Background(),
212-
[]string{"sh", "-c", goBuild(ver, workspace, archEnv, nil)},
213-
)
214-
require.NoError(t, err)
215-
216-
output, err := io.ReadAll(outputReader)
217-
require.NoError(t, err)
192+
// Standard build.
193+
execGo(t, container, &goExecConfig{
194+
version: ver,
195+
env: map[string]string{
196+
"GOOS": "linux",
197+
"GOARCH": architecture,
198+
},
199+
}, "build", "-buildvcs=false", "-o", "/dev/null", "main.go")
218200

219-
require.Equal(t, 0, exitCode, "build failed for architecture %s: %s", architecture, output)
201+
t.Logf("compilation checks passed for go%s on %s", ver, architecture)
220202
})
221203
}
222204
})

0 commit comments

Comments
 (0)