blob: eead25047c43b08caa867abbbbd824fc9d5853cd [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package task_test
import (
"flag"
"io/fs"
"os"
"os/exec"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/build/internal/task"
)
var readPKGFlag = flag.String("read-pkg", "", "Path to a Go macOS .pkg installer to run TestReadBinariesFromPKG with.")
func TestReadBinariesFromPKG(t *testing.T) {
if *readPKGFlag == "" {
t.Skip("skipping manual test since -read-pkg flag is not set")
}
if _, err := exec.LookPath("pkgutil"); err != nil {
// Since this is a manual test, we can afford to fail
// rather than skip if required dependencies are missing.
t.Fatal("required dependency pkgutil not found in PATH:", err)
}
if ext := filepath.Ext(*readPKGFlag); ext != ".pkg" {
t.Fatalf("got input file extension %q, want .pkg", ext)
}
f, err := os.Open(*readPKGFlag)
if err != nil {
t.Fatal(err)
}
defer f.Close()
got, err := task.ReadBinariesFromPKG(f)
if err != nil {
t.Fatal(err)
}
want, err := readBinariesFromPKGUsingXcode(t, *readPKGFlag)
if err != nil {
t.Fatal(err)
}
// Compare with reflect.DeepEqual first for speed;
// there's 100 MB or so of binary data to compare.
if !reflect.DeepEqual(want, got) {
t.Log("got files:")
for path := range got {
t.Log("\t" + path)
}
t.Log("want files:")
for path := range want {
t.Log("\t" + path)
}
t.Errorf("mismatch (-want +got):\n%s", cmp.Diff(want, got))
}
}
// readBinariesFromPKGUsingXcode implements the same functionality as
// ReadBinariesFromPKG but uses Xcode's pkgutil as its implementation.
func readBinariesFromPKGUsingXcode(t *testing.T, pkgPath string) (map[string][]byte, error) {
expanded := filepath.Join(t.TempDir(), "expanded")
out, err := exec.Command("pkgutil", "--expand-full", pkgPath, expanded).CombinedOutput()
if err != nil {
t.Fatalf("pkgutil failed: %v\noutput: %s", err, out)
}
var binaries = make(map[string][]byte) // Relative path starting with "go/" → binary data.
root := filepath.Join(expanded, "org.golang.go.pkg/Payload/usr/local")
err = filepath.Walk(root, func(path string, fi fs.FileInfo, err error) error {
if err != nil {
return err
}
name, err := filepath.Rel(root, path)
if err != nil {
return err
}
if !strings.HasPrefix(name, "go/bin/") && !strings.HasPrefix(name, "go/pkg/tool/") {
return nil
}
if !fi.Mode().IsRegular() || fi.Mode().Perm()&0100 == 0 {
return nil
}
b, err := os.ReadFile(path)
if err != nil {
return err
}
binaries[name] = b
return nil
})
if err != nil {
t.Fatal(err)
}
return binaries, nil
}