Files
dingtalk-workspace-cli/test/scripts/darwin_native_contract_test.go
chichuan 2d3f820f91 ci: keep the darwin-gated upgrade self-heal test reachable on macOS
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.
2026-08-04 17:59:59 +08:00

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"
}