github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/gcimporter/stdlib_test.go (about)

     1  // Copyright 2022 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 gcimporter_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/token"
    11  	"go/types"
    12  	"runtime"
    13  	"testing"
    14  	"unsafe"
    15  
    16  	"golang.org/x/tools/go/gcexportdata"
    17  	"golang.org/x/tools/go/packages"
    18  	"golang.org/x/tools/internal/testenv"
    19  )
    20  
    21  // TestStdlib ensures that all packages in std and x/tools can be
    22  // type-checked using export data. Takes around 3s.
    23  func TestStdlib(t *testing.T) {
    24  	testenv.NeedsGoPackages(t)
    25  
    26  	// gcexportdata.Read rapidly consumes FileSet address space,
    27  	// so disable the test on 32-bit machines.
    28  	// (We could use a fresh FileSet per type-check, but that
    29  	// would require us to re-parse the source using it.)
    30  	if unsafe.Sizeof(token.NoPos) < 8 {
    31  		t.Skip("skipping test on 32-bit machine")
    32  	}
    33  
    34  	// Load, parse and type-check the standard library.
    35  	// If we have the full source code for x/tools, also load and type-check that.
    36  	cfg := &packages.Config{Mode: packages.LoadAllSyntax}
    37  	patterns := []string{"std"}
    38  	minPkgs := 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more.
    39  	switch runtime.GOOS {
    40  	case "android", "ios":
    41  		// The go_.*_exec script for mobile builders only copies over the source tree
    42  		// for the package under test.
    43  	default:
    44  		patterns = append(patterns, "golang.org/x/tools/...")
    45  		minPkgs += 160 // At the time of writing, 'GOOS=plan9 go list ./... | wc -l' reports 188.
    46  	}
    47  	pkgs, err := packages.Load(cfg, patterns...)
    48  	if err != nil {
    49  		t.Fatalf("failed to load/parse/type-check: %v", err)
    50  	}
    51  	if packages.PrintErrors(pkgs) > 0 {
    52  		t.Fatal("there were errors during loading")
    53  	}
    54  	if len(pkgs) < minPkgs {
    55  		t.Errorf("too few packages (%d) were loaded", len(pkgs))
    56  	}
    57  
    58  	export := make(map[string][]byte) // keys are package IDs
    59  
    60  	// Re-type check them all in post-order, using export data.
    61  	packages.Visit(pkgs, nil, func(pkg *packages.Package) {
    62  		packages := make(map[string]*types.Package) // keys are package paths
    63  		cfg := &types.Config{
    64  			Error: func(e error) {
    65  				t.Errorf("type error: %v", e)
    66  			},
    67  			Importer: importerFunc(func(importPath string) (*types.Package, error) {
    68  				// Resolve import path to (vendored?) package path.
    69  				imported := pkg.Imports[importPath]
    70  
    71  				if imported.PkgPath == "unsafe" {
    72  					return types.Unsafe, nil // unsafe has no exportdata
    73  				}
    74  
    75  				data, ok := export[imported.ID]
    76  				if !ok {
    77  					return nil, fmt.Errorf("missing export data for %s", importPath)
    78  				}
    79  				return gcexportdata.Read(bytes.NewReader(data), pkg.Fset, packages, imported.PkgPath)
    80  			}),
    81  		}
    82  
    83  		// Re-typecheck the syntax and save the export data in the map.
    84  		newPkg := types.NewPackage(pkg.PkgPath, pkg.Name)
    85  		check := types.NewChecker(cfg, pkg.Fset, newPkg, nil)
    86  		check.Files(pkg.Syntax)
    87  
    88  		var out bytes.Buffer
    89  		if err := gcexportdata.Write(&out, pkg.Fset, newPkg); err != nil {
    90  			t.Fatalf("internal error writing export data: %v", err)
    91  		}
    92  		export[pkg.ID] = out.Bytes()
    93  	})
    94  }