github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/debug/buildinfo/buildinfo_test.go (about) 1 // Copyright 2021 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 buildinfo_test 6 7 import ( 8 "bytes" 9 "debug/buildinfo" 10 "flag" 11 "internal/testenv" 12 "os" 13 "os/exec" 14 "path" 15 "path/filepath" 16 "regexp" 17 "runtime" 18 "strings" 19 "testing" 20 ) 21 22 var flagAll = flag.Bool("all", false, "test all supported GOOS/GOARCH platforms, instead of only the current platform") 23 24 // TestReadFile confirms that ReadFile can read build information from binaries 25 // on supported target platforms. It builds a trivial binary on the current 26 // platforms (or all platforms if -all is set) in various configurations and 27 // checks that build information can or cannot be read. 28 func TestReadFile(t *testing.T) { 29 if testing.Short() { 30 t.Skip("test requires compiling and linking, which may be slow") 31 } 32 testenv.MustHaveGoBuild(t) 33 34 type platform struct{ goos, goarch string } 35 platforms := []platform{ 36 {"aix", "ppc64"}, 37 {"darwin", "amd64"}, 38 {"darwin", "arm64"}, 39 {"linux", "386"}, 40 {"linux", "amd64"}, 41 {"windows", "386"}, 42 {"windows", "amd64"}, 43 } 44 runtimePlatform := platform{runtime.GOOS, runtime.GOARCH} 45 haveRuntimePlatform := false 46 for _, p := range platforms { 47 if p == runtimePlatform { 48 haveRuntimePlatform = true 49 break 50 } 51 } 52 if !haveRuntimePlatform { 53 platforms = append(platforms, runtimePlatform) 54 } 55 56 buildWithModules := func(t *testing.T, goos, goarch string) string { 57 dir := t.TempDir() 58 gomodPath := filepath.Join(dir, "go.mod") 59 gomodData := []byte("module example.com/m\ngo 1.18\n") 60 if err := os.WriteFile(gomodPath, gomodData, 0666); err != nil { 61 t.Fatal(err) 62 } 63 helloPath := filepath.Join(dir, "hello.go") 64 helloData := []byte("package main\nfunc main() {}\n") 65 if err := os.WriteFile(helloPath, helloData, 0666); err != nil { 66 t.Fatal(err) 67 } 68 outPath := filepath.Join(dir, path.Base(t.Name())) 69 cmd := exec.Command("go", "build", "-o="+outPath) 70 cmd.Dir = dir 71 cmd.Env = append(os.Environ(), "GO111MODULE=on", "GOOS="+goos, "GOARCH="+goarch) 72 stderr := &bytes.Buffer{} 73 cmd.Stderr = stderr 74 if err := cmd.Run(); err != nil { 75 t.Fatalf("failed building test file: %v\n%s", err, stderr.Bytes()) 76 } 77 return outPath 78 } 79 80 buildWithGOPATH := func(t *testing.T, goos, goarch string) string { 81 gopathDir := t.TempDir() 82 pkgDir := filepath.Join(gopathDir, "src/example.com/m") 83 if err := os.MkdirAll(pkgDir, 0777); err != nil { 84 t.Fatal(err) 85 } 86 helloPath := filepath.Join(pkgDir, "hello.go") 87 helloData := []byte("package main\nfunc main() {}\n") 88 if err := os.WriteFile(helloPath, helloData, 0666); err != nil { 89 t.Fatal(err) 90 } 91 outPath := filepath.Join(gopathDir, path.Base(t.Name())) 92 cmd := exec.Command("go", "build", "-o="+outPath) 93 cmd.Dir = pkgDir 94 cmd.Env = append(os.Environ(), "GO111MODULE=off", "GOPATH="+gopathDir, "GOOS="+goos, "GOARCH="+goarch) 95 stderr := &bytes.Buffer{} 96 cmd.Stderr = stderr 97 if err := cmd.Run(); err != nil { 98 t.Fatalf("failed building test file: %v\n%s", err, stderr.Bytes()) 99 } 100 return outPath 101 } 102 103 damageBuildInfo := func(t *testing.T, name string) { 104 data, err := os.ReadFile(name) 105 if err != nil { 106 t.Fatal(err) 107 } 108 i := bytes.Index(data, []byte("\xff Go buildinf:")) 109 if i < 0 { 110 t.Fatal("Go buildinf not found") 111 } 112 data[i+2] = 'N' 113 if err := os.WriteFile(name, data, 0666); err != nil { 114 t.Fatal(err) 115 } 116 } 117 118 goVersionRe := regexp.MustCompile("(?m)^go\t.*\n") 119 buildRe := regexp.MustCompile("(?m)^build\t.*\n") 120 cleanOutputForComparison := func(got string) string { 121 // Remove or replace anything that might depend on the test's environment 122 // so we can check the output afterward with a string comparison. 123 // We'll remove all build lines except the compiler, just to make sure 124 // build lines are included. 125 got = goVersionRe.ReplaceAllString(got, "go\tGOVERSION\n") 126 got = buildRe.ReplaceAllStringFunc(got, func(match string) string { 127 if strings.HasPrefix(match, "build\t-compiler=") { 128 return match 129 } 130 return "" 131 }) 132 return got 133 } 134 135 cases := []struct { 136 name string 137 build func(t *testing.T, goos, goarch string) string 138 want string 139 wantErr string 140 }{ 141 { 142 name: "doesnotexist", 143 build: func(t *testing.T, goos, goarch string) string { 144 return "doesnotexist.txt" 145 }, 146 wantErr: "doesnotexist", 147 }, 148 { 149 name: "empty", 150 build: func(t *testing.T, _, _ string) string { 151 dir := t.TempDir() 152 name := filepath.Join(dir, "empty") 153 if err := os.WriteFile(name, nil, 0666); err != nil { 154 t.Fatal(err) 155 } 156 return name 157 }, 158 wantErr: "unrecognized file format", 159 }, 160 { 161 name: "valid_modules", 162 build: buildWithModules, 163 want: "go\tGOVERSION\n" + 164 "path\texample.com/m\n" + 165 "mod\texample.com/m\t(devel)\t\n" + 166 "build\t-compiler=gc\n", 167 }, 168 { 169 name: "invalid_modules", 170 build: func(t *testing.T, goos, goarch string) string { 171 name := buildWithModules(t, goos, goarch) 172 damageBuildInfo(t, name) 173 return name 174 }, 175 wantErr: "not a Go executable", 176 }, 177 { 178 name: "valid_gopath", 179 build: buildWithGOPATH, 180 want: "go\tGOVERSION\n" + 181 "path\texample.com/m\n" + 182 "build\t-compiler=gc\n", 183 }, 184 { 185 name: "invalid_gopath", 186 build: func(t *testing.T, goos, goarch string) string { 187 name := buildWithGOPATH(t, goos, goarch) 188 damageBuildInfo(t, name) 189 return name 190 }, 191 wantErr: "not a Go executable", 192 }, 193 } 194 195 for _, p := range platforms { 196 p := p 197 t.Run(p.goos+"_"+p.goarch, func(t *testing.T) { 198 if p != runtimePlatform && !*flagAll { 199 t.Skipf("skipping platforms other than %s_%s because -all was not set", runtimePlatform.goos, runtimePlatform.goarch) 200 } 201 for _, tc := range cases { 202 tc := tc 203 t.Run(tc.name, func(t *testing.T) { 204 t.Parallel() 205 name := tc.build(t, p.goos, p.goarch) 206 if info, err := buildinfo.ReadFile(name); err != nil { 207 if tc.wantErr == "" { 208 t.Fatalf("unexpected error: %v", err) 209 } else if errMsg := err.Error(); !strings.Contains(errMsg, tc.wantErr) { 210 t.Fatalf("got error %q; want error containing %q", errMsg, tc.wantErr) 211 } 212 } else { 213 if tc.wantErr != "" { 214 t.Fatalf("unexpected success; want error containing %q", tc.wantErr) 215 } else if got, err := info.MarshalText(); err != nil { 216 t.Fatalf("unexpected error marshaling BuildInfo: %v", err) 217 } else if got := cleanOutputForComparison(string(got)); got != tc.want { 218 if got != tc.want { 219 t.Fatalf("got:\n%s\nwant:\n%s", got, tc.want) 220 } 221 } 222 } 223 }) 224 } 225 }) 226 } 227 }