blob: eead25047c43b08caa867abbbbd824fc9d5853cd [file] [log] [blame]
Dmitri Shuralyov42c85332023-12-19 15:40:58 -05001// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package task_test
6
7import (
8 "flag"
9 "io/fs"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "reflect"
14 "strings"
15 "testing"
16
17 "github.com/google/go-cmp/cmp"
18 "golang.org/x/build/internal/task"
19)
20
21var readPKGFlag = flag.String("read-pkg", "", "Path to a Go macOS .pkg installer to run TestReadBinariesFromPKG with.")
22
23func TestReadBinariesFromPKG(t *testing.T) {
24 if *readPKGFlag == "" {
25 t.Skip("skipping manual test since -read-pkg flag is not set")
26 }
27 if _, err := exec.LookPath("pkgutil"); err != nil {
28 // Since this is a manual test, we can afford to fail
29 // rather than skip if required dependencies are missing.
30 t.Fatal("required dependency pkgutil not found in PATH:", err)
31 }
32 if ext := filepath.Ext(*readPKGFlag); ext != ".pkg" {
33 t.Fatalf("got input file extension %q, want .pkg", ext)
34 }
35 f, err := os.Open(*readPKGFlag)
36 if err != nil {
37 t.Fatal(err)
38 }
39 defer f.Close()
40
41 got, err := task.ReadBinariesFromPKG(f)
42 if err != nil {
43 t.Fatal(err)
44 }
45 want, err := readBinariesFromPKGUsingXcode(t, *readPKGFlag)
46 if err != nil {
47 t.Fatal(err)
48 }
49 // Compare with reflect.DeepEqual first for speed;
50 // there's 100 MB or so of binary data to compare.
51 if !reflect.DeepEqual(want, got) {
52 t.Log("got files:")
53 for path := range got {
54 t.Log("\t" + path)
55 }
56 t.Log("want files:")
57 for path := range want {
58 t.Log("\t" + path)
59 }
60 t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got))
61 }
62}
63
64// readBinariesFromPKGUsingXcode implements the same functionality as
65// ReadBinariesFromPKG but uses Xcode's pkgutil as its implementation.
66func readBinariesFromPKGUsingXcode(t *testing.T, pkgPath string) (map[string][]byte, error) {
67 expanded := filepath.Join(t.TempDir(), "expanded")
68 out, err := exec.Command("pkgutil", "--expand-full", pkgPath, expanded).CombinedOutput()
69 if err != nil {
70 t.Fatalf("pkgutil failed: %v\noutput: %s", err, out)
71 }
72 var binaries = make(map[string][]byte) // Relative path starting with "go/" → binary data.
73 root := filepath.Join(expanded, "org.golang.go.pkg/Payload/usr/local")
74 err = filepath.Walk(root, func(path string, fi fs.FileInfo, err error) error {
75 if err != nil {
76 return err
77 }
78 name, err := filepath.Rel(root, path)
79 if err != nil {
80 return err
81 }
82 if !strings.HasPrefix(name, "go/bin/") && !strings.HasPrefix(name, "go/pkg/tool/") {
83 return nil
84 }
85 if !fi.Mode().IsRegular() || fi.Mode().Perm()&0100 == 0 {
86 return nil
87 }
88 b, err := os.ReadFile(path)
89 if err != nil {
90 return err
91 }
92 binaries[name] = b
93 return nil
94 })
95 if err != nil {
96 t.Fatal(err)
97 }
98 return binaries, nil
99}