Narrowing the macOS internal/app -run pattern orphaned TestValidateNewBinary_RecoversFromUnsignedDarwin: it is the only runtime.GOOS != "darwin" gated test in the package, the Ubuntu race shard skips it on Linux, and the platform coverage gate only runs ^(TestAllShortcuts|TestCrossPlatformCoverage). No CI job selected it any more, so it could never run or fail again. - Add the self-heal test back to the macOS -run pattern. - Add the (CrossPlatformCoverage)? group the Windows pattern already has, which also recovers TestCrossPlatformCoverageAuthMigrateKeychainRemainingBranches. - Attribute vacuous runs: the two skip paths now name the branch that went unverified, and DWS_REQUIRE_AMFI_SELF_HEAL=1 escalates such a run to a hard failure on a host that does enforce amfid. GitHub's hosted macOS runners do not reproduce the amfid kill, so this test has been silently skipping there all along. - Restore step-timeout headroom: 10m + 5m exactly equalled the 15m job budget, leaving none for setup. The keychain/auth step drops to 6m (measured 2m43s). - Add a contract test that couples the macOS -run pattern to the set of darwin-gated tests in internal/app, so the next narrowing fails loudly instead of silently orphaning one.
142 lines
4.0 KiB
Go
142 lines
4.0 KiB
Go
package scripts_test
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestMacOSNativeJobKeepsDarwinGatedAppTestsReachable couples the macOS test
|
|
// job's -run pattern to the set of darwin-gated tests in internal/app.
|
|
//
|
|
// The Ubuntu race shard runs the whole package but skips anything gated on
|
|
// runtime.GOOS != "darwin", and the platform coverage gate only runs
|
|
// ^(TestAllShortcuts|TestCrossPlatformCoverage). That makes the macOS job the
|
|
// only place a darwin-gated internal/app test can execute, so narrowing its
|
|
// -run pattern can silently orphan one — which is exactly what happened in
|
|
// #857 before review caught it.
|
|
func TestMacOSNativeJobKeepsDarwinGatedAppTestsReachable(t *testing.T) {
|
|
root, err := filepath.Abs(filepath.Join("..", ".."))
|
|
if err != nil {
|
|
t.Fatalf("Abs(repo root) error = %v", err)
|
|
}
|
|
|
|
pattern := macOSAppRunPattern(t, root)
|
|
matcher, err := regexp.Compile(pattern)
|
|
if err != nil {
|
|
t.Fatalf("Compile(macOS -run pattern %q) error = %v", pattern, err)
|
|
}
|
|
|
|
gated := darwinGatedTestNames(t, filepath.Join(root, "internal", "app"))
|
|
if len(gated) == 0 {
|
|
t.Fatal("found no darwin-gated tests in internal/app; the scanner is broken or the gate style changed")
|
|
}
|
|
|
|
for _, name := range gated {
|
|
if !matcher.MatchString(name) {
|
|
t.Errorf(
|
|
"darwin-gated test %s is unreachable in CI: the Ubuntu shard skips it on Linux and the macOS -run pattern %q does not select it",
|
|
name,
|
|
pattern,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// macOSAppRunPattern extracts the -run pattern the macOS job applies to
|
|
// ./internal/app from the CI workflow.
|
|
func macOSAppRunPattern(t *testing.T, root string) string {
|
|
t.Helper()
|
|
|
|
data, err := os.ReadFile(filepath.Join(root, ".github", "workflows", "ci.yml"))
|
|
if err != nil {
|
|
t.Fatalf("ReadFile(ci.yml) error = %v", err)
|
|
}
|
|
workflow := string(data)
|
|
|
|
start := strings.Index(workflow, "\n test-darwin:\n")
|
|
end := strings.Index(workflow, "\n test-windows:\n")
|
|
if start < 0 || end <= start {
|
|
t.Fatal("ci.yml missing macOS test job boundaries")
|
|
}
|
|
|
|
matches := regexp.MustCompile(`\./internal/app -run '([^']+)'`).FindAllStringSubmatch(workflow[start:end], -1)
|
|
if len(matches) != 1 {
|
|
t.Fatalf("macOS test job ./internal/app -run invocation count = %d, want exactly 1", len(matches))
|
|
}
|
|
return matches[0][1]
|
|
}
|
|
|
|
// darwinGatedTestNames returns every Test function in dir whose body gates on
|
|
// runtime.GOOS != "darwin".
|
|
func darwinGatedTestNames(t *testing.T, dir string) []string {
|
|
t.Helper()
|
|
|
|
entries, err := filepath.Glob(filepath.Join(dir, "*_test.go"))
|
|
if err != nil {
|
|
t.Fatalf("Glob(%s) error = %v", dir, err)
|
|
}
|
|
|
|
var names []string
|
|
fset := token.NewFileSet()
|
|
for _, entry := range entries {
|
|
file, parseErr := parser.ParseFile(fset, entry, nil, parser.SkipObjectResolution)
|
|
if parseErr != nil {
|
|
t.Fatalf("ParseFile(%s) error = %v", entry, parseErr)
|
|
}
|
|
for _, decl := range file.Decls {
|
|
fn, ok := decl.(*ast.FuncDecl)
|
|
if !ok || fn.Body == nil || !strings.HasPrefix(fn.Name.Name, "Test") {
|
|
continue
|
|
}
|
|
if gatesOnNonDarwin(fn.Body) {
|
|
names = append(names, fn.Name.Name)
|
|
}
|
|
}
|
|
}
|
|
return names
|
|
}
|
|
|
|
// gatesOnNonDarwin reports whether body contains a `runtime.GOOS != "darwin"`
|
|
// comparison, the idiom this repo uses to skip a test off macOS.
|
|
func gatesOnNonDarwin(body *ast.BlockStmt) bool {
|
|
found := false
|
|
ast.Inspect(body, func(node ast.Node) bool {
|
|
if found {
|
|
return false
|
|
}
|
|
binary, ok := node.(*ast.BinaryExpr)
|
|
if !ok || binary.Op != token.NEQ {
|
|
return true
|
|
}
|
|
if !isRuntimeGOOS(binary.X) {
|
|
return true
|
|
}
|
|
literal, ok := binary.Y.(*ast.BasicLit)
|
|
if !ok || literal.Kind != token.STRING {
|
|
return true
|
|
}
|
|
if literal.Value == `"darwin"` {
|
|
found = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return found
|
|
}
|
|
|
|
// isRuntimeGOOS reports whether expr is the selector runtime.GOOS.
|
|
func isRuntimeGOOS(expr ast.Expr) bool {
|
|
selector, ok := expr.(*ast.SelectorExpr)
|
|
if !ok || selector.Sel.Name != "GOOS" {
|
|
return false
|
|
}
|
|
ident, ok := selector.X.(*ast.Ident)
|
|
return ok && ident.Name == "runtime"
|
|
}
|