| 1 | // Copyright 2018 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 packages_test |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "context" |
| 10 | "encoding/json" |
| 11 | "flag" |
| 12 | "fmt" |
| 13 | "go/ast" |
| 14 | constantpkg "go/constant" |
| 15 | "go/parser" |
| 16 | "go/token" |
| 17 | "go/types" |
| 18 | "io/ioutil" |
| 19 | "os" |
| 20 | "os/exec" |
| 21 | "path/filepath" |
| 22 | "reflect" |
| 23 | "runtime" |
| 24 | "sort" |
| 25 | "strings" |
| 26 | "testing" |
| 27 | "time" |
| 28 | |
| 29 | "golang.org/x/tools/go/packages" |
| 30 | "golang.org/x/tools/go/packages/packagestest" |
| 31 | "golang.org/x/tools/internal/packagesinternal" |
| 32 | "golang.org/x/tools/internal/testenv" |
| 33 | ) |
| 34 | |
| 35 | // testCtx is canceled when the test binary is about to time out. |
| 36 | // |
| 37 | // If https://golang.org/issue/28135 is accepted, uses of this variable in test |
| 38 | // functions should be replaced by t.Context(). |
| 39 | var testCtx = context.Background() |
| 40 | |
| 41 | func TestMain(m *testing.M) { |
| 42 | testenv.ExitIfSmallMachine() |
| 43 | |
| 44 | timeoutFlag := flag.Lookup("test.timeout") |
| 45 | if timeoutFlag != nil { |
| 46 | if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 { |
| 47 | aBitShorter := d * 95 / 100 |
| 48 | var cancel context.CancelFunc |
| 49 | testCtx, cancel = context.WithTimeout(testCtx, aBitShorter) |
| 50 | defer cancel() |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | os.Exit(m.Run()) |
| 55 | } |
| 56 | |
| 57 | func skipIfShort(t *testing.T, reason string) { |
| 58 | if testing.Short() { |
| 59 | t.Skipf("skipping slow test in short mode: %s", reason) |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | // testAllOrModulesParallel tests f, in parallel, against all packagestest |
| 64 | // exporters in long mode, but only against the Modules exporter in short mode. |
| 65 | func testAllOrModulesParallel(t *testing.T, f func(*testing.T, packagestest.Exporter)) { |
| 66 | t.Parallel() |
| 67 | packagestest.TestAll(t, func(t *testing.T, exporter packagestest.Exporter) { |
| 68 | t.Helper() |
| 69 | |
| 70 | switch exporter.Name() { |
| 71 | case "Modules": |
| 72 | case "GOPATH": |
| 73 | if testing.Short() { |
| 74 | t.Skipf("skipping GOPATH test in short mode") |
| 75 | } |
| 76 | default: |
| 77 | t.Fatalf("unexpected exporter %q", exporter.Name()) |
| 78 | } |
| 79 | |
| 80 | t.Parallel() |
| 81 | f(t, exporter) |
| 82 | }) |
| 83 | } |
| 84 | |
| 85 | // TODO(adonovan): more test cases to write: |
| 86 | // |
| 87 | // - When the tests fail, make them print a 'cd & load' command |
| 88 | // that will allow the maintainer to interact with the failing scenario. |
| 89 | // - errors in go-list metadata |
| 90 | // - a foo.test package that cannot be built for some reason (e.g. |
| 91 | // import error) will result in a JSON blob with no name and a |
| 92 | // nonexistent testmain file in GoFiles. Test that we handle this |
| 93 | // gracefully. |
| 94 | // - test more Flags. |
| 95 | // |
| 96 | // LoadSyntax & LoadAllSyntax modes: |
| 97 | // - Fset may be user-supplied or not. |
| 98 | // - Packages.Info is correctly set. |
| 99 | // - typechecker configuration is honored |
| 100 | // - import cycles are gracefully handled in type checker. |
| 101 | // - test typechecking of generated test main and cgo. |
| 102 | |
| 103 | // The zero-value of Config has LoadFiles mode. |
| 104 | func TestLoadZeroConfig(t *testing.T) { |
| 105 | testenv.NeedsGoPackages(t) |
| 106 | t.Parallel() |
| 107 | |
| 108 | initial, err := packages.Load(nil, "hash") |
| 109 | if err != nil { |
| 110 | t.Fatal(err) |
| 111 | } |
| 112 | if len(initial) != 1 { |
| 113 | t.Fatalf("got %s, want [hash]", initial) |
| 114 | } |
| 115 | hash := initial[0] |
| 116 | // Even though the hash package has imports, |
| 117 | // they are not reported. |
| 118 | got := fmt.Sprintf("srcs=%v imports=%v", srcs(hash), hash.Imports) |
| 119 | want := "srcs=[hash.go] imports=map[]" |
| 120 | if got != want { |
| 121 | t.Fatalf("got %s, want %s", got, want) |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | func TestLoadImportsGraph(t *testing.T) { testAllOrModulesParallel(t, testLoadImportsGraph) } |
| 126 | func testLoadImportsGraph(t *testing.T, exporter packagestest.Exporter) { |
| 127 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 128 | Name: "golang.org/fake", |
| 129 | Files: map[string]interface{}{ |
| 130 | "a/a.go": `package a; const A = 1`, |
| 131 | "b/b.go": `package b; import ("golang.org/fake/a"; _ "container/list"); var B = a.A`, |
| 132 | "c/c.go": `package c; import (_ "golang.org/fake/b"; _ "unsafe")`, |
| 133 | "c/c2.go": "// +build ignore\n\n" + `package c; import _ "fmt"`, |
| 134 | "subdir/d/d.go": `package d`, |
| 135 | "subdir/d/d_test.go": `package d; import _ "math/bits"`, |
| 136 | "subdir/d/x_test.go": `package d_test; import _ "golang.org/fake/subdir/d"`, // TODO(adonovan): test bad import here |
| 137 | "subdir/e/d.go": `package e`, |
| 138 | "e/e.go": `package main; import _ "golang.org/fake/b"`, |
| 139 | "e/e2.go": `package main; import _ "golang.org/fake/c"`, |
| 140 | "f/f.go": `package f`, |
| 141 | }}}) |
| 142 | defer exported.Cleanup() |
| 143 | exported.Config.Mode = packages.LoadImports |
| 144 | initial, err := packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/subdir/d", "golang.org/fake/e") |
| 145 | if err != nil { |
| 146 | t.Fatal(err) |
| 147 | } |
| 148 | |
| 149 | // Check graph topology. |
| 150 | graph, _ := importGraph(initial) |
| 151 | wantGraph := ` |
| 152 | container/list |
| 153 | golang.org/fake/a |
| 154 | golang.org/fake/b |
| 155 | * golang.org/fake/c |
| 156 | * golang.org/fake/e |
| 157 | * golang.org/fake/subdir/d |
| 158 | * golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 159 | * golang.org/fake/subdir/d.test |
| 160 | * golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 161 | math/bits |
| 162 | unsafe |
| 163 | golang.org/fake/b -> container/list |
| 164 | golang.org/fake/b -> golang.org/fake/a |
| 165 | golang.org/fake/c -> golang.org/fake/b |
| 166 | golang.org/fake/c -> unsafe |
| 167 | golang.org/fake/e -> golang.org/fake/b |
| 168 | golang.org/fake/e -> golang.org/fake/c |
| 169 | golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] -> math/bits |
| 170 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 171 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 172 | golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 173 | `[1:] |
| 174 | |
| 175 | if graph != wantGraph { |
| 176 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 177 | } |
| 178 | |
| 179 | exported.Config.Tests = true |
| 180 | initial, err = packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/subdir/d", "golang.org/fake/e") |
| 181 | if err != nil { |
| 182 | t.Fatal(err) |
| 183 | } |
| 184 | |
| 185 | // Check graph topology. |
| 186 | graph, all := importGraph(initial) |
| 187 | wantGraph = ` |
| 188 | container/list |
| 189 | golang.org/fake/a |
| 190 | golang.org/fake/b |
| 191 | * golang.org/fake/c |
| 192 | * golang.org/fake/e |
| 193 | * golang.org/fake/subdir/d |
| 194 | * golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 195 | * golang.org/fake/subdir/d.test |
| 196 | * golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 197 | math/bits |
| 198 | unsafe |
| 199 | golang.org/fake/b -> container/list |
| 200 | golang.org/fake/b -> golang.org/fake/a |
| 201 | golang.org/fake/c -> golang.org/fake/b |
| 202 | golang.org/fake/c -> unsafe |
| 203 | golang.org/fake/e -> golang.org/fake/b |
| 204 | golang.org/fake/e -> golang.org/fake/c |
| 205 | golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] -> math/bits |
| 206 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 207 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 208 | golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 209 | `[1:] |
| 210 | |
| 211 | if graph != wantGraph { |
| 212 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 213 | } |
| 214 | |
| 215 | // Check node information: kind, name, srcs. |
| 216 | for _, test := range []struct { |
| 217 | id string |
| 218 | wantName string |
| 219 | wantKind string |
| 220 | wantSrcs string |
| 221 | wantIgnored string |
| 222 | }{ |
| 223 | {"golang.org/fake/a", "a", "package", "a.go", ""}, |
| 224 | {"golang.org/fake/b", "b", "package", "b.go", ""}, |
| 225 | {"golang.org/fake/c", "c", "package", "c.go", "c2.go"}, // c2.go is ignored |
| 226 | {"golang.org/fake/e", "main", "command", "e.go e2.go", ""}, |
| 227 | {"container/list", "list", "package", "list.go", ""}, |
| 228 | {"golang.org/fake/subdir/d", "d", "package", "d.go", ""}, |
| 229 | {"golang.org/fake/subdir/d.test", "main", "command", "0.go", ""}, |
| 230 | {"unsafe", "unsafe", "package", "", ""}, |
| 231 | } { |
| 232 | p, ok := all[test.id] |
| 233 | if !ok { |
| 234 | t.Errorf("no package %s", test.id) |
| 235 | continue |
| 236 | } |
| 237 | if p.Name != test.wantName { |
| 238 | t.Errorf("%s.Name = %q, want %q", test.id, p.Name, test.wantName) |
| 239 | } |
| 240 | |
| 241 | // kind |
| 242 | var kind string |
| 243 | if p.Name == "main" { |
| 244 | kind += "command" |
| 245 | } else { |
| 246 | kind += "package" |
| 247 | } |
| 248 | if kind != test.wantKind { |
| 249 | t.Errorf("%s.Kind = %q, want %q", test.id, kind, test.wantKind) |
| 250 | } |
| 251 | |
| 252 | if srcs := strings.Join(srcs(p), " "); srcs != test.wantSrcs { |
| 253 | t.Errorf("%s.Srcs = [%s], want [%s]", test.id, srcs, test.wantSrcs) |
| 254 | } |
| 255 | if ignored := strings.Join(cleanPaths(p.IgnoredFiles), " "); ignored != test.wantIgnored { |
| 256 | t.Errorf("%s.Srcs = [%s], want [%s]", test.id, ignored, test.wantIgnored) |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | // Test an ad-hoc package, analogous to "go run hello.go". |
| 261 | if initial, err := packages.Load(exported.Config, exported.File("golang.org/fake", "c/c.go")); len(initial) == 0 { |
| 262 | t.Errorf("failed to obtain metadata for ad-hoc package: %s", err) |
| 263 | } else { |
| 264 | got := fmt.Sprintf("%s %s", initial[0].ID, srcs(initial[0])) |
| 265 | if want := "command-line-arguments [c.go]"; got != want { |
| 266 | t.Errorf("oops: got %s, want %s", got, want) |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | // Wildcards |
| 271 | // See StdlibTest for effective test of "std" wildcard. |
| 272 | // TODO(adonovan): test "all" returns everything in the current module. |
| 273 | { |
| 274 | // "..." (subdirectory) |
| 275 | initial, err = packages.Load(exported.Config, "golang.org/fake/subdir/...") |
| 276 | if err != nil { |
| 277 | t.Fatal(err) |
| 278 | } |
| 279 | graph, _ = importGraph(initial) |
| 280 | wantGraph = ` |
| 281 | * golang.org/fake/subdir/d |
| 282 | * golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 283 | * golang.org/fake/subdir/d.test |
| 284 | * golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 285 | * golang.org/fake/subdir/e |
| 286 | math/bits |
| 287 | golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] -> math/bits |
| 288 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 289 | golang.org/fake/subdir/d.test -> golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] |
| 290 | golang.org/fake/subdir/d_test [golang.org/fake/subdir/d.test] -> golang.org/fake/subdir/d [golang.org/fake/subdir/d.test] |
| 291 | `[1:] |
| 292 | |
| 293 | if graph != wantGraph { |
| 294 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | func TestLoadImportsTestVariants(t *testing.T) { |
| 300 | testAllOrModulesParallel(t, testLoadImportsTestVariants) |
| 301 | } |
| 302 | func testLoadImportsTestVariants(t *testing.T, exporter packagestest.Exporter) { |
| 303 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 304 | Name: "golang.org/fake", |
| 305 | Files: map[string]interface{}{ |
| 306 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 307 | "b/b.go": `package b`, |
| 308 | "b/b_test.go": `package b`, |
| 309 | "b/bx_test.go": `package b_test; import _ "golang.org/fake/a"`, |
| 310 | }}}) |
| 311 | defer exported.Cleanup() |
| 312 | exported.Config.Mode = packages.LoadImports |
| 313 | exported.Config.Tests = true |
| 314 | |
| 315 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/b") |
| 316 | if err != nil { |
| 317 | t.Fatal(err) |
| 318 | } |
| 319 | |
| 320 | // Check graph topology. |
| 321 | graph, _ := importGraph(initial) |
| 322 | wantGraph := ` |
| 323 | * golang.org/fake/a |
| 324 | golang.org/fake/a [golang.org/fake/b.test] |
| 325 | * golang.org/fake/b |
| 326 | * golang.org/fake/b [golang.org/fake/b.test] |
| 327 | * golang.org/fake/b.test |
| 328 | * golang.org/fake/b_test [golang.org/fake/b.test] |
| 329 | golang.org/fake/a -> golang.org/fake/b |
| 330 | golang.org/fake/a [golang.org/fake/b.test] -> golang.org/fake/b [golang.org/fake/b.test] |
| 331 | golang.org/fake/b.test -> golang.org/fake/b [golang.org/fake/b.test] |
| 332 | golang.org/fake/b.test -> golang.org/fake/b_test [golang.org/fake/b.test] |
| 333 | golang.org/fake/b_test [golang.org/fake/b.test] -> golang.org/fake/a [golang.org/fake/b.test] |
| 334 | `[1:] |
| 335 | |
| 336 | if graph != wantGraph { |
| 337 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | func TestLoadAbsolutePath(t *testing.T) { |
| 342 | t.Parallel() |
| 343 | |
| 344 | exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{ |
| 345 | Name: "golang.org/gopatha", |
| 346 | Files: map[string]interface{}{ |
| 347 | "a/a.go": `package a`, |
| 348 | }}, { |
| 349 | Name: "golang.org/gopathb", |
| 350 | Files: map[string]interface{}{ |
| 351 | "b/b.go": `package b`, |
| 352 | }}}) |
| 353 | defer exported.Cleanup() |
| 354 | |
| 355 | initial, err := packages.Load(exported.Config, filepath.Dir(exported.File("golang.org/gopatha", "a/a.go")), filepath.Dir(exported.File("golang.org/gopathb", "b/b.go"))) |
| 356 | if err != nil { |
| 357 | t.Fatalf("failed to load imports: %v", err) |
| 358 | } |
| 359 | |
| 360 | got := []string{} |
| 361 | for _, p := range initial { |
| 362 | got = append(got, p.ID) |
| 363 | } |
| 364 | sort.Strings(got) |
| 365 | want := []string{"golang.org/gopatha/a", "golang.org/gopathb/b"} |
| 366 | if !reflect.DeepEqual(got, want) { |
| 367 | t.Fatalf("initial packages loaded: got [%s], want [%s]", got, want) |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | func TestVendorImports(t *testing.T) { |
| 372 | t.Parallel() |
| 373 | |
| 374 | exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{ |
| 375 | Name: "golang.org/fake", |
| 376 | Files: map[string]interface{}{ |
| 377 | "a/a.go": `package a; import _ "b"; import _ "golang.org/fake/c";`, |
| 378 | "a/vendor/b/b.go": `package b; import _ "golang.org/fake/c"`, |
| 379 | "c/c.go": `package c; import _ "b"`, |
| 380 | "c/vendor/b/b.go": `package b`, |
| 381 | }}}) |
| 382 | defer exported.Cleanup() |
| 383 | exported.Config.Mode = packages.LoadImports |
| 384 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c") |
| 385 | if err != nil { |
| 386 | t.Fatal(err) |
| 387 | } |
| 388 | |
| 389 | graph, all := importGraph(initial) |
| 390 | wantGraph := ` |
| 391 | * golang.org/fake/a |
| 392 | golang.org/fake/a/vendor/b |
| 393 | * golang.org/fake/c |
| 394 | golang.org/fake/c/vendor/b |
| 395 | golang.org/fake/a -> golang.org/fake/a/vendor/b |
| 396 | golang.org/fake/a -> golang.org/fake/c |
| 397 | golang.org/fake/a/vendor/b -> golang.org/fake/c |
| 398 | golang.org/fake/c -> golang.org/fake/c/vendor/b |
| 399 | `[1:] |
| 400 | if graph != wantGraph { |
| 401 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 402 | } |
| 403 | |
| 404 | for _, test := range []struct { |
| 405 | pattern string |
| 406 | wantImports string |
| 407 | }{ |
| 408 | {"golang.org/fake/a", "b:golang.org/fake/a/vendor/b golang.org/fake/c:golang.org/fake/c"}, |
| 409 | {"golang.org/fake/c", "b:golang.org/fake/c/vendor/b"}, |
| 410 | {"golang.org/fake/a/vendor/b", "golang.org/fake/c:golang.org/fake/c"}, |
| 411 | {"golang.org/fake/c/vendor/b", ""}, |
| 412 | } { |
| 413 | // Test the import paths. |
| 414 | pkg := all[test.pattern] |
| 415 | if imports := strings.Join(imports(pkg), " "); imports != test.wantImports { |
| 416 | t.Errorf("package %q: got %s, want %s", test.pattern, imports, test.wantImports) |
| 417 | } |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | func imports(p *packages.Package) []string { |
| 422 | if p == nil { |
| 423 | return nil |
| 424 | } |
| 425 | keys := make([]string, 0, len(p.Imports)) |
| 426 | for k, v := range p.Imports { |
| 427 | keys = append(keys, fmt.Sprintf("%s:%s", k, v.ID)) |
| 428 | } |
| 429 | sort.Strings(keys) |
| 430 | return keys |
| 431 | } |
| 432 | |
| 433 | func TestConfigDir(t *testing.T) { testAllOrModulesParallel(t, testConfigDir) } |
| 434 | func testConfigDir(t *testing.T, exporter packagestest.Exporter) { |
| 435 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 436 | Name: "golang.org/fake", |
| 437 | Files: map[string]interface{}{ |
| 438 | "a/a.go": `package a; const Name = "a" `, |
| 439 | "a/b/b.go": `package b; const Name = "a/b"`, |
| 440 | "b/b.go": `package b; const Name = "b"`, |
| 441 | }}}) |
| 442 | defer exported.Cleanup() |
| 443 | aDir := filepath.Dir(exported.File("golang.org/fake", "a/a.go")) |
| 444 | bDir := filepath.Dir(exported.File("golang.org/fake", "b/b.go")) |
| 445 | baseDir := filepath.Dir(aDir) |
| 446 | |
| 447 | for _, test := range []struct { |
| 448 | dir string |
| 449 | pattern string |
| 450 | want string // value of Name constant |
| 451 | fails bool |
| 452 | }{ |
| 453 | {dir: bDir, pattern: "golang.org/fake/a", want: `"a"`}, |
| 454 | {dir: bDir, pattern: "golang.org/fake/b", want: `"b"`}, |
| 455 | {dir: bDir, pattern: "./a", fails: true}, |
| 456 | {dir: bDir, pattern: "./b", fails: true}, |
| 457 | {dir: baseDir, pattern: "golang.org/fake/a", want: `"a"`}, |
| 458 | {dir: baseDir, pattern: "golang.org/fake/b", want: `"b"`}, |
| 459 | {dir: baseDir, pattern: "./a", want: `"a"`}, |
| 460 | {dir: baseDir, pattern: "./b", want: `"b"`}, |
| 461 | {dir: aDir, pattern: "golang.org/fake/a", want: `"a"`}, |
| 462 | {dir: aDir, pattern: "golang.org/fake/b", want: `"b"`}, |
| 463 | {dir: aDir, pattern: "./a", fails: true}, |
| 464 | {dir: aDir, pattern: "./b", want: `"a/b"`}, |
| 465 | } { |
| 466 | exported.Config.Mode = packages.LoadSyntax // Use LoadSyntax to ensure that files can be opened. |
| 467 | exported.Config.Dir = test.dir |
| 468 | initial, err := packages.Load(exported.Config, test.pattern) |
| 469 | var got string |
| 470 | fails := false |
| 471 | if err != nil { |
| 472 | fails = true |
| 473 | } else if len(initial) > 0 { |
| 474 | if len(initial[0].Errors) > 0 { |
| 475 | fails = true |
| 476 | } else if c := constant(initial[0], "Name"); c != nil { |
| 477 | got = c.Val().String() |
| 478 | } |
| 479 | } |
| 480 | if got != test.want { |
| 481 | t.Errorf("dir %q, pattern %q: got %s, want %s", |
| 482 | test.dir, test.pattern, got, test.want) |
| 483 | } |
| 484 | if fails != test.fails { |
| 485 | // TODO: remove when go#28023 is fixed |
| 486 | if test.fails && strings.HasPrefix(test.pattern, "./") && exporter == packagestest.Modules { |
| 487 | // Currently go list in module mode does not handle missing directories correctly. |
| 488 | continue |
| 489 | } |
| 490 | t.Errorf("dir %q, pattern %q: error %v, want %v", |
| 491 | test.dir, test.pattern, fails, test.fails) |
| 492 | } |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | func TestConfigFlags(t *testing.T) { testAllOrModulesParallel(t, testConfigFlags) } |
| 497 | func testConfigFlags(t *testing.T, exporter packagestest.Exporter) { |
| 498 | // Test satisfying +build line tags, with -tags flag. |
| 499 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 500 | Name: "golang.org/fake", |
| 501 | Files: map[string]interface{}{ |
| 502 | // package a |
| 503 | "a/a.go": `package a; import _ "golang.org/fake/a/b"`, |
| 504 | "a/b.go": `// +build tag |
| 505 | |
| 506 | package a`, |
| 507 | "a/c.go": `// +build tag tag2 |
| 508 | |
| 509 | package a`, |
| 510 | "a/d.go": `// +build tag,tag2 |
| 511 | |
| 512 | package a`, |
| 513 | // package a/b |
| 514 | "a/b/a.go": `package b`, |
| 515 | "a/b/b.go": `// +build tag |
| 516 | |
| 517 | package b`, |
| 518 | }}}) |
| 519 | defer exported.Cleanup() |
| 520 | |
| 521 | for _, test := range []struct { |
| 522 | pattern string |
| 523 | tags []string |
| 524 | wantSrcs string |
| 525 | wantImportSrcs string |
| 526 | }{ |
| 527 | {`golang.org/fake/a`, []string{}, "a.go", "a.go"}, |
| 528 | {`golang.org/fake/a`, []string{`-tags=tag`}, "a.go b.go c.go", "a.go b.go"}, |
| 529 | {`golang.org/fake/a`, []string{`-tags=tag2`}, "a.go c.go", "a.go"}, |
| 530 | {`golang.org/fake/a`, []string{`-tags=tag tag2`}, "a.go b.go c.go d.go", "a.go b.go"}, |
| 531 | } { |
| 532 | exported.Config.Mode = packages.LoadImports |
| 533 | exported.Config.BuildFlags = test.tags |
| 534 | |
| 535 | initial, err := packages.Load(exported.Config, test.pattern) |
| 536 | if err != nil { |
| 537 | t.Error(err) |
| 538 | continue |
| 539 | } |
| 540 | if len(initial) != 1 { |
| 541 | t.Errorf("test tags %v: pattern %s, expected 1 package, got %d packages.", test.tags, test.pattern, len(initial)) |
| 542 | continue |
| 543 | } |
| 544 | pkg := initial[0] |
| 545 | if srcs := strings.Join(srcs(pkg), " "); srcs != test.wantSrcs { |
| 546 | t.Errorf("test tags %v: srcs of package %s = [%s], want [%s]", test.tags, test.pattern, srcs, test.wantSrcs) |
| 547 | } |
| 548 | for path, ipkg := range pkg.Imports { |
| 549 | if srcs := strings.Join(srcs(ipkg), " "); srcs != test.wantImportSrcs { |
| 550 | t.Errorf("build tags %v: srcs of imported package %s = [%s], want [%s]", test.tags, path, srcs, test.wantImportSrcs) |
| 551 | } |
| 552 | } |
| 553 | |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | func TestLoadTypes(t *testing.T) { testAllOrModulesParallel(t, testLoadTypes) } |
| 558 | func testLoadTypes(t *testing.T, exporter packagestest.Exporter) { |
| 559 | // In LoadTypes and LoadSyntax modes, the compiler will |
| 560 | // fail to generate an export data file for c, because it has |
| 561 | // a type error. The loader should fall back loading a and c |
| 562 | // from source, but use the export data for b. |
| 563 | |
| 564 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 565 | Name: "golang.org/fake", |
| 566 | Files: map[string]interface{}{ |
| 567 | "a/a.go": `package a; import "golang.org/fake/b"; import "golang.org/fake/c"; const A = "a" + b.B + c.C`, |
| 568 | "b/b.go": `package b; const B = "b"`, |
| 569 | "c/c.go": `package c; const C = "c" + 1`, |
| 570 | }}}) |
| 571 | defer exported.Cleanup() |
| 572 | |
| 573 | exported.Config.Mode = packages.LoadTypes |
| 574 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 575 | if err != nil { |
| 576 | t.Fatal(err) |
| 577 | } |
| 578 | |
| 579 | graph, all := importGraph(initial) |
| 580 | wantGraph := ` |
| 581 | * golang.org/fake/a |
| 582 | golang.org/fake/b |
| 583 | golang.org/fake/c |
| 584 | golang.org/fake/a -> golang.org/fake/b |
| 585 | golang.org/fake/a -> golang.org/fake/c |
| 586 | `[1:] |
| 587 | if graph != wantGraph { |
| 588 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 589 | } |
| 590 | |
| 591 | for _, id := range []string{ |
| 592 | "golang.org/fake/a", |
| 593 | "golang.org/fake/b", |
| 594 | "golang.org/fake/c", |
| 595 | } { |
| 596 | p := all[id] |
| 597 | if p == nil { |
| 598 | t.Errorf("missing package: %s", id) |
| 599 | continue |
| 600 | } |
| 601 | if p.Types == nil { |
| 602 | t.Errorf("missing types.Package for %s", p) |
| 603 | continue |
| 604 | } else if !p.Types.Complete() { |
| 605 | t.Errorf("incomplete types.Package for %s", p) |
| 606 | } else if p.TypesSizes == nil { |
| 607 | t.Errorf("TypesSizes is not filled in for %s", p) |
| 608 | } |
| 609 | |
| 610 | } |
| 611 | } |
| 612 | |
| 613 | // TestLoadTypesBits is equivalent to TestLoadTypes except that it only requests |
| 614 | // the types using the NeedTypes bit. |
| 615 | func TestLoadTypesBits(t *testing.T) { testAllOrModulesParallel(t, testLoadTypesBits) } |
| 616 | func testLoadTypesBits(t *testing.T, exporter packagestest.Exporter) { |
| 617 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 618 | Name: "golang.org/fake", |
| 619 | Files: map[string]interface{}{ |
| 620 | "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, |
| 621 | "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, |
| 622 | "c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`, |
| 623 | "d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`, |
| 624 | "e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F`, |
| 625 | "f/f.go": `package f; const F = "f"`, |
| 626 | }}}) |
| 627 | defer exported.Cleanup() |
| 628 | |
| 629 | exported.Config.Mode = packages.NeedTypes | packages.NeedImports |
| 630 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c") |
| 631 | if err != nil { |
| 632 | t.Fatal(err) |
| 633 | } |
| 634 | |
| 635 | graph, all := importGraph(initial) |
| 636 | wantGraph := ` |
| 637 | * golang.org/fake/a |
| 638 | golang.org/fake/b |
| 639 | * golang.org/fake/c |
| 640 | golang.org/fake/d |
| 641 | golang.org/fake/e |
| 642 | golang.org/fake/f |
| 643 | golang.org/fake/a -> golang.org/fake/b |
| 644 | golang.org/fake/b -> golang.org/fake/c |
| 645 | golang.org/fake/c -> golang.org/fake/d |
| 646 | golang.org/fake/d -> golang.org/fake/e |
| 647 | golang.org/fake/e -> golang.org/fake/f |
| 648 | `[1:] |
| 649 | if graph != wantGraph { |
| 650 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 651 | } |
| 652 | |
| 653 | for _, test := range []struct { |
| 654 | id string |
| 655 | }{ |
| 656 | {"golang.org/fake/a"}, |
| 657 | {"golang.org/fake/b"}, |
| 658 | {"golang.org/fake/c"}, |
| 659 | {"golang.org/fake/d"}, |
| 660 | {"golang.org/fake/e"}, |
| 661 | {"golang.org/fake/f"}, |
| 662 | } { |
| 663 | p := all[test.id] |
| 664 | if p == nil { |
| 665 | t.Errorf("missing package: %s", test.id) |
| 666 | continue |
| 667 | } |
| 668 | if p.Types == nil { |
| 669 | t.Errorf("missing types.Package for %s", p) |
| 670 | continue |
| 671 | } |
| 672 | // We don't request the syntax, so we shouldn't get it. |
| 673 | if p.Syntax != nil { |
| 674 | t.Errorf("Syntax unexpectedly provided for %s", p) |
| 675 | } |
| 676 | if p.Errors != nil { |
| 677 | t.Errorf("errors in package: %s: %s", p, p.Errors) |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | // Check value of constant. |
| 682 | aA := constant(all["golang.org/fake/a"], "A") |
| 683 | if aA == nil { |
| 684 | t.Fatalf("a.A: got nil") |
| 685 | } |
| 686 | if got, want := fmt.Sprintf("%v %v", aA, aA.Val()), `const golang.org/fake/a.A untyped string "abcdef"`; got != want { |
| 687 | t.Errorf("a.A: got %s, want %s", got, want) |
| 688 | } |
| 689 | } |
| 690 | |
| 691 | func TestLoadSyntaxOK(t *testing.T) { testAllOrModulesParallel(t, testLoadSyntaxOK) } |
| 692 | func testLoadSyntaxOK(t *testing.T, exporter packagestest.Exporter) { |
| 693 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 694 | Name: "golang.org/fake", |
| 695 | Files: map[string]interface{}{ |
| 696 | "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, |
| 697 | "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, |
| 698 | "c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`, |
| 699 | "d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`, |
| 700 | "e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F`, |
| 701 | "f/f.go": `package f; const F = "f"`, |
| 702 | }}}) |
| 703 | defer exported.Cleanup() |
| 704 | |
| 705 | exported.Config.Mode = packages.LoadSyntax |
| 706 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c") |
| 707 | if err != nil { |
| 708 | t.Fatal(err) |
| 709 | } |
| 710 | |
| 711 | graph, all := importGraph(initial) |
| 712 | wantGraph := ` |
| 713 | * golang.org/fake/a |
| 714 | golang.org/fake/b |
| 715 | * golang.org/fake/c |
| 716 | golang.org/fake/d |
| 717 | golang.org/fake/e |
| 718 | golang.org/fake/f |
| 719 | golang.org/fake/a -> golang.org/fake/b |
| 720 | golang.org/fake/b -> golang.org/fake/c |
| 721 | golang.org/fake/c -> golang.org/fake/d |
| 722 | golang.org/fake/d -> golang.org/fake/e |
| 723 | golang.org/fake/e -> golang.org/fake/f |
| 724 | `[1:] |
| 725 | if graph != wantGraph { |
| 726 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 727 | } |
| 728 | |
| 729 | for _, test := range []struct { |
| 730 | id string |
| 731 | wantSyntax bool |
| 732 | wantComplete bool |
| 733 | }{ |
| 734 | {"golang.org/fake/a", true, true}, // source package |
| 735 | {"golang.org/fake/b", true, true}, // source package because depends on initial package |
| 736 | {"golang.org/fake/c", true, true}, // source package |
| 737 | {"golang.org/fake/d", false, true}, // export data package |
| 738 | {"golang.org/fake/e", false, false}, // export data package |
| 739 | {"golang.org/fake/f", false, false}, // export data package |
| 740 | } { |
| 741 | // TODO(matloob): LoadSyntax and LoadAllSyntax are now equivalent, wantSyntax and wantComplete |
| 742 | // are true for all packages in the transitive dependency set. Add test cases on the individual |
| 743 | // Need* fields to check the equivalents on the new API. |
| 744 | p := all[test.id] |
| 745 | if p == nil { |
| 746 | t.Errorf("missing package: %s", test.id) |
| 747 | continue |
| 748 | } |
| 749 | if p.Types == nil { |
| 750 | t.Errorf("missing types.Package for %s", p) |
| 751 | continue |
| 752 | } else if p.Types.Complete() != test.wantComplete { |
| 753 | if test.wantComplete { |
| 754 | t.Errorf("incomplete types.Package for %s", p) |
| 755 | } else { |
| 756 | t.Errorf("unexpected complete types.Package for %s", p) |
| 757 | } |
| 758 | } |
| 759 | if (p.Syntax != nil) != test.wantSyntax { |
| 760 | if test.wantSyntax { |
| 761 | t.Errorf("missing ast.Files for %s", p) |
| 762 | } else { |
| 763 | t.Errorf("unexpected ast.Files for for %s", p) |
| 764 | } |
| 765 | } |
| 766 | if p.Errors != nil { |
| 767 | t.Errorf("errors in package: %s: %s", p, p.Errors) |
| 768 | } |
| 769 | } |
| 770 | |
| 771 | // Check value of constant. |
| 772 | aA := constant(all["golang.org/fake/a"], "A") |
| 773 | if aA == nil { |
| 774 | t.Fatalf("a.A: got nil") |
| 775 | } |
| 776 | if got, want := fmt.Sprintf("%v %v", aA, aA.Val()), `const golang.org/fake/a.A untyped string "abcdef"`; got != want { |
| 777 | t.Errorf("a.A: got %s, want %s", got, want) |
| 778 | } |
| 779 | } |
| 780 | |
| 781 | func TestLoadDiamondTypes(t *testing.T) { testAllOrModulesParallel(t, testLoadDiamondTypes) } |
| 782 | func testLoadDiamondTypes(t *testing.T, exporter packagestest.Exporter) { |
| 783 | // We make a diamond dependency and check the type d.D is the same through both paths |
| 784 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 785 | Name: "golang.org/fake", |
| 786 | Files: map[string]interface{}{ |
| 787 | "a/a.go": `package a; import ("golang.org/fake/b"; "golang.org/fake/c"); var _ = b.B == c.C`, |
| 788 | "b/b.go": `package b; import "golang.org/fake/d"; var B d.D`, |
| 789 | "c/c.go": `package c; import "golang.org/fake/d"; var C d.D`, |
| 790 | "d/d.go": `package d; type D int`, |
| 791 | }}}) |
| 792 | defer exported.Cleanup() |
| 793 | |
| 794 | exported.Config.Mode = packages.LoadSyntax |
| 795 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 796 | if err != nil { |
| 797 | t.Fatal(err) |
| 798 | } |
| 799 | packages.Visit(initial, nil, func(pkg *packages.Package) { |
| 800 | for _, err := range pkg.Errors { |
| 801 | t.Errorf("package %s: %v", pkg.ID, err) |
| 802 | } |
| 803 | }) |
| 804 | |
| 805 | graph, _ := importGraph(initial) |
| 806 | wantGraph := ` |
| 807 | * golang.org/fake/a |
| 808 | golang.org/fake/b |
| 809 | golang.org/fake/c |
| 810 | golang.org/fake/d |
| 811 | golang.org/fake/a -> golang.org/fake/b |
| 812 | golang.org/fake/a -> golang.org/fake/c |
| 813 | golang.org/fake/b -> golang.org/fake/d |
| 814 | golang.org/fake/c -> golang.org/fake/d |
| 815 | `[1:] |
| 816 | if graph != wantGraph { |
| 817 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 818 | } |
| 819 | } |
| 820 | |
| 821 | func TestLoadSyntaxError(t *testing.T) { testAllOrModulesParallel(t, testLoadSyntaxError) } |
| 822 | func testLoadSyntaxError(t *testing.T, exporter packagestest.Exporter) { |
| 823 | // A type error in a lower-level package (e) prevents go list |
| 824 | // from producing export data for all packages that depend on it |
| 825 | // [a-e]. Only f should be loaded from export data, and the rest |
| 826 | // should be IllTyped. |
| 827 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 828 | Name: "golang.org/fake", |
| 829 | Files: map[string]interface{}{ |
| 830 | "a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`, |
| 831 | "b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`, |
| 832 | "c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`, |
| 833 | "d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`, |
| 834 | "e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F + 1`, // type error |
| 835 | "f/f.go": `package f; const F = "f"`, |
| 836 | }}}) |
| 837 | defer exported.Cleanup() |
| 838 | |
| 839 | exported.Config.Mode = packages.LoadSyntax |
| 840 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c") |
| 841 | if err != nil { |
| 842 | t.Fatal(err) |
| 843 | } |
| 844 | |
| 845 | all := make(map[string]*packages.Package) |
| 846 | packages.Visit(initial, nil, func(p *packages.Package) { |
| 847 | all[p.ID] = p |
| 848 | }) |
| 849 | |
| 850 | for _, test := range []struct { |
| 851 | id string |
| 852 | wantSyntax bool |
| 853 | wantIllTyped bool |
| 854 | }{ |
| 855 | {"golang.org/fake/a", true, true}, |
| 856 | {"golang.org/fake/b", true, true}, |
| 857 | {"golang.org/fake/c", true, true}, |
| 858 | {"golang.org/fake/d", true, true}, |
| 859 | {"golang.org/fake/e", true, true}, |
| 860 | {"golang.org/fake/f", false, false}, |
| 861 | } { |
| 862 | p := all[test.id] |
| 863 | if p == nil { |
| 864 | t.Errorf("missing package: %s", test.id) |
| 865 | continue |
| 866 | } |
| 867 | if p.Types == nil { |
| 868 | t.Errorf("missing types.Package for %s", p) |
| 869 | continue |
| 870 | } else if !p.Types.Complete() { |
| 871 | t.Errorf("incomplete types.Package for %s", p) |
| 872 | } |
| 873 | if (p.Syntax != nil) != test.wantSyntax { |
| 874 | if test.wantSyntax { |
| 875 | t.Errorf("missing ast.Files for %s", test.id) |
| 876 | } else { |
| 877 | t.Errorf("unexpected ast.Files for for %s", test.id) |
| 878 | } |
| 879 | } |
| 880 | if p.IllTyped != test.wantIllTyped { |
| 881 | t.Errorf("IllTyped was %t for %s", p.IllTyped, test.id) |
| 882 | } |
| 883 | } |
| 884 | |
| 885 | // Check value of constant. |
| 886 | aA := constant(all["golang.org/fake/a"], "A") |
| 887 | if aA == nil { |
| 888 | t.Fatalf("a.A: got nil") |
| 889 | } |
| 890 | if got, want := aA.String(), `const golang.org/fake/a.A invalid type`; got != want { |
| 891 | t.Errorf("a.A: got %s, want %s", got, want) |
| 892 | } |
| 893 | } |
| 894 | |
| 895 | // This function tests use of the ParseFile hook to modify |
| 896 | // the AST after parsing. |
| 897 | func TestParseFileModifyAST(t *testing.T) { testAllOrModulesParallel(t, testParseFileModifyAST) } |
| 898 | func testParseFileModifyAST(t *testing.T, exporter packagestest.Exporter) { |
| 899 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 900 | Name: "golang.org/fake", |
| 901 | Files: map[string]interface{}{ |
| 902 | "a/a.go": `package a; const A = "a" `, |
| 903 | }}}) |
| 904 | defer exported.Cleanup() |
| 905 | |
| 906 | exported.Config.Mode = packages.LoadAllSyntax |
| 907 | exported.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { |
| 908 | const mode = parser.AllErrors | parser.ParseComments |
| 909 | f, err := parser.ParseFile(fset, filename, src, mode) |
| 910 | // modify AST to change `const A = "a"` to `const A = "b"` |
| 911 | spec := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec) |
| 912 | spec.Values[0].(*ast.BasicLit).Value = `"b"` |
| 913 | return f, err |
| 914 | } |
| 915 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 916 | if err != nil { |
| 917 | t.Error(err) |
| 918 | } |
| 919 | |
| 920 | // Check value of a.A has been set to "b" |
| 921 | a := initial[0] |
| 922 | got := constant(a, "A").Val().String() |
| 923 | if got != `"b"` { |
| 924 | t.Errorf("a.A: got %s, want %s", got, `"b"`) |
| 925 | } |
| 926 | } |
| 927 | |
| 928 | func TestAdHocPackagesBadImport(t *testing.T) { |
| 929 | t.Parallel() |
| 930 | |
| 931 | // This test doesn't use packagestest because we are testing ad-hoc packages, |
| 932 | // which are outside of $GOPATH and outside of a module. |
| 933 | tmp, err := ioutil.TempDir("", "a") |
| 934 | if err != nil { |
| 935 | t.Fatal(err) |
| 936 | } |
| 937 | defer os.RemoveAll(tmp) |
| 938 | |
| 939 | filename := filepath.Join(tmp, "a.go") |
| 940 | content := []byte(`package a |
| 941 | import _ "badimport" |
| 942 | const A = 1 |
| 943 | `) |
| 944 | if err := ioutil.WriteFile(filename, content, 0775); err != nil { |
| 945 | t.Fatal(err) |
| 946 | } |
| 947 | |
| 948 | // Make sure that the user's value of GO111MODULE does not affect test results. |
| 949 | for _, go111module := range []string{"off", "auto", "on"} { |
| 950 | config := &packages.Config{ |
| 951 | Env: append(os.Environ(), "GOPACKAGESDRIVER=off", fmt.Sprintf("GO111MODULE=%s", go111module)), |
| 952 | Dir: tmp, |
| 953 | Mode: packages.LoadAllSyntax, |
| 954 | Logf: t.Logf, |
| 955 | } |
| 956 | initial, err := packages.Load(config, fmt.Sprintf("file=%s", filename)) |
| 957 | if err != nil { |
| 958 | t.Error(err) |
| 959 | } |
| 960 | if len(initial) == 0 { |
| 961 | t.Fatalf("no packages for %s with GO111MODULE=%s", filename, go111module) |
| 962 | } |
| 963 | // Check value of a.A. |
| 964 | a := initial[0] |
| 965 | // There's an error because there's a bad import. |
| 966 | aA := constant(a, "A") |
| 967 | if aA == nil { |
| 968 | t.Errorf("a.A: got nil") |
| 969 | return |
| 970 | } |
| 971 | got := aA.Val().String() |
| 972 | if want := "1"; got != want { |
| 973 | t.Errorf("a.A: got %s, want %s", got, want) |
| 974 | } |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | func TestLoadAllSyntaxImportErrors(t *testing.T) { |
| 979 | testAllOrModulesParallel(t, testLoadAllSyntaxImportErrors) |
| 980 | } |
| 981 | func testLoadAllSyntaxImportErrors(t *testing.T, exporter packagestest.Exporter) { |
| 982 | // TODO(matloob): Remove this once go list -e -compiled is fixed. |
| 983 | // See https://golang.org/issue/26755 |
| 984 | t.Skip("go list -compiled -e fails with non-zero exit status for empty packages") |
| 985 | |
| 986 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 987 | Name: "golang.org/fake", |
| 988 | Files: map[string]interface{}{ |
| 989 | "unicycle/unicycle.go": `package unicycle; import _ "unicycle"`, |
| 990 | "bicycle1/bicycle1.go": `package bicycle1; import _ "bicycle2"`, |
| 991 | "bicycle2/bicycle2.go": `package bicycle2; import _ "bicycle1"`, |
| 992 | "bad/bad.go": `not a package declaration`, |
| 993 | "empty/README.txt": `not a go file`, |
| 994 | "root/root.go": `package root |
| 995 | import ( |
| 996 | _ "bicycle1" |
| 997 | _ "unicycle" |
| 998 | _ "nonesuch" |
| 999 | _ "empty" |
| 1000 | _ "bad" |
| 1001 | )`, |
| 1002 | }}}) |
| 1003 | defer exported.Cleanup() |
| 1004 | |
| 1005 | exported.Config.Mode = packages.LoadAllSyntax |
| 1006 | initial, err := packages.Load(exported.Config, "root") |
| 1007 | if err != nil { |
| 1008 | t.Fatal(err) |
| 1009 | } |
| 1010 | |
| 1011 | // Cycle-forming edges are removed from the graph: |
| 1012 | // bicycle2 -> bicycle1 |
| 1013 | // unicycle -> unicycle |
| 1014 | graph, all := importGraph(initial) |
| 1015 | wantGraph := ` |
| 1016 | bicycle1 |
| 1017 | bicycle2 |
| 1018 | * root |
| 1019 | unicycle |
| 1020 | bicycle1 -> bicycle2 |
| 1021 | root -> bicycle1 |
| 1022 | root -> unicycle |
| 1023 | `[1:] |
| 1024 | if graph != wantGraph { |
| 1025 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 1026 | } |
| 1027 | for _, test := range []struct { |
| 1028 | id string |
| 1029 | wantErrs []string |
| 1030 | }{ |
| 1031 | {"bicycle1", nil}, |
| 1032 | {"bicycle2", []string{ |
| 1033 | "could not import bicycle1 (import cycle: [root bicycle1 bicycle2])", |
| 1034 | }}, |
| 1035 | {"unicycle", []string{ |
| 1036 | "could not import unicycle (import cycle: [root unicycle])", |
| 1037 | }}, |
| 1038 | {"root", []string{ |
| 1039 | `could not import bad (missing package: "bad")`, |
| 1040 | `could not import empty (missing package: "empty")`, |
| 1041 | `could not import nonesuch (missing package: "nonesuch")`, |
| 1042 | }}, |
| 1043 | } { |
| 1044 | p := all[test.id] |
| 1045 | if p == nil { |
| 1046 | t.Errorf("missing package: %s", test.id) |
| 1047 | continue |
| 1048 | } |
| 1049 | if p.Types == nil { |
| 1050 | t.Errorf("missing types.Package for %s", test.id) |
| 1051 | } |
| 1052 | if p.Syntax == nil { |
| 1053 | t.Errorf("missing ast.Files for %s", test.id) |
| 1054 | } |
| 1055 | if !p.IllTyped { |
| 1056 | t.Errorf("IllTyped was false for %s", test.id) |
| 1057 | } |
| 1058 | if errs := errorMessages(p.Errors); !reflect.DeepEqual(errs, test.wantErrs) { |
| 1059 | t.Errorf("in package %s, got errors %s, want %s", p, errs, test.wantErrs) |
| 1060 | } |
| 1061 | } |
| 1062 | } |
| 1063 | |
| 1064 | func TestAbsoluteFilenames(t *testing.T) { testAllOrModulesParallel(t, testAbsoluteFilenames) } |
| 1065 | func testAbsoluteFilenames(t *testing.T, exporter packagestest.Exporter) { |
| 1066 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1067 | Name: "golang.org/fake", |
| 1068 | Files: map[string]interface{}{ |
| 1069 | "a/a.go": `package a; const A = 1`, |
| 1070 | "b/b.go": `package b; import ("golang.org/fake/a"; _ "errors"); var B = a.A`, |
| 1071 | "b/vendor/a/a.go": `package a; const A = 1`, |
| 1072 | "c/c.go": `package c; import (_ "golang.org/fake/b"; _ "unsafe")`, |
| 1073 | "c/c2.go": "// +build ignore\n\n" + `package c; import _ "fmt"`, |
| 1074 | "subdir/d/d.go": `package d`, |
| 1075 | "subdir/e/d.go": `package e`, |
| 1076 | "e/e.go": `package main; import _ "golang.org/fake/b"`, |
| 1077 | "e/e2.go": `package main; import _ "golang.org/fake/c"`, |
| 1078 | "f/f.go": `package f`, |
| 1079 | "f/f.s": ``, |
| 1080 | "g/g.go": `package g; import _ "embed";` + "\n//go:embed g2.txt\n" + `var s string`, |
| 1081 | "g/g2.txt": "hello", |
| 1082 | "h/h.go": `package g; import _ "embed";` + "\n//go:embed a*.txt\n" + `var s string`, |
| 1083 | "h/aa.txt": "hello", |
| 1084 | }}}) |
| 1085 | defer exported.Cleanup() |
| 1086 | exported.Config.Dir = filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) |
| 1087 | |
| 1088 | checkFile := func(filename string) { |
| 1089 | if !filepath.IsAbs(filename) { |
| 1090 | t.Errorf("filename is not absolute: %s", filename) |
| 1091 | } |
| 1092 | if _, err := os.Stat(filename); err != nil { |
| 1093 | t.Errorf("stat error, %s: %v", filename, err) |
| 1094 | } |
| 1095 | } |
| 1096 | |
| 1097 | for _, test := range []struct { |
| 1098 | pattern string |
| 1099 | want string |
| 1100 | }{ |
| 1101 | // Import paths |
| 1102 | {"golang.org/fake/a", "a.go"}, |
| 1103 | {"golang.org/fake/b/vendor/a", "a.go"}, |
| 1104 | {"golang.org/fake/b", "b.go"}, |
| 1105 | {"golang.org/fake/c", "c.go"}, |
| 1106 | {"golang.org/fake/subdir/d", "d.go"}, |
| 1107 | {"golang.org/fake/subdir/e", "d.go"}, |
| 1108 | {"golang.org/fake/e", "e.go e2.go"}, |
| 1109 | {"golang.org/fake/f", "f.go f.s"}, |
| 1110 | {"golang.org/fake/g", "g.go g2.txt"}, |
| 1111 | {"golang.org/fake/h", "h.go aa.txt"}, |
| 1112 | // Relative paths |
| 1113 | {"./a", "a.go"}, |
| 1114 | {"./b/vendor/a", "a.go"}, |
| 1115 | {"./b", "b.go"}, |
| 1116 | {"./c", "c.go"}, |
| 1117 | {"./subdir/d", "d.go"}, |
| 1118 | {"./subdir/e", "d.go"}, |
| 1119 | {"./e", "e.go e2.go"}, |
| 1120 | {"./f", "f.go f.s"}, |
| 1121 | {"./g", "g.go g2.txt"}, |
| 1122 | {"./h", "h.go aa.txt"}, |
| 1123 | } { |
| 1124 | exported.Config.Mode = packages.LoadFiles | packages.NeedEmbedFiles |
| 1125 | pkgs, err := packages.Load(exported.Config, test.pattern) |
| 1126 | if err != nil { |
| 1127 | t.Errorf("pattern %s: %v", test.pattern, err) |
| 1128 | continue |
| 1129 | } |
| 1130 | |
| 1131 | if got := strings.Join(srcs(pkgs[0]), " "); got != test.want { |
| 1132 | t.Errorf("in package %s, got %s, want %s", test.pattern, got, test.want) |
| 1133 | } |
| 1134 | |
| 1135 | // Test that files in all packages exist and are absolute paths. |
| 1136 | _, all := importGraph(pkgs) |
| 1137 | for _, pkg := range all { |
| 1138 | for _, filename := range pkg.GoFiles { |
| 1139 | checkFile(filename) |
| 1140 | } |
| 1141 | for _, filename := range pkg.OtherFiles { |
| 1142 | checkFile(filename) |
| 1143 | } |
| 1144 | for _, filename := range pkg.EmbedFiles { |
| 1145 | checkFile(filename) |
| 1146 | } |
| 1147 | for _, filename := range pkg.IgnoredFiles { |
| 1148 | checkFile(filename) |
| 1149 | } |
| 1150 | } |
| 1151 | } |
| 1152 | } |
| 1153 | |
| 1154 | func TestContains(t *testing.T) { testAllOrModulesParallel(t, testContains) } |
| 1155 | func testContains(t *testing.T, exporter packagestest.Exporter) { |
| 1156 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1157 | Name: "golang.org/fake", |
| 1158 | Files: map[string]interface{}{ |
| 1159 | "a/a.go": `package a; import "golang.org/fake/b"`, |
| 1160 | "b/b.go": `package b; import "golang.org/fake/c"`, |
| 1161 | "c/c.go": `package c`, |
| 1162 | }}}) |
| 1163 | defer exported.Cleanup() |
| 1164 | bFile := exported.File("golang.org/fake", "b/b.go") |
| 1165 | exported.Config.Mode = packages.LoadImports |
| 1166 | initial, err := packages.Load(exported.Config, "file="+bFile) |
| 1167 | if err != nil { |
| 1168 | t.Fatal(err) |
| 1169 | } |
| 1170 | |
| 1171 | graph, _ := importGraph(initial) |
| 1172 | wantGraph := ` |
| 1173 | * golang.org/fake/b |
| 1174 | golang.org/fake/c |
| 1175 | golang.org/fake/b -> golang.org/fake/c |
| 1176 | `[1:] |
| 1177 | if graph != wantGraph { |
| 1178 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 1179 | } |
| 1180 | } |
| 1181 | |
| 1182 | // This test ensures that the effective GOARCH variable in the |
| 1183 | // application determines the Sizes function used by the type checker. |
| 1184 | // This behavior is a stop-gap until we make the build system's query |
| 1185 | // tool report the correct sizes function for the actual configuration. |
| 1186 | func TestSizes(t *testing.T) { testAllOrModulesParallel(t, testSizes) } |
| 1187 | func testSizes(t *testing.T, exporter packagestest.Exporter) { |
| 1188 | // Only run this test on operating systems that have both an amd64 and 386 port. |
| 1189 | switch runtime.GOOS { |
| 1190 | case "linux", "windows", "freebsd", "openbsd", "netbsd", "android": |
| 1191 | default: |
| 1192 | t.Skipf("skipping test on %s", runtime.GOOS) |
| 1193 | } |
| 1194 | |
| 1195 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1196 | Name: "golang.org/fake", |
| 1197 | Files: map[string]interface{}{ |
| 1198 | "a/a.go": `package a; import "unsafe"; const WordSize = 8*unsafe.Sizeof(int(0))`, |
| 1199 | }}}) |
| 1200 | defer exported.Cleanup() |
| 1201 | |
| 1202 | exported.Config.Mode = packages.LoadSyntax |
| 1203 | savedEnv := exported.Config.Env |
| 1204 | for arch, wantWordSize := range map[string]int64{"386": 32, "amd64": 64} { |
| 1205 | exported.Config.Env = append(savedEnv, "GOARCH="+arch) |
| 1206 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 1207 | if err != nil { |
| 1208 | t.Fatal(err) |
| 1209 | } |
| 1210 | if packages.PrintErrors(initial) > 0 { |
| 1211 | t.Fatal("there were errors") |
| 1212 | } |
| 1213 | gotWordSize, _ := constantpkg.Int64Val(constant(initial[0], "WordSize").Val()) |
| 1214 | if gotWordSize != wantWordSize { |
| 1215 | t.Errorf("for GOARCH=%s, got word size %d, want %d", arch, gotWordSize, wantWordSize) |
| 1216 | } |
| 1217 | } |
| 1218 | } |
| 1219 | |
| 1220 | // TestContainsFallbackSticks ensures that when there are both contains and non-contains queries |
| 1221 | // the decision whether to fallback to the pre-1.11 go list sticks across both sets of calls to |
| 1222 | // go list. |
| 1223 | func TestContainsFallbackSticks(t *testing.T) { |
| 1224 | testAllOrModulesParallel(t, testContainsFallbackSticks) |
| 1225 | } |
| 1226 | func testContainsFallbackSticks(t *testing.T, exporter packagestest.Exporter) { |
| 1227 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1228 | Name: "golang.org/fake", |
| 1229 | Files: map[string]interface{}{ |
| 1230 | "a/a.go": `package a; import "golang.org/fake/b"`, |
| 1231 | "b/b.go": `package b; import "golang.org/fake/c"`, |
| 1232 | "c/c.go": `package c`, |
| 1233 | }}}) |
| 1234 | defer exported.Cleanup() |
| 1235 | |
| 1236 | exported.Config.Mode = packages.LoadImports |
| 1237 | bFile := exported.File("golang.org/fake", "b/b.go") |
| 1238 | initial, err := packages.Load(exported.Config, "golang.org/fake/a", "file="+bFile) |
| 1239 | if err != nil { |
| 1240 | t.Fatal(err) |
| 1241 | } |
| 1242 | |
| 1243 | graph, _ := importGraph(initial) |
| 1244 | wantGraph := ` |
| 1245 | * golang.org/fake/a |
| 1246 | * golang.org/fake/b |
| 1247 | golang.org/fake/c |
| 1248 | golang.org/fake/a -> golang.org/fake/b |
| 1249 | golang.org/fake/b -> golang.org/fake/c |
| 1250 | `[1:] |
| 1251 | if graph != wantGraph { |
| 1252 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 1253 | } |
| 1254 | } |
| 1255 | |
| 1256 | // Test that Load with no patterns is equivalent to loading "." via the golist |
| 1257 | // driver. |
| 1258 | func TestNoPatterns(t *testing.T) { testAllOrModulesParallel(t, testNoPatterns) } |
| 1259 | func testNoPatterns(t *testing.T, exporter packagestest.Exporter) { |
| 1260 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1261 | Name: "golang.org/fake", |
| 1262 | Files: map[string]interface{}{ |
| 1263 | "a/a.go": `package a;`, |
| 1264 | "a/b/b.go": `package b;`, |
| 1265 | }}}) |
| 1266 | defer exported.Cleanup() |
| 1267 | |
| 1268 | aDir := filepath.Dir(exported.File("golang.org/fake", "a/a.go")) |
| 1269 | exported.Config.Dir = aDir |
| 1270 | |
| 1271 | initial, err := packages.Load(exported.Config) |
| 1272 | if err != nil { |
| 1273 | t.Fatal(err) |
| 1274 | } |
| 1275 | if len(initial) != 1 || initial[0].Name != "a" { |
| 1276 | t.Fatalf(`Load() = %v, wanted just the package in the current directory`, initial) |
| 1277 | } |
| 1278 | } |
| 1279 | |
| 1280 | func TestJSON(t *testing.T) { testAllOrModulesParallel(t, testJSON) } |
| 1281 | func testJSON(t *testing.T, exporter packagestest.Exporter) { |
| 1282 | //TODO: add in some errors |
| 1283 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1284 | Name: "golang.org/fake", |
| 1285 | Files: map[string]interface{}{ |
| 1286 | "a/a.go": `package a; const A = 1`, |
| 1287 | "b/b.go": `package b; import "golang.org/fake/a"; var B = a.A`, |
| 1288 | "c/c.go": `package c; import "golang.org/fake/b" ; var C = b.B`, |
| 1289 | "d/d.go": `package d; import "golang.org/fake/b" ; var D = b.B`, |
| 1290 | }}}) |
| 1291 | defer exported.Cleanup() |
| 1292 | |
| 1293 | exported.Config.Mode = packages.LoadImports |
| 1294 | initial, err := packages.Load(exported.Config, "golang.org/fake/c", "golang.org/fake/d") |
| 1295 | if err != nil { |
| 1296 | t.Fatal(err) |
| 1297 | } |
| 1298 | |
| 1299 | // Visit and print all packages. |
| 1300 | buf := &bytes.Buffer{} |
| 1301 | enc := json.NewEncoder(buf) |
| 1302 | enc.SetIndent("", "\t") |
| 1303 | packages.Visit(initial, nil, func(pkg *packages.Package) { |
| 1304 | // trim the source lists for stable results |
| 1305 | pkg.GoFiles = cleanPaths(pkg.GoFiles) |
| 1306 | pkg.CompiledGoFiles = cleanPaths(pkg.CompiledGoFiles) |
| 1307 | pkg.OtherFiles = cleanPaths(pkg.OtherFiles) |
| 1308 | pkg.IgnoredFiles = cleanPaths(pkg.IgnoredFiles) |
| 1309 | if err := enc.Encode(pkg); err != nil { |
| 1310 | t.Fatal(err) |
| 1311 | } |
| 1312 | }) |
| 1313 | |
| 1314 | wantJSON := ` |
| 1315 | { |
| 1316 | "ID": "golang.org/fake/a", |
| 1317 | "Name": "a", |
| 1318 | "PkgPath": "golang.org/fake/a", |
| 1319 | "GoFiles": [ |
| 1320 | "a.go" |
| 1321 | ], |
| 1322 | "CompiledGoFiles": [ |
| 1323 | "a.go" |
| 1324 | ] |
| 1325 | } |
| 1326 | { |
| 1327 | "ID": "golang.org/fake/b", |
| 1328 | "Name": "b", |
| 1329 | "PkgPath": "golang.org/fake/b", |
| 1330 | "GoFiles": [ |
| 1331 | "b.go" |
| 1332 | ], |
| 1333 | "CompiledGoFiles": [ |
| 1334 | "b.go" |
| 1335 | ], |
| 1336 | "Imports": { |
| 1337 | "golang.org/fake/a": "golang.org/fake/a" |
| 1338 | } |
| 1339 | } |
| 1340 | { |
| 1341 | "ID": "golang.org/fake/c", |
| 1342 | "Name": "c", |
| 1343 | "PkgPath": "golang.org/fake/c", |
| 1344 | "GoFiles": [ |
| 1345 | "c.go" |
| 1346 | ], |
| 1347 | "CompiledGoFiles": [ |
| 1348 | "c.go" |
| 1349 | ], |
| 1350 | "Imports": { |
| 1351 | "golang.org/fake/b": "golang.org/fake/b" |
| 1352 | } |
| 1353 | } |
| 1354 | { |
| 1355 | "ID": "golang.org/fake/d", |
| 1356 | "Name": "d", |
| 1357 | "PkgPath": "golang.org/fake/d", |
| 1358 | "GoFiles": [ |
| 1359 | "d.go" |
| 1360 | ], |
| 1361 | "CompiledGoFiles": [ |
| 1362 | "d.go" |
| 1363 | ], |
| 1364 | "Imports": { |
| 1365 | "golang.org/fake/b": "golang.org/fake/b" |
| 1366 | } |
| 1367 | } |
| 1368 | `[1:] |
| 1369 | |
| 1370 | if buf.String() != wantJSON { |
| 1371 | t.Errorf("wrong JSON: got <<%s>>, want <<%s>>", buf.String(), wantJSON) |
| 1372 | } |
| 1373 | // now decode it again |
| 1374 | var decoded []*packages.Package |
| 1375 | dec := json.NewDecoder(buf) |
| 1376 | for dec.More() { |
| 1377 | p := new(packages.Package) |
| 1378 | if err := dec.Decode(p); err != nil { |
| 1379 | t.Fatal(err) |
| 1380 | } |
| 1381 | decoded = append(decoded, p) |
| 1382 | } |
| 1383 | if len(decoded) != 4 { |
| 1384 | t.Fatalf("got %d packages, want 4", len(decoded)) |
| 1385 | } |
| 1386 | for i, want := range []*packages.Package{{ |
| 1387 | ID: "golang.org/fake/a", |
| 1388 | Name: "a", |
| 1389 | }, { |
| 1390 | ID: "golang.org/fake/b", |
| 1391 | Name: "b", |
| 1392 | Imports: map[string]*packages.Package{ |
| 1393 | "golang.org/fake/a": {ID: "golang.org/fake/a"}, |
| 1394 | }, |
| 1395 | }, { |
| 1396 | ID: "golang.org/fake/c", |
| 1397 | Name: "c", |
| 1398 | Imports: map[string]*packages.Package{ |
| 1399 | "golang.org/fake/b": {ID: "golang.org/fake/b"}, |
| 1400 | }, |
| 1401 | }, { |
| 1402 | ID: "golang.org/fake/d", |
| 1403 | Name: "d", |
| 1404 | Imports: map[string]*packages.Package{ |
| 1405 | "golang.org/fake/b": {ID: "golang.org/fake/b"}, |
| 1406 | }, |
| 1407 | }} { |
| 1408 | got := decoded[i] |
| 1409 | if got.ID != want.ID { |
| 1410 | t.Errorf("Package %d has ID %q want %q", i, got.ID, want.ID) |
| 1411 | } |
| 1412 | if got.Name != want.Name { |
| 1413 | t.Errorf("Package %q has Name %q want %q", got.ID, got.Name, want.Name) |
| 1414 | } |
| 1415 | if len(got.Imports) != len(want.Imports) { |
| 1416 | t.Errorf("Package %q has %d imports want %d", got.ID, len(got.Imports), len(want.Imports)) |
| 1417 | continue |
| 1418 | } |
| 1419 | for path, ipkg := range got.Imports { |
| 1420 | if want.Imports[path] == nil { |
| 1421 | t.Errorf("Package %q has unexpected import %q", got.ID, path) |
| 1422 | continue |
| 1423 | } |
| 1424 | if want.Imports[path].ID != ipkg.ID { |
| 1425 | t.Errorf("Package %q import %q is %q want %q", got.ID, path, ipkg.ID, want.Imports[path].ID) |
| 1426 | } |
| 1427 | } |
| 1428 | } |
| 1429 | } |
| 1430 | |
| 1431 | func TestRejectInvalidQueries(t *testing.T) { |
| 1432 | t.Parallel() |
| 1433 | |
| 1434 | queries := []string{"key=", "key=value"} |
| 1435 | cfg := &packages.Config{ |
| 1436 | Mode: packages.LoadImports, |
| 1437 | Env: append(os.Environ(), "GO111MODULE=off", "GOPACKAGESDRIVER=off"), |
| 1438 | } |
| 1439 | for _, q := range queries { |
| 1440 | if _, err := packages.Load(cfg, q); err == nil { |
| 1441 | t.Errorf("packages.Load(%q) succeeded. Expected \"invalid query type\" error", q) |
| 1442 | } else if !strings.Contains(err.Error(), "invalid query type") { |
| 1443 | t.Errorf("packages.Load(%q): got error %v, want \"invalid query type\" error", q, err) |
| 1444 | } |
| 1445 | } |
| 1446 | } |
| 1447 | |
| 1448 | func TestPatternPassthrough(t *testing.T) { testAllOrModulesParallel(t, testPatternPassthrough) } |
| 1449 | func testPatternPassthrough(t *testing.T, exporter packagestest.Exporter) { |
| 1450 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1451 | Name: "golang.org/fake", |
| 1452 | Files: map[string]interface{}{ |
| 1453 | "a/a.go": `package a;`, |
| 1454 | }}}) |
| 1455 | defer exported.Cleanup() |
| 1456 | |
| 1457 | initial, err := packages.Load(exported.Config, "pattern=a") |
| 1458 | if err != nil { |
| 1459 | t.Fatal(err) |
| 1460 | } |
| 1461 | |
| 1462 | graph, _ := importGraph(initial) |
| 1463 | wantGraph := ` |
| 1464 | * a |
| 1465 | `[1:] |
| 1466 | if graph != wantGraph { |
| 1467 | t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph) |
| 1468 | } |
| 1469 | |
| 1470 | } |
| 1471 | |
| 1472 | func TestConfigDefaultEnv(t *testing.T) { |
| 1473 | // packagestest.TestAll instead of testAllOrModulesParallel because this test |
| 1474 | // can't be parallelized (it modifies the environment). |
| 1475 | packagestest.TestAll(t, testConfigDefaultEnv) |
| 1476 | } |
| 1477 | func testConfigDefaultEnv(t *testing.T, exporter packagestest.Exporter) { |
| 1478 | const driverJSON = `{ |
| 1479 | "Roots": ["gopackagesdriver"], |
| 1480 | "Packages": [{"ID": "gopackagesdriver", "Name": "gopackagesdriver"}] |
| 1481 | }` |
| 1482 | var ( |
| 1483 | pathKey string |
| 1484 | driverScript packagestest.Writer |
| 1485 | ) |
| 1486 | switch runtime.GOOS { |
| 1487 | case "android": |
| 1488 | t.Skip("doesn't run on android") |
| 1489 | case "windows": |
| 1490 | // TODO(jayconrod): write an equivalent batch script for windows. |
| 1491 | // Hint: "type" can be used to read a file to stdout. |
| 1492 | t.Skip("test requires sh") |
| 1493 | case "plan9": |
| 1494 | pathKey = "path" |
| 1495 | driverScript = packagestest.Script(`#!/bin/rc |
| 1496 | |
| 1497 | cat <<'EOF' |
| 1498 | ` + driverJSON + ` |
| 1499 | EOF |
| 1500 | `) |
| 1501 | default: |
| 1502 | pathKey = "PATH" |
| 1503 | driverScript = packagestest.Script(`#!/bin/sh |
| 1504 | |
| 1505 | cat - <<'EOF' |
| 1506 | ` + driverJSON + ` |
| 1507 | EOF |
| 1508 | `) |
| 1509 | } |
| 1510 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1511 | Name: "golang.org/fake", |
| 1512 | Files: map[string]interface{}{ |
| 1513 | "bin/gopackagesdriver": driverScript, |
| 1514 | "golist/golist.go": "package golist", |
| 1515 | }}}) |
| 1516 | defer exported.Cleanup() |
| 1517 | driver := exported.File("golang.org/fake", "bin/gopackagesdriver") |
| 1518 | binDir := filepath.Dir(driver) |
| 1519 | if err := os.Chmod(driver, 0755); err != nil { |
| 1520 | t.Fatal(err) |
| 1521 | } |
| 1522 | |
| 1523 | path, ok := os.LookupEnv(pathKey) |
| 1524 | var pathWithDriver string |
| 1525 | if ok { |
| 1526 | pathWithDriver = binDir + string(os.PathListSeparator) + path |
| 1527 | } else { |
| 1528 | pathWithDriver = binDir |
| 1529 | } |
| 1530 | for _, test := range []struct { |
| 1531 | desc string |
| 1532 | path string |
| 1533 | driver string |
| 1534 | wantIDs string |
| 1535 | }{ |
| 1536 | { |
| 1537 | desc: "driver_off", |
| 1538 | path: pathWithDriver, |
| 1539 | driver: "off", |
| 1540 | wantIDs: "[golist]", |
| 1541 | }, { |
| 1542 | desc: "driver_unset", |
| 1543 | path: pathWithDriver, |
| 1544 | driver: "", |
| 1545 | wantIDs: "[gopackagesdriver]", |
| 1546 | }, { |
| 1547 | desc: "driver_set", |
| 1548 | path: "", |
| 1549 | driver: driver, |
| 1550 | wantIDs: "[gopackagesdriver]", |
| 1551 | }, |
| 1552 | } { |
| 1553 | t.Run(test.desc, func(t *testing.T) { |
| 1554 | oldPath := os.Getenv(pathKey) |
| 1555 | os.Setenv(pathKey, test.path) |
| 1556 | defer os.Setenv(pathKey, oldPath) |
| 1557 | // Clone exported.Config |
| 1558 | config := exported.Config |
| 1559 | config.Env = append([]string{}, exported.Config.Env...) |
| 1560 | config.Env = append(config.Env, "GOPACKAGESDRIVER="+test.driver) |
| 1561 | pkgs, err := packages.Load(exported.Config, "golist") |
| 1562 | if err != nil { |
| 1563 | t.Fatal(err) |
| 1564 | } |
| 1565 | |
| 1566 | gotIds := make([]string, len(pkgs)) |
| 1567 | for i, pkg := range pkgs { |
| 1568 | gotIds[i] = pkg.ID |
| 1569 | } |
| 1570 | if fmt.Sprint(pkgs) != test.wantIDs { |
| 1571 | t.Errorf("got %v; want %v", gotIds, test.wantIDs) |
| 1572 | } |
| 1573 | }) |
| 1574 | } |
| 1575 | } |
| 1576 | |
| 1577 | // This test that a simple x test package layout loads correctly. |
| 1578 | // There was a bug in go list where it returned multiple copies of the same |
| 1579 | // package (specifically in this case of golang.org/fake/a), and this triggered |
| 1580 | // a bug in go/packages where it would leave an empty entry in the root package |
| 1581 | // list. This would then cause a nil pointer crash. |
| 1582 | // This bug was triggered by the simple package layout below, and thus this |
| 1583 | // test will make sure the bug remains fixed. |
| 1584 | func TestBasicXTest(t *testing.T) { testAllOrModulesParallel(t, testBasicXTest) } |
| 1585 | func testBasicXTest(t *testing.T, exporter packagestest.Exporter) { |
| 1586 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1587 | Name: "golang.org/fake", |
| 1588 | Files: map[string]interface{}{ |
| 1589 | "a/a.go": `package a;`, |
| 1590 | "a/a_test.go": `package a_test;`, |
| 1591 | }}}) |
| 1592 | defer exported.Cleanup() |
| 1593 | |
| 1594 | exported.Config.Mode = packages.LoadFiles |
| 1595 | exported.Config.Tests = true |
| 1596 | _, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 1597 | if err != nil { |
| 1598 | t.Fatal(err) |
| 1599 | } |
| 1600 | } |
| 1601 | |
| 1602 | func TestErrorMissingFile(t *testing.T) { testAllOrModulesParallel(t, testErrorMissingFile) } |
| 1603 | func testErrorMissingFile(t *testing.T, exporter packagestest.Exporter) { |
| 1604 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1605 | Name: "golang.org/fake", |
| 1606 | Files: map[string]interface{}{ |
| 1607 | "a/a_test.go": `package a;`, |
| 1608 | }}}) |
| 1609 | defer exported.Cleanup() |
| 1610 | |
| 1611 | exported.Config.Mode = packages.LoadSyntax |
| 1612 | exported.Config.Tests = false |
| 1613 | pkgs, err := packages.Load(exported.Config, "missing.go") |
| 1614 | if err != nil { |
| 1615 | t.Fatal(err) |
| 1616 | } |
| 1617 | if len(pkgs) == 0 && runtime.GOOS == "windows" { |
| 1618 | t.Skip("Issue #31344: the ad-hoc command-line-arguments package isn't created on windows") |
| 1619 | } |
| 1620 | if len(pkgs) != 1 || (pkgs[0].PkgPath != "command-line-arguments" && pkgs[0].PkgPath != "missing.go") { |
| 1621 | t.Fatalf("packages.Load: want [command-line-arguments] or [missing.go], got %v", pkgs) |
| 1622 | } |
| 1623 | if len(pkgs[0].Errors) == 0 { |
| 1624 | t.Errorf("result of Load: want package with errors, got none: %+v", pkgs[0]) |
| 1625 | } |
| 1626 | } |
| 1627 | |
| 1628 | func TestReturnErrorWhenUsingNonGoFiles(t *testing.T) { |
| 1629 | testAllOrModulesParallel(t, testReturnErrorWhenUsingNonGoFiles) |
| 1630 | } |
| 1631 | func testReturnErrorWhenUsingNonGoFiles(t *testing.T, exporter packagestest.Exporter) { |
| 1632 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1633 | Name: "golang.org/gopatha", |
| 1634 | Files: map[string]interface{}{ |
| 1635 | "a/a.go": `package a`, |
| 1636 | }}, { |
| 1637 | Name: "golang.org/gopathb", |
| 1638 | Files: map[string]interface{}{ |
| 1639 | "b/b.c": `package b`, |
| 1640 | }}}) |
| 1641 | defer exported.Cleanup() |
| 1642 | config := packages.Config{Env: append(os.Environ(), "GOPACKAGESDRIVER=off")} |
| 1643 | pkgs, err := packages.Load(&config, "b/b.c") |
| 1644 | if err != nil { |
| 1645 | return |
| 1646 | } |
| 1647 | // Go <1.14 calls the package command-line-arguments while Go 1.14+ uses the file names. |
| 1648 | if len(pkgs) != 1 || (pkgs[0].PkgPath != "command-line-arguments" && pkgs[0].PkgPath != "b/b.c") { |
| 1649 | t.Fatalf("packages.Load: want [command-line-arguments] or [b/b.c], got %v", pkgs) |
| 1650 | } |
| 1651 | if len(pkgs[0].Errors) != 1 { |
| 1652 | t.Fatalf("result of Load: want package with one error, got: %+v", pkgs[0]) |
| 1653 | } |
| 1654 | } |
| 1655 | |
| 1656 | func TestReturnErrorWhenUsingGoFilesInMultipleDirectories(t *testing.T) { |
| 1657 | testAllOrModulesParallel(t, testReturnErrorWhenUsingGoFilesInMultipleDirectories) |
| 1658 | } |
| 1659 | func testReturnErrorWhenUsingGoFilesInMultipleDirectories(t *testing.T, exporter packagestest.Exporter) { |
| 1660 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1661 | Name: "golang.org/gopatha", |
| 1662 | Files: map[string]interface{}{ |
| 1663 | "a/a.go": `package a`, |
| 1664 | "b/b.go": `package b`, |
| 1665 | }}}) |
| 1666 | defer exported.Cleanup() |
| 1667 | want := "named files must all be in one directory" |
| 1668 | pkgs, err := packages.Load(exported.Config, exported.File("golang.org/gopatha", "a/a.go"), exported.File("golang.org/gopatha", "b/b.go")) |
| 1669 | if err != nil { |
| 1670 | // Check if the error returned is the one we expected. |
| 1671 | if !strings.Contains(err.Error(), want) { |
| 1672 | t.Fatalf("want error message: %s, got: %s", want, err.Error()) |
| 1673 | } |
| 1674 | return |
| 1675 | } |
| 1676 | if len(pkgs) != 1 || pkgs[0].PkgPath != "command-line-arguments" { |
| 1677 | t.Fatalf("packages.Load: want [command-line-arguments], got %v", pkgs) |
| 1678 | } |
| 1679 | if len(pkgs[0].Errors) != 1 { |
| 1680 | t.Fatalf("result of Load: want package with one error, got: %+v", pkgs[0]) |
| 1681 | } |
| 1682 | got := pkgs[0].Errors[0].Error() |
| 1683 | if !strings.Contains(got, want) { |
| 1684 | t.Fatalf("want error message: %s, got: %s", want, got) |
| 1685 | } |
| 1686 | } |
| 1687 | |
| 1688 | func TestReturnErrorForUnexpectedDirectoryLayout(t *testing.T) { |
| 1689 | testAllOrModulesParallel(t, testReturnErrorForUnexpectedDirectoryLayout) |
| 1690 | } |
| 1691 | func testReturnErrorForUnexpectedDirectoryLayout(t *testing.T, exporter packagestest.Exporter) { |
| 1692 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1693 | Name: "golang.org/gopatha", |
| 1694 | Files: map[string]interface{}{ |
| 1695 | "a/testdata/a.go": `package a; import _ "b"`, |
| 1696 | "a/vendor/b/b.go": `package b; import _ "fmt"`, |
| 1697 | }}}) |
| 1698 | defer exported.Cleanup() |
| 1699 | want := "unexpected directory layout" |
| 1700 | // triggering this error requires a relative package path |
| 1701 | exported.Config.Dir = filepath.Dir(exported.File("golang.org/gopatha", "a/testdata/a.go")) |
| 1702 | pkgs, err := packages.Load(exported.Config, ".") |
| 1703 | |
| 1704 | // This error doesn't seem to occur in module mode; so only |
| 1705 | // complain if we get zero packages while also getting no error. |
| 1706 | if err == nil { |
| 1707 | if len(pkgs) == 0 { |
| 1708 | // TODO(dh): we'll need to expand on the error check if/when Go stops emitting this error |
| 1709 | t.Fatalf("want error, got nil") |
| 1710 | } |
| 1711 | return |
| 1712 | } |
| 1713 | // Check if the error returned is the one we expected. |
| 1714 | if !strings.Contains(err.Error(), want) { |
| 1715 | t.Fatalf("want error message: %s, got: %s", want, err.Error()) |
| 1716 | } |
| 1717 | } |
| 1718 | |
| 1719 | func TestMissingDependency(t *testing.T) { testAllOrModulesParallel(t, testMissingDependency) } |
| 1720 | func testMissingDependency(t *testing.T, exporter packagestest.Exporter) { |
| 1721 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1722 | Name: "golang.org/fake", |
| 1723 | Files: map[string]interface{}{ |
| 1724 | "a/a.go": `package a; import _ "this/package/doesnt/exist"`, |
| 1725 | }}}) |
| 1726 | defer exported.Cleanup() |
| 1727 | |
| 1728 | exported.Config.Mode = packages.LoadAllSyntax |
| 1729 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 1730 | if err != nil { |
| 1731 | t.Fatal(err) |
| 1732 | } |
| 1733 | if len(pkgs) != 1 && pkgs[0].PkgPath != "golang.org/fake/a" { |
| 1734 | t.Fatalf("packages.Load: want [golang.org/fake/a], got %v", pkgs) |
| 1735 | } |
| 1736 | if len(pkgs[0].Errors) == 0 { |
| 1737 | t.Errorf("result of Load: want package with errors, got none: %+v", pkgs[0]) |
| 1738 | } |
| 1739 | } |
| 1740 | |
| 1741 | func TestAdHocContains(t *testing.T) { testAllOrModulesParallel(t, testAdHocContains) } |
| 1742 | func testAdHocContains(t *testing.T, exporter packagestest.Exporter) { |
| 1743 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1744 | Name: "golang.org/fake", |
| 1745 | Files: map[string]interface{}{ |
| 1746 | "a/a.go": `package a;`, |
| 1747 | }}}) |
| 1748 | defer exported.Cleanup() |
| 1749 | |
| 1750 | tmpfile, err := ioutil.TempFile("", "adhoc*.go") |
| 1751 | filename := tmpfile.Name() |
| 1752 | if err != nil { |
| 1753 | t.Fatal(err) |
| 1754 | } |
| 1755 | fmt.Fprint(tmpfile, `package main; import "fmt"; func main() { fmt.Println("time for coffee") }`) |
| 1756 | if err := tmpfile.Close(); err != nil { |
| 1757 | t.Fatal(err) |
| 1758 | } |
| 1759 | |
| 1760 | defer func() { |
| 1761 | if err := os.Remove(filename); err != nil { |
| 1762 | t.Fatal(err) |
| 1763 | } |
| 1764 | }() |
| 1765 | |
| 1766 | exported.Config.Mode = packages.NeedImports | packages.NeedFiles |
| 1767 | pkgs, err := packages.Load(exported.Config, "file="+filename) |
| 1768 | if err != nil { |
| 1769 | t.Fatal(err) |
| 1770 | } |
| 1771 | if len(pkgs) != 1 && pkgs[0].PkgPath != "command-line-arguments" { |
| 1772 | t.Fatalf("packages.Load: want [command-line-arguments], got %v", pkgs) |
| 1773 | } |
| 1774 | pkg := pkgs[0] |
| 1775 | if _, ok := pkg.Imports["fmt"]; !ok || len(pkg.Imports) != 1 { |
| 1776 | t.Fatalf("Imports of loaded package: want [fmt], got %v", pkg.Imports) |
| 1777 | } |
| 1778 | if len(pkg.GoFiles) != 1 || pkg.GoFiles[0] != filename { |
| 1779 | t.Fatalf("GoFiles of loaded package: want [%s], got %v", filename, pkg.GoFiles) |
| 1780 | } |
| 1781 | } |
| 1782 | |
| 1783 | func TestCgoNoCcompiler(t *testing.T) { testAllOrModulesParallel(t, testCgoNoCcompiler) } |
| 1784 | func testCgoNoCcompiler(t *testing.T, exporter packagestest.Exporter) { |
| 1785 | testenv.NeedsTool(t, "cgo") |
| 1786 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1787 | Name: "golang.org/fake", |
| 1788 | Files: map[string]interface{}{ |
| 1789 | "a/a.go": `package a |
| 1790 | import "net/http" |
| 1791 | const A = http.MethodGet |
| 1792 | `, |
| 1793 | }}}) |
| 1794 | defer exported.Cleanup() |
| 1795 | |
| 1796 | // Explicitly enable cgo but configure a nonexistent C compiler. |
| 1797 | exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1", "CC=doesnotexist") |
| 1798 | exported.Config.Mode = packages.LoadAllSyntax |
| 1799 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 1800 | |
| 1801 | if err != nil { |
| 1802 | t.Fatal(err) |
| 1803 | } |
| 1804 | |
| 1805 | // Check value of a.A. |
| 1806 | a := initial[0] |
| 1807 | aA := constant(a, "A") |
| 1808 | if aA == nil { |
| 1809 | t.Fatalf("a.A: got nil") |
| 1810 | } |
| 1811 | got := aA.Val().String() |
| 1812 | if got != "\"GET\"" { |
| 1813 | t.Errorf("a.A: got %s, want %s", got, "\"GET\"") |
| 1814 | } |
| 1815 | } |
| 1816 | |
| 1817 | func TestCgoMissingFile(t *testing.T) { testAllOrModulesParallel(t, testCgoMissingFile) } |
| 1818 | func testCgoMissingFile(t *testing.T, exporter packagestest.Exporter) { |
| 1819 | testenv.NeedsTool(t, "cgo") |
| 1820 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1821 | Name: "golang.org/fake", |
| 1822 | Files: map[string]interface{}{ |
| 1823 | "a/a.go": `package a |
| 1824 | |
| 1825 | // #include "foo.h" |
| 1826 | import "C" |
| 1827 | |
| 1828 | const A = 4 |
| 1829 | `, |
| 1830 | }}}) |
| 1831 | defer exported.Cleanup() |
| 1832 | |
| 1833 | // Explicitly enable cgo. |
| 1834 | exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1") |
| 1835 | exported.Config.Mode = packages.LoadAllSyntax |
| 1836 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 1837 | |
| 1838 | if err != nil { |
| 1839 | t.Fatal(err) |
| 1840 | } |
| 1841 | |
| 1842 | // Check value of a.A. |
| 1843 | a := initial[0] |
| 1844 | aA := constant(a, "A") |
| 1845 | if aA == nil { |
| 1846 | t.Fatalf("a.A: got nil") |
| 1847 | } |
| 1848 | got := aA.Val().String() |
| 1849 | if got != "4" { |
| 1850 | t.Errorf("a.A: got %s, want %s", got, "4") |
| 1851 | } |
| 1852 | } |
| 1853 | |
| 1854 | func TestLoadImportsC(t *testing.T) { |
| 1855 | // This test checks that when a package depends on the |
| 1856 | // test variant of "syscall", "unsafe", or "runtime/cgo", that dependency |
| 1857 | // is not removed when those packages are added when it imports "C". |
| 1858 | // |
| 1859 | // For this test to work, the external test of syscall must have a dependency |
| 1860 | // on net, and net must import "syscall" and "C". |
| 1861 | if runtime.GOOS == "windows" { |
| 1862 | t.Skipf("skipping on windows; packages on windows do not satisfy conditions for test.") |
| 1863 | } |
| 1864 | if runtime.GOOS == "plan9" { |
| 1865 | // See https://golang.org/issue/27100. |
| 1866 | t.Skip(`skipping on plan9; for some reason "net [syscall.test]" is not loaded`) |
| 1867 | } |
| 1868 | t.Parallel() |
| 1869 | testenv.NeedsGoPackages(t) |
| 1870 | |
| 1871 | cfg := &packages.Config{ |
| 1872 | Context: testCtx, |
| 1873 | Mode: packages.LoadImports, |
| 1874 | Tests: true, |
| 1875 | } |
| 1876 | initial, err := packages.Load(cfg, "syscall", "net") |
| 1877 | if err != nil { |
| 1878 | t.Fatalf("failed to load imports: %v", err) |
| 1879 | } |
| 1880 | |
| 1881 | _, all := importGraph(initial) |
| 1882 | |
| 1883 | for _, test := range []struct { |
| 1884 | pattern string |
| 1885 | wantImport string // an import to check for |
| 1886 | }{ |
| 1887 | {"net", "syscall:syscall"}, |
| 1888 | {"net [syscall.test]", "syscall:syscall [syscall.test]"}, |
| 1889 | {"syscall_test [syscall.test]", "net:net [syscall.test]"}, |
| 1890 | } { |
| 1891 | // Test the import paths. |
| 1892 | pkg := all[test.pattern] |
| 1893 | if pkg == nil { |
| 1894 | t.Errorf("package %q not loaded", test.pattern) |
| 1895 | continue |
| 1896 | } |
| 1897 | if imports := strings.Join(imports(pkg), " "); !strings.Contains(imports, test.wantImport) { |
| 1898 | t.Errorf("package %q: got \n%s, \nwant to have %s", test.pattern, imports, test.wantImport) |
| 1899 | } |
| 1900 | } |
| 1901 | } |
| 1902 | |
| 1903 | func TestCgoNoSyntax(t *testing.T) { |
| 1904 | testAllOrModulesParallel(t, testCgoNoSyntax) |
| 1905 | } |
| 1906 | func testCgoNoSyntax(t *testing.T, exporter packagestest.Exporter) { |
| 1907 | testenv.NeedsTool(t, "cgo") |
| 1908 | |
| 1909 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1910 | Name: "golang.org/fake", |
| 1911 | Files: map[string]interface{}{ |
| 1912 | "c/c.go": `package c; import "C"`, |
| 1913 | }, |
| 1914 | }}) |
| 1915 | |
| 1916 | // Explicitly enable cgo. |
| 1917 | exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1") |
| 1918 | |
| 1919 | modes := []packages.LoadMode{ |
| 1920 | packages.NeedTypes, |
| 1921 | packages.NeedName | packages.NeedTypes, |
| 1922 | packages.NeedName | packages.NeedTypes | packages.NeedImports, |
| 1923 | packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps, |
| 1924 | packages.NeedName | packages.NeedImports, |
| 1925 | } |
| 1926 | for _, mode := range modes { |
| 1927 | mode := mode |
| 1928 | t.Run(fmt.Sprint(mode), func(t *testing.T) { |
| 1929 | exported.Config.Mode = mode |
| 1930 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/c") |
| 1931 | if err != nil { |
| 1932 | t.Fatal(err) |
| 1933 | } |
| 1934 | if len(pkgs) != 1 { |
| 1935 | t.Fatalf("Expected 1 package, got %v", pkgs) |
| 1936 | } |
| 1937 | pkg := pkgs[0] |
| 1938 | if len(pkg.Errors) != 0 { |
| 1939 | t.Fatalf("Expected no errors in package, got %v", pkg.Errors) |
| 1940 | } |
| 1941 | }) |
| 1942 | } |
| 1943 | } |
| 1944 | |
| 1945 | func TestCgoBadPkgConfig(t *testing.T) { |
| 1946 | testAllOrModulesParallel(t, testCgoBadPkgConfig) |
| 1947 | } |
| 1948 | func testCgoBadPkgConfig(t *testing.T, exporter packagestest.Exporter) { |
| 1949 | skipIfShort(t, "builds and links a fake pkgconfig binary") |
| 1950 | testenv.NeedsTool(t, "cgo") |
| 1951 | |
| 1952 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 1953 | Name: "golang.org/fake", |
| 1954 | Files: map[string]interface{}{ |
| 1955 | "c/c.go": `package c |
| 1956 | |
| 1957 | // #cgo pkg-config: --cflags -- foo |
| 1958 | import "C"`, |
| 1959 | }, |
| 1960 | }}) |
| 1961 | |
| 1962 | dir := buildFakePkgconfig(t, exported.Config.Env) |
| 1963 | defer os.RemoveAll(dir) |
| 1964 | env := exported.Config.Env |
| 1965 | for i, v := range env { |
| 1966 | if strings.HasPrefix(v, "PATH=") { |
| 1967 | env[i] = "PATH=" + dir + string(os.PathListSeparator) + v[len("PATH="):] |
| 1968 | } |
| 1969 | } |
| 1970 | |
| 1971 | exported.Config.Env = append(exported.Config.Env, "CGO_ENABLED=1") |
| 1972 | |
| 1973 | exported.Config.Mode = packages.NeedName | packages.NeedCompiledGoFiles |
| 1974 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/c") |
| 1975 | if err != nil { |
| 1976 | t.Fatal(err) |
| 1977 | } |
| 1978 | if len(pkgs) != 1 { |
| 1979 | t.Fatalf("Expected 1 package, got %v", pkgs) |
| 1980 | } |
| 1981 | if pkgs[0].Name != "c" { |
| 1982 | t.Fatalf("Expected package to have name \"c\", got %q", pkgs[0].Name) |
| 1983 | } |
| 1984 | } |
| 1985 | |
| 1986 | func buildFakePkgconfig(t *testing.T, env []string) string { |
| 1987 | tmpdir, err := ioutil.TempDir("", "fakepkgconfig") |
| 1988 | if err != nil { |
| 1989 | t.Fatal(err) |
| 1990 | } |
| 1991 | err = ioutil.WriteFile(filepath.Join(tmpdir, "pkg-config.go"), []byte(` |
| 1992 | package main |
| 1993 | |
| 1994 | import "fmt" |
| 1995 | import "os" |
| 1996 | |
| 1997 | func main() { |
| 1998 | fmt.Fprintln(os.Stderr, "bad") |
| 1999 | os.Exit(2) |
| 2000 | } |
| 2001 | `), 0644) |
| 2002 | if err != nil { |
| 2003 | os.RemoveAll(tmpdir) |
| 2004 | t.Fatal(err) |
| 2005 | } |
| 2006 | cmd := exec.Command("go", "build", "-o", "pkg-config", "pkg-config.go") |
| 2007 | cmd.Dir = tmpdir |
| 2008 | cmd.Env = env |
| 2009 | |
| 2010 | if b, err := cmd.CombinedOutput(); err != nil { |
| 2011 | os.RemoveAll(tmpdir) |
| 2012 | fmt.Println(os.Environ()) |
| 2013 | t.Log(string(b)) |
| 2014 | t.Fatal(err) |
| 2015 | } |
| 2016 | return tmpdir |
| 2017 | } |
| 2018 | |
| 2019 | func TestIssue32814(t *testing.T) { testAllOrModulesParallel(t, testIssue32814) } |
| 2020 | func testIssue32814(t *testing.T, exporter packagestest.Exporter) { |
| 2021 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2022 | Name: "golang.org/fake", |
| 2023 | Files: map[string]interface{}{}}}) |
| 2024 | defer exported.Cleanup() |
| 2025 | |
| 2026 | exported.Config.Mode = packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes |
| 2027 | pkgs, err := packages.Load(exported.Config, "fmt") |
| 2028 | |
| 2029 | if err != nil { |
| 2030 | t.Fatal(err) |
| 2031 | } |
| 2032 | |
| 2033 | if len(pkgs) != 1 && pkgs[0].PkgPath != "fmt" { |
| 2034 | t.Fatalf("packages.Load: want [fmt], got %v", pkgs) |
| 2035 | } |
| 2036 | pkg := pkgs[0] |
| 2037 | if len(pkg.Errors) != 0 { |
| 2038 | t.Fatalf("Errors for fmt pkg: got %v, want none", pkg.Errors) |
| 2039 | } |
| 2040 | if !pkg.Types.Complete() { |
| 2041 | t.Fatalf("Types.Complete() for fmt pkg: got %v, want true", pkgs[0].Types.Complete()) |
| 2042 | |
| 2043 | } |
| 2044 | } |
| 2045 | |
| 2046 | func TestLoadTypesInfoWithoutNeedDeps(t *testing.T) { |
| 2047 | testAllOrModulesParallel(t, testLoadTypesInfoWithoutNeedDeps) |
| 2048 | } |
| 2049 | func testLoadTypesInfoWithoutNeedDeps(t *testing.T, exporter packagestest.Exporter) { |
| 2050 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2051 | Name: "golang.org/fake", |
| 2052 | Files: map[string]interface{}{ |
| 2053 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 2054 | "b/b.go": `package b`, |
| 2055 | }}}) |
| 2056 | defer exported.Cleanup() |
| 2057 | |
| 2058 | exported.Config.Mode = packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports |
| 2059 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2060 | if err != nil { |
| 2061 | t.Fatal(err) |
| 2062 | } |
| 2063 | pkg := pkgs[0] |
| 2064 | if pkg.IllTyped { |
| 2065 | t.Fatal("Loaded package is ill typed") |
| 2066 | } |
| 2067 | const expectedImport = "golang.org/fake/b" |
| 2068 | if _, ok := pkg.Imports[expectedImport]; !ok || len(pkg.Imports) != 1 { |
| 2069 | t.Fatalf("Imports of loaded package: want [%s], got %v", expectedImport, pkg.Imports) |
| 2070 | } |
| 2071 | } |
| 2072 | |
| 2073 | func TestLoadWithNeedDeps(t *testing.T) { |
| 2074 | testAllOrModulesParallel(t, testLoadWithNeedDeps) |
| 2075 | } |
| 2076 | func testLoadWithNeedDeps(t *testing.T, exporter packagestest.Exporter) { |
| 2077 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2078 | Name: "golang.org/fake", |
| 2079 | Files: map[string]interface{}{ |
| 2080 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 2081 | "b/b.go": `package b; import _ "golang.org/fake/c"`, |
| 2082 | "c/c.go": `package c`, |
| 2083 | }}}) |
| 2084 | defer exported.Cleanup() |
| 2085 | |
| 2086 | exported.Config.Mode = packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports | packages.NeedDeps |
| 2087 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2088 | if err != nil { |
| 2089 | t.Fatal(err) |
| 2090 | } |
| 2091 | if len(pkgs) != 1 { |
| 2092 | t.Fatalf("Expected 1 package, got %d", len(pkgs)) |
| 2093 | } |
| 2094 | |
| 2095 | pkgA := pkgs[0] |
| 2096 | if pkgA.IllTyped { |
| 2097 | t.Fatal("Loaded package is ill typed") |
| 2098 | } |
| 2099 | |
| 2100 | pkgB := pkgA.Imports["golang.org/fake/b"] |
| 2101 | if pkgB == nil || len(pkgA.Imports) != 1 { |
| 2102 | t.Fatalf("Imports of loaded package 'a' are invalid: %v", pkgA.Imports) |
| 2103 | } |
| 2104 | if pkgB.Types == nil || !pkgB.Types.Complete() || pkgB.TypesInfo == nil { |
| 2105 | t.Fatalf("Types of package 'b' are nil or incomplete: %v, %v", pkgB.Types, pkgB.TypesInfo) |
| 2106 | } |
| 2107 | |
| 2108 | pkgC := pkgB.Imports["golang.org/fake/c"] |
| 2109 | if pkgC == nil || len(pkgB.Imports) != 1 { |
| 2110 | t.Fatalf("Imports of loaded package 'c' are invalid: %v", pkgB.Imports) |
| 2111 | } |
| 2112 | if pkgC.Types == nil || !pkgC.Types.Complete() || pkgC.TypesInfo == nil { |
| 2113 | t.Fatalf("Types of package 'b' are nil or incomplete: %v, %v", pkgC.Types, pkgC.TypesInfo) |
| 2114 | } |
| 2115 | } |
| 2116 | |
| 2117 | func TestImpliedLoadMode(t *testing.T) { |
| 2118 | testAllOrModulesParallel(t, testImpliedLoadMode) |
| 2119 | } |
| 2120 | func testImpliedLoadMode(t *testing.T, exporter packagestest.Exporter) { |
| 2121 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2122 | Name: "golang.org/fake", |
| 2123 | Files: map[string]interface{}{ |
| 2124 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 2125 | "b/b.go": `package b`, |
| 2126 | }}}) |
| 2127 | defer exported.Cleanup() |
| 2128 | |
| 2129 | exported.Config.Mode = packages.NeedTypes | packages.NeedTypesInfo |
| 2130 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2131 | if err != nil { |
| 2132 | t.Fatal(err) |
| 2133 | } |
| 2134 | if len(pkgs) != 1 { |
| 2135 | t.Fatalf("Expected 1 package, got %d", len(pkgs)) |
| 2136 | } |
| 2137 | |
| 2138 | pkg := pkgs[0] |
| 2139 | if pkg.IllTyped { |
| 2140 | t.Fatalf("Loaded package is ill typed: %v", pkg.Errors) |
| 2141 | } |
| 2142 | |
| 2143 | // Check that packages.NeedTypesInfo worked well. |
| 2144 | if !pkg.Types.Complete() { |
| 2145 | t.Fatalf("Loaded package types are incomplete") |
| 2146 | } |
| 2147 | |
| 2148 | // Check that implied packages.NeedImports by packages.NeedTypesInfo |
| 2149 | // didn't add Imports. |
| 2150 | if len(pkg.Imports) != 0 { |
| 2151 | t.Fatalf("Package imports weren't requested but were returned: %v", pkg.Imports) |
| 2152 | } |
| 2153 | } |
| 2154 | |
| 2155 | func TestIssue35331(t *testing.T) { |
| 2156 | testAllOrModulesParallel(t, testIssue35331) |
| 2157 | } |
| 2158 | func testIssue35331(t *testing.T, exporter packagestest.Exporter) { |
| 2159 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2160 | Name: "golang.org/fake", |
| 2161 | }}) |
| 2162 | defer exported.Cleanup() |
| 2163 | |
| 2164 | exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | |
| 2165 | packages.NeedImports | packages.NeedDeps | packages.NeedSyntax |
| 2166 | exported.Config.Tests = false |
| 2167 | pkgs, err := packages.Load(exported.Config, "strconv") |
| 2168 | if err != nil { |
| 2169 | t.Fatal(err) |
| 2170 | } |
| 2171 | if len(pkgs) != 1 { |
| 2172 | t.Fatalf("Expected 1 package, got %v", pkgs) |
| 2173 | } |
| 2174 | packages.Visit(pkgs, func(pkg *packages.Package) bool { |
| 2175 | if len(pkg.Errors) > 0 { |
| 2176 | t.Errorf("Expected no errors in package %q, got %v", pkg.ID, pkg.Errors) |
| 2177 | } |
| 2178 | if len(pkg.Syntax) == 0 && pkg.ID != "unsafe" { |
| 2179 | t.Errorf("Expected syntax on package %q, got none.", pkg.ID) |
| 2180 | } |
| 2181 | return true |
| 2182 | }, nil) |
| 2183 | } |
| 2184 | |
| 2185 | func TestMultiplePackageVersionsIssue36188(t *testing.T) { |
| 2186 | testAllOrModulesParallel(t, testMultiplePackageVersionsIssue36188) |
| 2187 | } |
| 2188 | |
| 2189 | func testMultiplePackageVersionsIssue36188(t *testing.T, exporter packagestest.Exporter) { |
| 2190 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2191 | Name: "golang.org/fake", |
| 2192 | Files: map[string]interface{}{ |
| 2193 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 2194 | "b/b.go": `package main`, |
| 2195 | }}}) |
| 2196 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/b") |
| 2197 | if err != nil { |
| 2198 | t.Fatal(err) |
| 2199 | } |
| 2200 | sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].ID < pkgs[j].ID }) |
| 2201 | if len(pkgs) != 2 { |
| 2202 | t.Fatalf("expected two packages, got %v", pkgs) |
| 2203 | } |
| 2204 | if pkgs[0].ID != "golang.org/fake/a" && pkgs[1].ID != "golang.org/fake/b" { |
| 2205 | t.Fatalf(`expected (sorted) IDs "golang.org/fake/a" and "golang.org/fake/b", got %q and %q`, |
| 2206 | pkgs[0].ID, pkgs[1].ID) |
| 2207 | } |
| 2208 | if pkgs[0].Errors == nil { |
| 2209 | t.Errorf(`expected error on package "golang.org/fake/a", got none`) |
| 2210 | } |
| 2211 | if pkgs[1].Errors != nil { |
| 2212 | t.Errorf(`expected no errors on package "golang.org/fake/b", got %v`, pkgs[1].Errors) |
| 2213 | } |
| 2214 | defer exported.Cleanup() |
| 2215 | } |
| 2216 | |
| 2217 | func TestLoadModeStrings(t *testing.T) { |
| 2218 | testcases := []struct { |
| 2219 | mode packages.LoadMode |
| 2220 | expected string |
| 2221 | }{ |
| 2222 | { |
| 2223 | packages.LoadMode(0), |
| 2224 | "LoadMode(0)", |
| 2225 | }, |
| 2226 | { |
| 2227 | packages.NeedName, |
| 2228 | "LoadMode(NeedName)", |
| 2229 | }, |
| 2230 | { |
| 2231 | packages.NeedFiles, |
| 2232 | "LoadMode(NeedFiles)", |
| 2233 | }, |
| 2234 | { |
| 2235 | packages.NeedCompiledGoFiles, |
| 2236 | "LoadMode(NeedCompiledGoFiles)", |
| 2237 | }, |
| 2238 | { |
| 2239 | packages.NeedImports, |
| 2240 | "LoadMode(NeedImports)", |
| 2241 | }, |
| 2242 | { |
| 2243 | packages.NeedDeps, |
| 2244 | "LoadMode(NeedDeps)", |
| 2245 | }, |
| 2246 | { |
| 2247 | packages.NeedExportFile, |
| 2248 | "LoadMode(NeedExportFile)", |
| 2249 | }, |
| 2250 | { |
| 2251 | packages.NeedTypes, |
| 2252 | "LoadMode(NeedTypes)", |
| 2253 | }, |
| 2254 | { |
| 2255 | packages.NeedSyntax, |
| 2256 | "LoadMode(NeedSyntax)", |
| 2257 | }, |
| 2258 | { |
| 2259 | packages.NeedTypesInfo, |
| 2260 | "LoadMode(NeedTypesInfo)", |
| 2261 | }, |
| 2262 | { |
| 2263 | packages.NeedTypesSizes, |
| 2264 | "LoadMode(NeedTypesSizes)", |
| 2265 | }, |
| 2266 | { |
| 2267 | packages.NeedName | packages.NeedExportFile, |
| 2268 | "LoadMode(NeedName|NeedExportFile)", |
| 2269 | }, |
| 2270 | { |
| 2271 | packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps | packages.NeedExportFile | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedTypesSizes, |
| 2272 | "LoadMode(NeedName|NeedFiles|NeedCompiledGoFiles|NeedImports|NeedDeps|NeedExportFile|NeedTypes|NeedSyntax|NeedTypesInfo|NeedTypesSizes)", |
| 2273 | }, |
| 2274 | { |
| 2275 | packages.NeedName | 8192, |
| 2276 | "LoadMode(NeedName|Unknown)", |
| 2277 | }, |
| 2278 | { |
| 2279 | 4096, |
| 2280 | "LoadMode(Unknown)", |
| 2281 | }, |
| 2282 | } |
| 2283 | |
| 2284 | for tcInd, tc := range testcases { |
| 2285 | t.Run(fmt.Sprintf("test-%d", tcInd), func(t *testing.T) { |
| 2286 | actual := tc.mode.String() |
| 2287 | if tc.expected != actual { |
| 2288 | t.Errorf("want %#v, got %#v", tc.expected, actual) |
| 2289 | } |
| 2290 | }) |
| 2291 | } |
| 2292 | } |
| 2293 | |
| 2294 | func TestCycleImportStack(t *testing.T) { |
| 2295 | testAllOrModulesParallel(t, testCycleImportStack) |
| 2296 | } |
| 2297 | func testCycleImportStack(t *testing.T, exporter packagestest.Exporter) { |
| 2298 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2299 | Name: "golang.org/fake", |
| 2300 | Files: map[string]interface{}{ |
| 2301 | "a/a.go": `package a; import _ "golang.org/fake/b"`, |
| 2302 | "b/b.go": `package b; import _ "golang.org/fake/a"`, |
| 2303 | }}}) |
| 2304 | defer exported.Cleanup() |
| 2305 | |
| 2306 | exported.Config.Mode = packages.NeedName | packages.NeedImports |
| 2307 | pkgs, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2308 | if err != nil { |
| 2309 | t.Fatal(err) |
| 2310 | } |
| 2311 | if len(pkgs) != 1 { |
| 2312 | t.Fatalf("Expected 1 package, got %v", pkgs) |
| 2313 | } |
| 2314 | pkg := pkgs[0] |
| 2315 | if len(pkg.Errors) != 1 { |
| 2316 | t.Fatalf("Expected one error in package, got %v", pkg.Errors) |
| 2317 | } |
| 2318 | expected := "import cycle not allowed: import stack: [golang.org/fake/a golang.org/fake/b golang.org/fake/a]" |
| 2319 | if pkg.Errors[0].Msg != expected { |
| 2320 | t.Fatalf("Expected error %q, got %q", expected, pkg.Errors[0].Msg) |
| 2321 | } |
| 2322 | } |
| 2323 | |
| 2324 | func TestForTestField(t *testing.T) { |
| 2325 | testAllOrModulesParallel(t, testForTestField) |
| 2326 | } |
| 2327 | func testForTestField(t *testing.T, exporter packagestest.Exporter) { |
| 2328 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2329 | Name: "golang.org/fake", |
| 2330 | Files: map[string]interface{}{ |
| 2331 | "a/a.go": `package a; func hello() {};`, |
| 2332 | "a/a_test.go": `package a; import "testing"; func TestA1(t *testing.T) {};`, |
| 2333 | "a/x_test.go": `package a_test; import "testing"; func TestA2(t *testing.T) {};`, |
| 2334 | }}}) |
| 2335 | defer exported.Cleanup() |
| 2336 | |
| 2337 | // Add overlays to make sure they don't affect anything. |
| 2338 | exported.Config.Overlay = map[string][]byte{ |
| 2339 | "a/a_test.go": []byte(`package a; import "testing"; func TestA1(t *testing.T) { hello(); };`), |
| 2340 | "a/x_test.go": []byte(`package a_test; import "testing"; func TestA2(t *testing.T) { hello(); };`), |
| 2341 | } |
| 2342 | exported.Config.Tests = true |
| 2343 | exported.Config.Mode = packages.NeedName | packages.NeedImports |
| 2344 | forTest := "golang.org/fake/a" |
| 2345 | pkgs, err := packages.Load(exported.Config, forTest) |
| 2346 | if err != nil { |
| 2347 | t.Fatal(err) |
| 2348 | } |
| 2349 | if len(pkgs) != 4 { |
| 2350 | t.Errorf("expected 4 packages, got %v", len(pkgs)) |
| 2351 | } |
| 2352 | for _, pkg := range pkgs { |
| 2353 | var hasTestFile bool |
| 2354 | for _, f := range pkg.CompiledGoFiles { |
| 2355 | if strings.Contains(f, "a_test.go") || strings.Contains(f, "x_test.go") { |
| 2356 | hasTestFile = true |
| 2357 | break |
| 2358 | } |
| 2359 | } |
| 2360 | if !hasTestFile { |
| 2361 | continue |
| 2362 | } |
| 2363 | got := packagesinternal.GetForTest(pkg) |
| 2364 | if got != forTest { |
| 2365 | t.Errorf("expected %q, got %q", forTest, got) |
| 2366 | } |
| 2367 | } |
| 2368 | } |
| 2369 | |
| 2370 | func TestIssue37529(t *testing.T) { |
| 2371 | testAllOrModulesParallel(t, testIssue37529) |
| 2372 | } |
| 2373 | func testIssue37529(t *testing.T, exporter packagestest.Exporter) { |
| 2374 | // Tests #37529. When automatic vendoring is triggered, and we try to determine |
| 2375 | // the module root dir for a new overlay package, we previously would do a go list -m all, |
| 2376 | // which is incompatible with automatic vendoring. |
| 2377 | |
| 2378 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2379 | Name: "golang.org/fake", |
| 2380 | Files: map[string]interface{}{ |
| 2381 | "c/c2.go": `package c`, |
| 2382 | "a/a.go": `package a; import "b.com/b"; const A = b.B`, |
| 2383 | "vendor/b.com/b/b.go": `package b; const B = 4`, |
| 2384 | }}}) |
| 2385 | rootDir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) |
| 2386 | exported.Config.Overlay = map[string][]byte{ |
| 2387 | filepath.Join(rootDir, "c/c.go"): []byte(`package c; import "golang.org/fake/a"; const C = a.A`), |
| 2388 | } |
| 2389 | exported.Config.Env = append(exported.Config.Env, "GOFLAGS=-mod=vendor") |
| 2390 | exported.Config.Mode = packages.LoadAllSyntax |
| 2391 | |
| 2392 | defer exported.Cleanup() |
| 2393 | |
| 2394 | initial, err := packages.Load(exported.Config, "golang.org/fake/c") |
| 2395 | if err != nil { |
| 2396 | t.Fatal(err) |
| 2397 | } |
| 2398 | |
| 2399 | // Check value of a.A. |
| 2400 | a := initial[0] |
| 2401 | aA := constant(a, "C") |
| 2402 | if aA == nil { |
| 2403 | t.Fatalf("a.A: got nil") |
| 2404 | } |
| 2405 | got := aA.Val().String() |
| 2406 | if got != "4" { |
| 2407 | t.Errorf("a.A: got %s, want %s", got, "4") |
| 2408 | } |
| 2409 | } |
| 2410 | |
| 2411 | func TestIssue37098(t *testing.T) { testAllOrModulesParallel(t, testIssue37098) } |
| 2412 | func testIssue37098(t *testing.T, exporter packagestest.Exporter) { |
| 2413 | // packages.Load should only return Go sources in |
| 2414 | // (*Package).CompiledGoFiles. This tests #37098, where using SWIG to |
| 2415 | // causes C++ sources to be inadvertently included in |
| 2416 | // (*Package).CompiledGoFiles. |
| 2417 | |
| 2418 | // This is fixed in Go 1.17, but not earlier. |
| 2419 | testenv.NeedsGo1Point(t, 17) |
| 2420 | |
| 2421 | if _, err := exec.LookPath("swig"); err != nil { |
| 2422 | t.Skip("skipping test: swig not available") |
| 2423 | } |
| 2424 | if _, err := exec.LookPath("g++"); err != nil { |
| 2425 | t.Skip("skipping test: g++ not available") |
| 2426 | } |
| 2427 | |
| 2428 | // Create a fake package with an empty Go source, and a SWIG interface |
| 2429 | // file. |
| 2430 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2431 | Name: "golang.org/fake", |
| 2432 | Files: map[string]interface{}{ |
| 2433 | // The "package" statement must be included for SWIG sources to |
| 2434 | // be generated. |
| 2435 | "a/a.go": "package a", |
| 2436 | "a/a.swigcxx": "", |
| 2437 | }}}) |
| 2438 | defer exported.Cleanup() |
| 2439 | |
| 2440 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2441 | if err != nil { |
| 2442 | t.Fatalf("failed to load the package: %v", err) |
| 2443 | } |
| 2444 | // Try and parse each of the files |
| 2445 | for _, pkg := range initial { |
| 2446 | for _, file := range pkg.CompiledGoFiles { |
| 2447 | |
| 2448 | // Validate that each file can be parsed as a Go source. |
| 2449 | fset := token.NewFileSet() |
| 2450 | _, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly) |
| 2451 | if err != nil { |
| 2452 | t.Errorf("Failed to parse file '%s' as a Go source: %v", file, err) |
| 2453 | |
| 2454 | contents, err := ioutil.ReadFile(file) |
| 2455 | if err != nil { |
| 2456 | t.Fatalf("Failed to read the un-parsable file '%s': %v", file, err) |
| 2457 | } |
| 2458 | |
| 2459 | // Print out some of the un-parsable file to aid in debugging. |
| 2460 | n := len(contents) |
| 2461 | |
| 2462 | // Don't print the whole file if it is too large. |
| 2463 | const maxBytes = 1000 |
| 2464 | if n > maxBytes { |
| 2465 | n = maxBytes |
| 2466 | } |
| 2467 | |
| 2468 | t.Logf("First %d bytes of un-parsable file: %s", n, contents[:n]) |
| 2469 | } |
| 2470 | } |
| 2471 | } |
| 2472 | } |
| 2473 | |
| 2474 | // TestIssue56632 checks that CompiledGoFiles does not contain non-go files regardless of |
| 2475 | // whether the NeedFiles mode bit is set. |
| 2476 | func TestIssue56632(t *testing.T) { |
| 2477 | t.Parallel() |
| 2478 | testenv.NeedsGoBuild(t) |
| 2479 | testenv.NeedsTool(t, "cgo") |
| 2480 | |
| 2481 | exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{{ |
| 2482 | Name: "golang.org/issue56632", |
| 2483 | Files: map[string]interface{}{ |
| 2484 | "a/a.go": `package a`, |
| 2485 | "a/a_cgo.go": `package a |
| 2486 | |
| 2487 | import "C"`, |
| 2488 | "a/a.s": ``, |
| 2489 | "a/a.c": ``, |
| 2490 | }}}) |
| 2491 | defer exported.Cleanup() |
| 2492 | |
| 2493 | modes := []packages.LoadMode{packages.NeedCompiledGoFiles, packages.NeedCompiledGoFiles | packages.NeedFiles, packages.NeedImports | packages.NeedCompiledGoFiles, packages.NeedImports | packages.NeedFiles | packages.NeedCompiledGoFiles} |
| 2494 | for _, mode := range modes { |
| 2495 | exported.Config.Mode = mode |
| 2496 | |
| 2497 | initial, err := packages.Load(exported.Config, "golang.org/issue56632/a") |
| 2498 | if err != nil { |
| 2499 | t.Fatalf("failed to load package: %v", err) |
| 2500 | } |
| 2501 | |
| 2502 | if len(initial) != 1 { |
| 2503 | t.Errorf("expected 3 packages, got %d", len(initial)) |
| 2504 | } |
| 2505 | |
| 2506 | p := initial[0] |
| 2507 | |
| 2508 | if len(p.Errors) != 0 { |
| 2509 | t.Errorf("expected no errors, got %v", p.Errors) |
| 2510 | } |
| 2511 | |
| 2512 | for _, f := range p.CompiledGoFiles { |
| 2513 | if strings.HasSuffix(f, ".s") || strings.HasSuffix(f, ".c") { |
| 2514 | t.Errorf("expected no non-Go CompiledGoFiles, got file %q in CompiledGoFiles", f) |
| 2515 | } |
| 2516 | } |
| 2517 | } |
| 2518 | } |
| 2519 | |
| 2520 | // TestInvalidFilesInXTest checks the fix for golang/go#37971 in Go 1.15. |
| 2521 | func TestInvalidFilesInXTest(t *testing.T) { testAllOrModulesParallel(t, testInvalidFilesInXTest) } |
| 2522 | func testInvalidFilesInXTest(t *testing.T, exporter packagestest.Exporter) { |
| 2523 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
| 2524 | { |
| 2525 | Name: "golang.org/fake", |
| 2526 | Files: map[string]interface{}{ |
| 2527 | "d/d.go": `package d; import "net/http"; const d = http.MethodGet; func Get() string { return d; }`, |
| 2528 | "d/d2.go": ``, // invalid file |
| 2529 | "d/d_test.go": `package d_test; import "testing"; import "golang.org/fake/d"; func TestD(t *testing.T) { d.Get(); }`, |
| 2530 | }, |
| 2531 | }, |
| 2532 | }) |
| 2533 | defer exported.Cleanup() |
| 2534 | |
| 2535 | exported.Config.Mode = packages.NeedName | packages.NeedFiles |
| 2536 | exported.Config.Tests = true |
| 2537 | |
| 2538 | initial, err := packages.Load(exported.Config, "golang.org/fake/d") |
| 2539 | if err != nil { |
| 2540 | t.Fatal(err) |
| 2541 | } |
| 2542 | if len(initial) != 3 { |
| 2543 | t.Errorf("expected 3 packages, got %d", len(initial)) |
| 2544 | } |
| 2545 | } |
| 2546 | |
| 2547 | func TestTypecheckCgo(t *testing.T) { testAllOrModulesParallel(t, testTypecheckCgo) } |
| 2548 | func testTypecheckCgo(t *testing.T, exporter packagestest.Exporter) { |
| 2549 | testenv.NeedsTool(t, "cgo") |
| 2550 | |
| 2551 | const cgo = `package cgo |
| 2552 | import "C" |
| 2553 | |
| 2554 | func Example() { |
| 2555 | C.CString("hi") |
| 2556 | } |
| 2557 | ` |
| 2558 | exported := packagestest.Export(t, exporter, []packagestest.Module{ |
| 2559 | { |
| 2560 | Name: "golang.org/fake", |
| 2561 | Files: map[string]interface{}{ |
| 2562 | "cgo/cgo.go": cgo, |
| 2563 | }, |
| 2564 | }, |
| 2565 | }) |
| 2566 | defer exported.Cleanup() |
| 2567 | |
| 2568 | exported.Config.Mode = packages.NeedFiles | packages.NeedCompiledGoFiles | |
| 2569 | packages.NeedSyntax | packages.NeedDeps | packages.NeedTypes | |
| 2570 | packages.LoadMode(packagesinternal.TypecheckCgo) |
| 2571 | |
| 2572 | initial, err := packages.Load(exported.Config, "golang.org/fake/cgo") |
| 2573 | if err != nil { |
| 2574 | t.Fatal(err) |
| 2575 | } |
| 2576 | pkg := initial[0] |
| 2577 | if len(pkg.Errors) != 0 { |
| 2578 | t.Fatalf("package has errors: %v", pkg.Errors) |
| 2579 | } |
| 2580 | |
| 2581 | expos := pkg.Types.Scope().Lookup("Example").Pos() |
| 2582 | fname := pkg.Fset.File(expos).Name() |
| 2583 | if !strings.HasSuffix(fname, "cgo.go") { |
| 2584 | t.Errorf("position for cgo package was loaded from %v, wanted cgo.go", fname) |
| 2585 | } |
| 2586 | } |
| 2587 | |
| 2588 | func TestModule(t *testing.T) { |
| 2589 | testAllOrModulesParallel(t, testModule) |
| 2590 | } |
| 2591 | func testModule(t *testing.T, exporter packagestest.Exporter) { |
| 2592 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2593 | Name: "golang.org/fake", |
| 2594 | Files: map[string]interface{}{"a/a.go": `package a`}}}) |
| 2595 | exported.Config.Mode = packages.NeedModule |
| 2596 | rootDir := filepath.Dir(filepath.Dir(exported.File("golang.org/fake", "a/a.go"))) |
| 2597 | |
| 2598 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2599 | if err != nil { |
| 2600 | t.Fatal(err) |
| 2601 | } |
| 2602 | |
| 2603 | if len(initial) != 1 { |
| 2604 | t.Fatal("want exactly one package, got ", initial) |
| 2605 | } |
| 2606 | a := initial[0] |
| 2607 | switch exported.Exporter.Name() { |
| 2608 | case "GOPATH": |
| 2609 | if a.Module != nil { |
| 2610 | t.Fatal("package.Module: want nil, got ", a.Module) |
| 2611 | } |
| 2612 | case "Modules": |
| 2613 | // Make sure Modules field is set, and spot check a few of its fields. |
| 2614 | if a.Module == nil { |
| 2615 | t.Fatal("package.Module: want non-nil, got nil") |
| 2616 | } |
| 2617 | if a.Module.Path != "golang.org/fake" { |
| 2618 | t.Fatalf("package.Modile.Path: want \"golang.org/fake\", got %q", a.Module.Path) |
| 2619 | } |
| 2620 | if a.Module.GoMod != filepath.Join(rootDir, "go.mod") { |
| 2621 | t.Fatalf("package.Module.GoMod: want %q, got %q", filepath.Join(rootDir, "go.mod"), a.Module.GoMod) |
| 2622 | } |
| 2623 | default: |
| 2624 | t.Fatalf("Expected exporter to be GOPATH or Modules, got %v", exported.Exporter.Name()) |
| 2625 | } |
| 2626 | } |
| 2627 | |
| 2628 | func TestExternal_NotHandled(t *testing.T) { |
| 2629 | testAllOrModulesParallel(t, testExternal_NotHandled) |
| 2630 | } |
| 2631 | func testExternal_NotHandled(t *testing.T, exporter packagestest.Exporter) { |
| 2632 | skipIfShort(t, "builds and links fake driver binaries") |
| 2633 | testenv.NeedsGoBuild(t) |
| 2634 | |
| 2635 | tempdir, err := ioutil.TempDir("", "testexternal") |
| 2636 | if err != nil { |
| 2637 | t.Fatal(err) |
| 2638 | } |
| 2639 | defer os.RemoveAll(tempdir) |
| 2640 | |
| 2641 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2642 | Name: "golang.org/fake", |
| 2643 | Files: map[string]interface{}{ |
| 2644 | "a/a.go": `package a`, |
| 2645 | "empty_driver/main.go": `package main |
| 2646 | |
| 2647 | import ( |
| 2648 | "fmt" |
| 2649 | "io/ioutil" |
| 2650 | "os" |
| 2651 | ) |
| 2652 | |
| 2653 | func main() { |
| 2654 | ioutil.ReadAll(os.Stdin) |
| 2655 | fmt.Println("{}") |
| 2656 | } |
| 2657 | `, |
| 2658 | "nothandled_driver/main.go": `package main |
| 2659 | |
| 2660 | import ( |
| 2661 | "fmt" |
| 2662 | "io/ioutil" |
| 2663 | "os" |
| 2664 | ) |
| 2665 | |
| 2666 | func main() { |
| 2667 | ioutil.ReadAll(os.Stdin) |
| 2668 | fmt.Println("{\"NotHandled\": true}") |
| 2669 | } |
| 2670 | `, |
| 2671 | }}}) |
| 2672 | baseEnv := exported.Config.Env |
| 2673 | |
| 2674 | // As a control, create a fake driver that always returns an empty response. |
| 2675 | emptyDriverPath := filepath.Join(tempdir, "empty_driver.exe") // Add .exe because Windows expects it. |
| 2676 | cmd := exec.Command("go", "build", "-o", emptyDriverPath, "golang.org/fake/empty_driver") |
| 2677 | cmd.Env = baseEnv |
| 2678 | cmd.Dir = exported.Config.Dir |
| 2679 | if b, err := cmd.CombinedOutput(); err != nil { |
| 2680 | t.Log(string(b)) |
| 2681 | t.Fatal(err) |
| 2682 | } |
| 2683 | |
| 2684 | exported.Config.Env = append(append([]string{}, baseEnv...), "GOPACKAGESDRIVER="+emptyDriverPath) |
| 2685 | initial, err := packages.Load(exported.Config, "golang.org/fake/a") |
| 2686 | if err != nil { |
| 2687 | t.Fatal(err) |
| 2688 | } |
| 2689 | |
| 2690 | if len(initial) != 0 { |
| 2691 | t.Errorf("package.Load with empty driver: want [], got %v", initial) |
| 2692 | } |
| 2693 | |
| 2694 | // Create a fake driver that always returns a NotHandled response. |
| 2695 | notHandledDriverPath := filepath.Join(tempdir, "nothandled_driver.exe") |
| 2696 | cmd = exec.Command("go", "build", "-o", notHandledDriverPath, "golang.org/fake/nothandled_driver") |
| 2697 | cmd.Env = baseEnv |
| 2698 | cmd.Dir = exported.Config.Dir |
| 2699 | if b, err := cmd.CombinedOutput(); err != nil { |
| 2700 | t.Log(string(b)) |
| 2701 | t.Fatal(err) |
| 2702 | } |
| 2703 | |
| 2704 | exported.Config.Env = append(append([]string{}, baseEnv...), "GOPACKAGESDRIVER="+notHandledDriverPath) |
| 2705 | initial, err = packages.Load(exported.Config, "golang.org/fake/a") |
| 2706 | if err != nil { |
| 2707 | t.Fatal(err) |
| 2708 | } |
| 2709 | |
| 2710 | if len(initial) != 1 || initial[0].PkgPath != "golang.org/fake/a" { |
| 2711 | t.Errorf("package.Load: want [golang.org/fake/a], got %v", initial) |
| 2712 | } |
| 2713 | } |
| 2714 | |
| 2715 | func TestInvalidPackageName(t *testing.T) { |
| 2716 | testAllOrModulesParallel(t, testInvalidPackageName) |
| 2717 | } |
| 2718 | |
| 2719 | func testInvalidPackageName(t *testing.T, exporter packagestest.Exporter) { |
| 2720 | exported := packagestest.Export(t, exporter, []packagestest.Module{{ |
| 2721 | Name: "golang.org/fake", |
| 2722 | Files: map[string]interface{}{ |
| 2723 | "main.go": `package default |
| 2724 | |
| 2725 | func main() { |
| 2726 | } |
| 2727 | `, |
| 2728 | }, |
| 2729 | }}) |
| 2730 | defer exported.Cleanup() |
| 2731 | |
| 2732 | initial, err := packages.Load(exported.Config, "golang.org/fake") |
| 2733 | if err != nil { |
| 2734 | t.Fatal(err) |
| 2735 | } |
| 2736 | pkg := initial[0] |
| 2737 | if len(pkg.CompiledGoFiles) != 1 { |
| 2738 | t.Fatalf("expected 1 Go file in package %s, got %v", pkg.ID, len(pkg.CompiledGoFiles)) |
| 2739 | } |
| 2740 | } |
| 2741 | |
| 2742 | func TestEmptyEnvironment(t *testing.T) { |
| 2743 | t.Parallel() |
| 2744 | |
| 2745 | cfg := &packages.Config{ |
| 2746 | Env: []string{"FOO=BAR"}, |
| 2747 | } |
| 2748 | _, err := packages.Load(cfg, "fmt") |
| 2749 | if err == nil { |
| 2750 | t.Fatal("Load with explicitly empty environment should fail") |
| 2751 | } |
| 2752 | } |
| 2753 | |
| 2754 | func TestPackageLoadSingleFile(t *testing.T) { |
| 2755 | tmp, err := ioutil.TempDir("", "a") |
| 2756 | if err != nil { |
| 2757 | t.Fatal(err) |
| 2758 | } |
| 2759 | defer os.RemoveAll(tmp) |
| 2760 | |
| 2761 | filename := filepath.Join(tmp, "a.go") |
| 2762 | |
| 2763 | if err := ioutil.WriteFile(filename, []byte(`package main; func main() { println("hello world") }`), 0775); err != nil { |
| 2764 | t.Fatal(err) |
| 2765 | } |
| 2766 | |
| 2767 | pkgs, err := packages.Load(&packages.Config{Mode: packages.LoadSyntax, Dir: tmp}, "file="+filename) |
| 2768 | if err != nil { |
| 2769 | t.Fatalf("could not load package: %v", err) |
| 2770 | } |
| 2771 | if len(pkgs) != 1 { |
| 2772 | t.Fatalf("expected one package to be loaded, got %d", len(pkgs)) |
| 2773 | } |
| 2774 | if len(pkgs[0].CompiledGoFiles) != 1 || pkgs[0].CompiledGoFiles[0] != filename { |
| 2775 | t.Fatalf("expected one compiled go file (%q), got %v", filename, pkgs[0].CompiledGoFiles) |
| 2776 | } |
| 2777 | } |
| 2778 | |
| 2779 | func errorMessages(errors []packages.Error) []string { |
| 2780 | var msgs []string |
| 2781 | for _, err := range errors { |
| 2782 | msgs = append(msgs, err.Msg) |
| 2783 | } |
| 2784 | return msgs |
| 2785 | } |
| 2786 | |
| 2787 | func srcs(p *packages.Package) []string { |
| 2788 | return cleanPaths(append(append(p.GoFiles[:len(p.GoFiles):len(p.GoFiles)], p.OtherFiles...), p.EmbedFiles...)) |
| 2789 | } |
| 2790 | |
| 2791 | // cleanPaths attempts to reduce path names to stable forms |
| 2792 | func cleanPaths(paths []string) []string { |
| 2793 | result := make([]string, len(paths)) |
| 2794 | for i, src := range paths { |
| 2795 | // If the source file doesn't have an extension like .go or .s, |
| 2796 | // it comes from GOCACHE. The names there aren't predictable. |
| 2797 | name := filepath.Base(src) |
| 2798 | if !strings.Contains(name, ".") { |
| 2799 | result[i] = fmt.Sprintf("%d.go", i) // make cache names predictable |
| 2800 | } else { |
| 2801 | result[i] = name |
| 2802 | } |
| 2803 | } |
| 2804 | return result |
| 2805 | } |
| 2806 | |
| 2807 | // importGraph returns the import graph as a user-friendly string, |
| 2808 | // and a map containing all packages keyed by ID. |
| 2809 | func importGraph(initial []*packages.Package) (string, map[string]*packages.Package) { |
| 2810 | out := new(bytes.Buffer) |
| 2811 | |
| 2812 | initialSet := make(map[*packages.Package]bool) |
| 2813 | for _, p := range initial { |
| 2814 | initialSet[p] = true |
| 2815 | } |
| 2816 | |
| 2817 | // We can't use Visit because we need to prune |
| 2818 | // the traversal of specific edges, not just nodes. |
| 2819 | var nodes, edges []string |
| 2820 | res := make(map[string]*packages.Package) |
| 2821 | seen := make(map[*packages.Package]bool) |
| 2822 | var visit func(p *packages.Package) |
| 2823 | visit = func(p *packages.Package) { |
| 2824 | if !seen[p] { |
| 2825 | seen[p] = true |
| 2826 | if res[p.ID] != nil { |
| 2827 | panic("duplicate ID: " + p.ID) |
| 2828 | } |
| 2829 | res[p.ID] = p |
| 2830 | |
| 2831 | star := ' ' // mark initial packages with a star |
| 2832 | if initialSet[p] { |
| 2833 | star = '*' |
| 2834 | } |
| 2835 | nodes = append(nodes, fmt.Sprintf("%c %s", star, p.ID)) |
| 2836 | |
| 2837 | // To avoid a lot of noise, |
| 2838 | // we prune uninteresting dependencies of testmain packages, |
| 2839 | // which we identify by this import: |
| 2840 | isTestMain := p.Imports["testing/internal/testdeps"] != nil |
| 2841 | |
| 2842 | for _, imp := range p.Imports { |
| 2843 | if isTestMain { |
| 2844 | switch imp.ID { |
| 2845 | case "os", "reflect", "testing", "testing/internal/testdeps": |
| 2846 | continue |
| 2847 | } |
| 2848 | } |
| 2849 | // math/bits took on a dependency on unsafe in 1.12, which breaks some |
| 2850 | // tests. As a short term hack, prune that edge. |
| 2851 | // ditto for ("errors", "internal/reflectlite") in 1.13. |
| 2852 | // TODO(matloob): think of a cleaner solution, or remove math/bits from the test. |
| 2853 | if p.ID == "math/bits" && imp.ID == "unsafe" { |
| 2854 | continue |
| 2855 | } |
| 2856 | edges = append(edges, fmt.Sprintf("%s -> %s", p, imp)) |
| 2857 | visit(imp) |
| 2858 | } |
| 2859 | } |
| 2860 | } |
| 2861 | for _, p := range initial { |
| 2862 | visit(p) |
| 2863 | } |
| 2864 | |
| 2865 | // Sort, ignoring leading optional star prefix. |
| 2866 | sort.Slice(nodes, func(i, j int) bool { return nodes[i][2:] < nodes[j][2:] }) |
| 2867 | for _, node := range nodes { |
| 2868 | fmt.Fprintf(out, "%s\n", node) |
| 2869 | } |
| 2870 | |
| 2871 | sort.Strings(edges) |
| 2872 | for _, edge := range edges { |
| 2873 | fmt.Fprintf(out, " %s\n", edge) |
| 2874 | } |
| 2875 | |
| 2876 | return out.String(), res |
| 2877 | } |
| 2878 | |
| 2879 | func constant(p *packages.Package, name string) *types.Const { |
| 2880 | if p == nil || p.Types == nil { |
| 2881 | return nil |
| 2882 | } |
| 2883 | c := p.Types.Scope().Lookup(name) |
| 2884 | if c == nil { |
| 2885 | return nil |
| 2886 | } |
| 2887 | return c.(*types.Const) |
| 2888 | } |
| 2889 | |
| 2890 | func copyAll(srcPath, dstPath string) error { |
| 2891 | return filepath.Walk(srcPath, func(path string, info os.FileInfo, _ error) error { |
| 2892 | if info.IsDir() { |
| 2893 | return nil |
| 2894 | } |
| 2895 | contents, err := ioutil.ReadFile(path) |
| 2896 | if err != nil { |
| 2897 | return err |
| 2898 | } |
| 2899 | rel, err := filepath.Rel(srcPath, path) |
| 2900 | if err != nil { |
| 2901 | return err |
| 2902 | } |
| 2903 | dstFilePath := strings.Replace(filepath.Join(dstPath, rel), "definitelynot_go.mod", "go.mod", -1) |
| 2904 | if err := os.MkdirAll(filepath.Dir(dstFilePath), 0755); err != nil { |
| 2905 | return err |
| 2906 | } |
| 2907 | if err := ioutil.WriteFile(dstFilePath, contents, 0644); err != nil { |
| 2908 | return err |
| 2909 | } |
| 2910 | return nil |
| 2911 | }) |
| 2912 | } |
| 2913 | |
| 2914 | func TestExportFile(t *testing.T) { |
| 2915 | // This used to trigger the log.Fatal in loadFromExportData. |
| 2916 | // See go.dev/issue/45584. |
| 2917 | cfg := new(packages.Config) |
| 2918 | cfg.Mode = packages.NeedTypes |
| 2919 | packages.Load(cfg, "fmt") |
| 2920 | } |
| 2921 |
Members