97 lines
3.7 KiB
Go
97 lines
3.7 KiB
Go
// Package gates holds the tests of the zone's BUILD gates — the checks that run before any code
|
|
// does. They live in their own package because their subject is the Makefile and go.mod rather than
|
|
// any package's behaviour, and a gate nobody tests is a gate that quietly stops gating: this file
|
|
// exists because the toolchain floor was raised in three documents and one echo, while the check
|
|
// itself went on accepting the version it was raised against (re-check of the P5 dofix, FP5-9).
|
|
package gates
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// zoneRoot is where the Makefile and go.mod live, two levels up from this package.
|
|
const zoneRoot = "../.."
|
|
|
|
// The gate is exercised with versions THIS HOST does not have. That is the whole point of asserting
|
|
// on the comparison rather than on a run: a gate tested only against the toolchain that happens to be
|
|
// installed proves nothing about the one it is meant to refuse.
|
|
func TestTheToolchainGateComparesVersionsRatherThanMatchingThem(t *testing.T) {
|
|
min := minVersion(t)
|
|
if min != "1.26.6" {
|
|
// Not a failure — the floor is allowed to move — but the table below is written against it.
|
|
t.Logf("GO_MIN_VERSION is %s; the cases below assume the 1.26.x floor", min)
|
|
}
|
|
for _, c := range []struct {
|
|
version string
|
|
accept bool
|
|
}{
|
|
{"go1.26.6", true}, // the floor itself
|
|
{"go1.26.7", true}, // a later patch
|
|
{"go1.26.10", true}, // double digits: the regex this replaced ordered 1.26.10 BELOW 1.26.9
|
|
{"go1.27.0", true}, // a later minor
|
|
{"go2.0.0", true},
|
|
{"go1.26.5", false}, // the release the floor was raised against — the case that used to pass
|
|
{"go1.26.4", false},
|
|
{"go1.25.9", false},
|
|
{"go1.9.9", false}, // one-digit minor must not sort above 1.26
|
|
{"go1.26.6rc1", false}, // a prerelease does not carry the fixes its number promises
|
|
{"devel go1.27-abcdef", false},
|
|
} {
|
|
err := makeTarget(t, "version-check", "GO_VERSION="+c.version)
|
|
if c.accept && err != nil {
|
|
t.Errorf("the gate refused %s, which is at or above the floor %s: %v", c.version, min, err)
|
|
}
|
|
if !c.accept && err == nil {
|
|
t.Errorf("the gate ACCEPTED %s, below the floor %s", c.version, min)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The other half of the same floor: the Makefile is a convenience, go.mod is what every build reads —
|
|
// including one that never calls make. The two must name the same version, or a build that skips the
|
|
// battery gets a toolchain the battery would have refused.
|
|
func TestGoModPinsTheSameToolchainTheBatteryDemands(t *testing.T) {
|
|
src, err := os.ReadFile(filepath.Join(zoneRoot, "go.mod"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := regexp.MustCompile(`(?m)^toolchain go(\S+)$`).FindSubmatch(src)
|
|
if m == nil {
|
|
t.Fatal("go.mod carries no toolchain directive: the floor then lives only in the Makefile, and a build that does not call make ignores it")
|
|
}
|
|
if got, want := string(m[1]), minVersion(t); got != want {
|
|
t.Errorf("go.mod pins the toolchain at %s and the battery demands %s", got, want)
|
|
}
|
|
}
|
|
|
|
func minVersion(t *testing.T) string {
|
|
t.Helper()
|
|
src, err := os.ReadFile(filepath.Join(zoneRoot, "Makefile"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := regexp.MustCompile(`(?m)^GO_MIN_VERSION := (\S+)$`).FindSubmatch(src)
|
|
if m == nil {
|
|
t.Fatal("the Makefile no longer declares GO_MIN_VERSION")
|
|
}
|
|
return string(m[1])
|
|
}
|
|
|
|
func makeTarget(t *testing.T, args ...string) error {
|
|
t.Helper()
|
|
if _, err := exec.LookPath("make"); err != nil {
|
|
t.Skip("make is not on this host: the build gates cannot be exercised")
|
|
}
|
|
cmd := exec.CommandContext(t.Context(), "make", args...)
|
|
cmd.Dir = zoneRoot
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil && !strings.Contains(string(out), "required") {
|
|
t.Fatalf("make %v failed for a reason other than the gate: %s", args, out)
|
|
}
|
|
return err
|
|
}
|