205 lines
6.9 KiB
Go
205 lines
6.9 KiB
Go
package validation
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRunCompilesTargetsAfterTestAndVetInSelectionOrder(t *testing.T) {
|
|
logPath := installGoShim(t, "", false)
|
|
targets := []Target{{OS: "linux", Arch: "amd64"}, {OS: "linux", Arch: "arm64"}, {OS: "darwin", Arch: "arm64"}}
|
|
err := Run(context.Background(), Request{
|
|
RepositoryDir: t.TempDir(),
|
|
Settings: Settings{FullVet: true, CompileTargets: targets},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Run() error = %v", err)
|
|
}
|
|
|
|
var commands []string
|
|
for _, command := range readGoShimLog(t, logPath) {
|
|
if strings.HasPrefix(command, "test ") || strings.HasPrefix(command, "vet ") || strings.HasPrefix(command, "build ") {
|
|
commands = append(commands, command)
|
|
}
|
|
}
|
|
if len(commands) != len(targets)+2 || !strings.HasPrefix(commands[0], "test ") || !strings.HasPrefix(commands[1], "vet ") {
|
|
t.Fatalf("validation commands = %q, want test, vet, then one build per target", commands)
|
|
}
|
|
for index, target := range targets {
|
|
command := commands[index+2]
|
|
if !strings.HasPrefix(command, "build ") {
|
|
t.Fatalf("command %d = %q, want package build", index+2, command)
|
|
}
|
|
for _, argument := range []string{"build", "-mod=readonly", "-o " + os.DevNull, "./..."} {
|
|
if !strings.Contains(" "+command+" ", " "+argument+" ") {
|
|
t.Errorf("build command %q does not contain %q", command, argument)
|
|
}
|
|
}
|
|
if got, want := strings.Fields(command)[0], "build"; got != want {
|
|
t.Errorf("command = %q, want %q", got, want)
|
|
}
|
|
if target.OS == "" || target.Arch == "" {
|
|
t.Fatalf("test target is incomplete: %#v", target)
|
|
}
|
|
}
|
|
|
|
environments, err := os.ReadFile(os.Getenv("RELEASEKIT_GO_ENV_LOG"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := strings.Split(strings.TrimSpace(string(environments)), "\n")
|
|
if len(got) != len(targets) {
|
|
t.Fatalf("static build environments = %q, want %d entries", got, len(targets))
|
|
}
|
|
for index, target := range targets {
|
|
if want := fmt.Sprintf("%s/%s|0", target.OS, target.Arch); got[index] != want {
|
|
t.Errorf("static build environment %d = %q, want %q", index, got[index], want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunAttributesTargetCompileFailureAndStops(t *testing.T) {
|
|
logPath := installGoShim(t, "darwin/arm64", false)
|
|
targets := []Target{{OS: "linux", Arch: "amd64"}, {OS: "darwin", Arch: "arm64"}, {OS: "windows", Arch: "amd64"}}
|
|
err := Run(context.Background(), Request{
|
|
RepositoryDir: t.TempDir(),
|
|
Settings: Settings{FullVet: true, CompileTargets: targets},
|
|
})
|
|
var failure *Failure
|
|
if !errors.As(err, &failure) || failure.Check != PackageCompile {
|
|
t.Fatalf("Run() error = %v, want a package-compile failure", err)
|
|
}
|
|
if len(failure.Findings) != 1 || failure.Findings[0].Target != "darwin/arm64" {
|
|
t.Fatalf("package compilation findings = %#v, want target darwin/arm64", failure.Findings)
|
|
}
|
|
var exitError *exec.ExitError
|
|
if !errors.As(err, &exitError) {
|
|
t.Fatalf("Run() error = %v, want the Go command failure preserved", err)
|
|
}
|
|
var commands []string
|
|
for _, command := range readGoShimLog(t, logPath) {
|
|
if strings.HasPrefix(command, "test ") || strings.HasPrefix(command, "vet ") || strings.HasPrefix(command, "build ") {
|
|
commands = append(commands, command)
|
|
}
|
|
}
|
|
if len(commands) != 4 || !strings.HasPrefix(commands[0], "test ") || !strings.HasPrefix(commands[1], "vet ") || !strings.HasPrefix(commands[2], "build ") || !strings.HasPrefix(commands[3], "build ") {
|
|
t.Fatalf("validation commands after target failure = %q, want test, vet, two builds", commands)
|
|
}
|
|
}
|
|
|
|
func TestRunRejectsIncompleteCompileTargets(t *testing.T) {
|
|
installGoShim(t, "", false)
|
|
for _, target := range []Target{{OS: "linux"}, {Arch: "amd64"}, {OS: " ", Arch: "amd64"}, {OS: "linux/other", Arch: "amd64"}} {
|
|
err := Run(context.Background(), Request{
|
|
RepositoryDir: t.TempDir(),
|
|
Settings: Settings{CompileTargets: []Target{target}},
|
|
})
|
|
var failure *Failure
|
|
if !errors.As(err, &failure) || failure.Check != PackageCompile {
|
|
t.Errorf("Run(%#v) error = %v, want a package-compile validation failure", target, err)
|
|
continue
|
|
}
|
|
if len(failure.Findings) != 1 || failure.Findings[0].Target != target.OS+"/"+target.Arch {
|
|
t.Errorf("Run(%#v) findings = %#v, want the incomplete target", target, failure.Findings)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunCompilesSourceOnlyAndExecutablePackagesWithoutArtifacts(t *testing.T) {
|
|
fixtures := []struct {
|
|
name string
|
|
files map[string]string
|
|
}{
|
|
{
|
|
name: "source library",
|
|
files: map[string]string{
|
|
"library.go": "package fixture\n\nconst Value = 1\n",
|
|
},
|
|
},
|
|
{
|
|
name: "single main",
|
|
files: map[string]string{
|
|
"library.go": "package fixture\n\nconst Value = 1\n",
|
|
"cmd/app/main.go": "package main\n\nfunc main() {}\n",
|
|
},
|
|
},
|
|
{
|
|
name: "multiple mains with repeated basenames",
|
|
files: map[string]string{
|
|
"library.go": "package fixture\n\nconst Value = 1\n",
|
|
"cmd/one/tool/main.go": "package main\n\nfunc main() {}\n",
|
|
"cmd/two/tool/main.go": "package main\n\nfunc main() {}\n",
|
|
},
|
|
},
|
|
}
|
|
for _, fixture := range fixtures {
|
|
t.Run(fixture.name, func(t *testing.T) {
|
|
module := writeCompileFixture(t, fixture.files)
|
|
before := readCompileFixture(t, module, fixture.files)
|
|
var targets []Target
|
|
if fixture.name == "source library" {
|
|
targets = []Target{{OS: "linux", Arch: "amd64"}, {OS: "linux", Arch: "arm64"}, {OS: "darwin", Arch: "arm64"}}
|
|
} else {
|
|
targets = []Target{{OS: "linux", Arch: "amd64"}}
|
|
}
|
|
if err := Run(context.Background(), Request{
|
|
RepositoryDir: module,
|
|
Settings: Settings{CompileTargets: targets},
|
|
}); err != nil {
|
|
t.Fatalf("Run() error = %v", err)
|
|
}
|
|
after := readCompileFixture(t, module, fixture.files)
|
|
for path, contents := range before {
|
|
if string(after[path]) != string(contents) {
|
|
t.Errorf("candidate file %q changed during compilation", path)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func writeCompileFixture(t *testing.T, files map[string]string) string {
|
|
t.Helper()
|
|
module := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(module, "go.mod"), []byte("module example.com/compilefixture\n\ngo 1.25.0\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for path, contents := range files {
|
|
filename := filepath.Join(module, filepath.FromSlash(path))
|
|
if err := os.MkdirAll(filepath.Dir(filename), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filename, []byte(contents), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return module
|
|
}
|
|
|
|
func readCompileFixture(t *testing.T, module string, files map[string]string) map[string][]byte {
|
|
t.Helper()
|
|
contents := make(map[string][]byte, len(files)+1)
|
|
for _, path := range append([]string{"go.mod"}, fixturePaths(files)...) {
|
|
data, err := os.ReadFile(filepath.Join(module, filepath.FromSlash(path)))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
contents[path] = data
|
|
}
|
|
return contents
|
|
}
|
|
|
|
func fixturePaths(files map[string]string) []string {
|
|
paths := make([]string, 0, len(files))
|
|
for path := range files {
|
|
paths = append(paths, path)
|
|
}
|
|
return paths
|
|
}
|