golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/task/darwin_test.go (about)

     1  // 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  
     5  package task_test
     6  
     7  import (
     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  
    21  var readPKGFlag = flag.String("read-pkg", "", "Path to a Go macOS .pkg installer to run TestReadBinariesFromPKG with.")
    22  
    23  func 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.
    66  func 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  }