7
7
package gcimporter_test
8
8
9
9
import (
10
- "bufio"
11
10
"bytes"
12
11
"fmt"
13
12
"go/ast"
14
- "go/build"
15
13
"go/constant"
16
14
"go/importer"
17
15
"go/parser"
18
16
"go/token"
19
17
"go/types"
20
- "io"
21
18
"math/big"
22
19
"os"
23
20
"path/filepath"
24
21
"reflect"
25
- "runtime"
26
- "sort"
27
22
"strings"
28
23
"testing"
29
24
30
- "golang.org/x/tools/go/ast/inspector"
31
- "golang.org/x/tools/go/buildutil"
32
25
"golang.org/x/tools/go/gcexportdata"
33
- "golang.org/x/tools/go/loader "
26
+ "golang.org/x/tools/go/packages "
34
27
"golang.org/x/tools/internal/aliases"
35
28
"golang.org/x/tools/internal/gcimporter"
36
29
"golang.org/x/tools/internal/testenv"
37
- "golang.org/x/tools/internal/typeparams/genericfeatures"
38
30
)
39
31
40
- func readExportFile (filename string ) ([]byte , error ) {
41
- f , err := os .Open (filename )
42
- if err != nil {
43
- return nil , err
44
- }
45
- defer f .Close ()
46
-
47
- buf := bufio .NewReader (f )
48
- if _ , _ , err := gcimporter .FindExportData (buf ); err != nil {
49
- return nil , err
50
- }
51
-
52
- if ch , err := buf .ReadByte (); err != nil {
53
- return nil , err
54
- } else if ch != 'i' {
55
- return nil , fmt .Errorf ("unexpected byte: %v" , ch )
56
- }
57
-
58
- return io .ReadAll (buf )
59
- }
60
-
61
32
func iexport (fset * token.FileSet , version int , pkg * types.Package ) ([]byte , error ) {
62
33
var buf bytes.Buffer
63
34
const bundle , shallow = false , false
@@ -67,105 +38,95 @@ func iexport(fset *token.FileSet, version int, pkg *types.Package) ([]byte, erro
67
38
return buf .Bytes (), nil
68
39
}
69
40
70
- // isUnifiedBuilder reports whether we are executing on a go builder that uses
71
- // unified export data.
72
- func isUnifiedBuilder () bool {
73
- return os .Getenv ("GO_BUILDER_NAME" ) == "linux-amd64-unified"
74
- }
75
-
76
- const minStdlibPackages = 248
77
-
78
41
func TestIExportData_stdlib (t * testing.T ) {
79
- if runtime .Compiler == "gccgo" {
80
- t .Skip ("gccgo standard library is inaccessible" )
81
- }
82
- testenv .NeedsGoBuild (t )
42
+ testenv .NeedsGoPackages (t )
83
43
if isRace {
84
44
t .Skipf ("stdlib tests take too long in race mode and flake on builders" )
85
45
}
86
46
if testing .Short () {
87
47
t .Skip ("skipping RAM hungry test in -short mode" )
88
48
}
89
49
90
- // Load, parse and type-check the program.
91
- ctxt := build .Default // copy
92
- ctxt .GOPATH = "" // disable GOPATH
93
- conf := loader.Config {
94
- Build : & ctxt ,
95
- AllowErrors : true ,
96
- TypeChecker : types.Config {
97
- Sizes : types .SizesFor (ctxt .Compiler , ctxt .GOARCH ),
98
- Error : func (err error ) { t .Log (err ) },
99
- },
100
- }
101
- for _ , path := range buildutil .AllPackages (conf .Build ) {
102
- conf .Import (path )
50
+ var errorsDir string // GOROOT/src/errors directory
51
+ {
52
+ cfg := packages.Config {
53
+ Mode : packages .NeedName | packages .NeedFiles ,
54
+ }
55
+ pkgs , err := packages .Load (& cfg , "errors" )
56
+ if err != nil {
57
+ t .Fatal (err )
58
+ }
59
+ errorsDir = filepath .Dir (pkgs [0 ].GoFiles [0 ])
103
60
}
104
61
105
- // Create a package containing type and value errors to ensure
106
- // they are properly encoded/decoded.
107
- f , err := conf .ParseFile ("haserrors/haserrors.go" , `package haserrors
62
+ // Load types from syntax for all std packages.
63
+ //
64
+ // Append a file to package errors containing type and
65
+ // value errors to ensure they are properly encoded/decoded.
66
+ const bad = `package errors
108
67
const UnknownValue = "" + 0
109
68
type UnknownType undefined
110
- ` )
69
+ `
70
+ cfg := packages.Config {
71
+ Mode : packages .LoadAllSyntax | packages .NeedDeps ,
72
+ Overlay : map [string ][]byte {filepath .Join (errorsDir , "bad.go" ): []byte (bad )},
73
+ }
74
+ pkgs , err := packages .Load (& cfg , "std" ) // ~800ms
111
75
if err != nil {
112
76
t .Fatal (err )
113
77
}
114
- conf . CreateFromFiles ( "haserrors" , f )
78
+ fset := pkgs [ 0 ]. Fset
115
79
116
- prog , err := conf .Load ()
117
- if err != nil {
118
- t .Fatalf ("Load failed: %v" , err )
119
- }
80
+ version := gcimporter .IExportVersion
120
81
121
- var sorted []* types.Package
122
- isUnified := isUnifiedBuilder ()
123
- for pkg , info := range prog .AllPackages {
124
- // Temporarily skip packages that use generics on the unified builder, to
125
- // fix TryBots.
126
- //
127
- // TODO(#48595): fix this test with GOEXPERIMENT=unified.
128
- inspect := inspector .New (info .Files )
129
- features := genericfeatures .ForPackage (inspect , & info .Info )
130
- if isUnified && features != 0 {
131
- t .Logf ("skipping package %q which uses generics" , pkg .Path ())
132
- continue
82
+ // Export and reimport each package, and check that they match.
83
+ var allPkgs []* types.Package
84
+ var errorsPkg * types.Package // reimported errors package
85
+ packages .Visit (pkgs , nil , func (ppkg * packages.Package ) { // ~300ms
86
+ pkg := ppkg .Types
87
+ path := pkg .Path ()
88
+ if path == "unsafe" ||
89
+ strings .HasPrefix (path , "cmd/" ) ||
90
+ strings .HasPrefix (path , "vendor/" ) {
91
+ return
133
92
}
134
- if info .Files != nil { // non-empty directory
135
- sorted = append (sorted , pkg )
93
+ allPkgs = append (allPkgs , pkg )
94
+
95
+ // Export and reimport the package, and compare.
96
+ exportdata , err := iexport (fset , version , pkg )
97
+ if err != nil {
98
+ t .Error (err )
99
+ return
100
+ }
101
+ pkg2 := testPkgData (t , fset , version , pkg , exportdata )
102
+ if path == "errors" {
103
+ errorsPkg = pkg2
136
104
}
137
- }
138
- sort .Slice (sorted , func (i , j int ) bool {
139
- return sorted [i ].Path () < sorted [j ].Path ()
140
105
})
141
106
142
- version := gcimporter . IExportVersion
143
- numPkgs := len ( sorted )
144
- if want := minStdlibPackages ; numPkgs < want {
145
- t .Errorf ("Loaded only %d packages, want at least %d" , numPkgs , want )
107
+ // Assert that we saw a plausible sized library.
108
+ const minStdlibPackages = 284
109
+ if n := len ( allPkgs ); n < minStdlibPackages {
110
+ t .Errorf ("Loaded only %d packages, want at least %d" , n , minStdlibPackages )
146
111
}
147
112
148
- // TODO(adonovan): opt: parallelize this slow loop.
149
- for _ , pkg := range sorted {
150
- if exportdata , err := iexport (conf .Fset , version , pkg ); err != nil {
151
- t .Error (err )
152
- } else {
153
- testPkgData (t , conf .Fset , version , pkg , exportdata )
113
+ // Check that reimported errors package has bad decls.
114
+ if errorsPkg == nil {
115
+ t .Fatalf ("'errors' package not found" )
116
+ }
117
+ for _ , name := range []string {"UnknownType" , "UnknownValue" } {
118
+ obj := errorsPkg .Scope ().Lookup (name )
119
+ if obj == nil {
120
+ t .Errorf ("errors.%s not found" , name )
154
121
}
155
-
156
- if pkg .Name () == "main" || pkg .Name () == "haserrors" {
157
- // skip; no export data
158
- } else if bp , err := ctxt .Import (pkg .Path (), "" , build .FindOnly ); err != nil {
159
- t .Log ("warning:" , err )
160
- } else if exportdata , err := readExportFile (bp .PkgObj ); err != nil {
161
- t .Log ("warning:" , err )
162
- } else {
163
- testPkgData (t , conf .Fset , version , pkg , exportdata )
122
+ if typ := obj .Type ().Underlying (); typ .String () != "invalid type" {
123
+ t .Errorf ("errors.%s has underlying type %s, want invalid type" , name , typ )
164
124
}
165
125
}
166
126
127
+ // (Sole) test of bundle functionality (250ms).
167
128
var bundle bytes.Buffer
168
- if err := gcimporter .IExportBundle (& bundle , conf . Fset , sorted ); err != nil {
129
+ if err := gcimporter .IExportBundle (& bundle , fset , allPkgs ); err != nil {
169
130
t .Fatal (err )
170
131
}
171
132
fset2 := token .NewFileSet ()
@@ -174,13 +135,13 @@ type UnknownType undefined
174
135
if err != nil {
175
136
t .Fatal (err )
176
137
}
177
-
178
- for i , pkg := range sorted {
179
- testPkg (t , conf .Fset , version , pkg , fset2 , pkgs2 [i ])
138
+ for i , pkg := range allPkgs {
139
+ testPkg (t , fset , version , pkg , fset2 , pkgs2 [i ])
180
140
}
181
141
}
182
142
183
- func testPkgData (t * testing.T , fset * token.FileSet , version int , pkg * types.Package , exportdata []byte ) {
143
+ // testPkgData imports a package from export data and compares it with pkg.
144
+ func testPkgData (t * testing.T , fset * token.FileSet , version int , pkg * types.Package , exportdata []byte ) * types.Package {
184
145
imports := make (map [string ]* types.Package )
185
146
fset2 := token .NewFileSet ()
186
147
_ , pkg2 , err := gcimporter .IImportData (fset2 , imports , exportdata , pkg .Path ())
@@ -189,6 +150,7 @@ func testPkgData(t *testing.T, fset *token.FileSet, version int, pkg *types.Pack
189
150
}
190
151
191
152
testPkg (t , fset , version , pkg , fset2 , pkg2 )
153
+ return pkg2
192
154
}
193
155
194
156
func testPkg (t * testing.T , fset * token.FileSet , version int , pkg * types.Package , fset2 * token.FileSet , pkg2 * types.Package ) {
0 commit comments