File: build_test.go

package info (click to toggle)
golang-github-gopherjs-gopherjs 0.0~git20170927.0.4152256-7
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,468 kB
  • sloc: cpp: 91; makefile: 7
file content (199 lines) | stat: -rw-r--r-- 7,248 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package build

import (
	"fmt"
	gobuild "go/build"
	"go/token"
	"strconv"
	"strings"
	"testing"

	"github.com/kisielk/gotool"
	"github.com/shurcooL/go/importgraphutil"
)

// Natives augment the standard library with GopherJS-specific changes.
// This test ensures that none of the standard library packages are modified
// in a way that adds imports which the original upstream standard library package
// does not already import. Doing that can increase generated output size or cause
// other unexpected issues (since the cmd/go tool does not know about these extra imports),
// so it's best to avoid it.
//
// It checks all standard library packages. Each package is considered as a normal
// package, as a test package, and as an external test package.
func TestNativesDontImportExtraPackages(t *testing.T) {
	// Calculate the forward import graph for all standard library packages.
	// It's needed for populateImportSet.
	stdOnly := gobuild.Default
	stdOnly.GOPATH = "" // We only care about standard library, so skip all GOPATH packages.
	forward, _, err := importgraphutil.BuildNoTests(&stdOnly)
	if err != nil {
		t.Fatalf("importgraphutil.BuildNoTests: %v", err)
	}

	// populateImportSet takes a slice of imports, and populates set with those
	// imports, as well as their transitive dependencies. That way, the set can
	// be quickly queried to check if a package is in the import graph of imports.
	//
	// Note, this does not include transitive imports of test/xtest packages,
	// which could cause some false positives. It currently doesn't, but if it does,
	// then support for that should be added here.
	populateImportSet := func(imports []string, set *stringSet) {
		for _, p := range imports {
			(*set)[p] = struct{}{}
			switch p {
			case "sync":
				(*set)["github.com/gopherjs/gopherjs/nosync"] = struct{}{}
			}
			transitiveImports := forward.Search(p)
			for p := range transitiveImports {
				(*set)[p] = struct{}{}
			}
		}
	}

	// Check all standard library packages.
	//
	// The general strategy is to first import each standard library package using the
	// normal build.Import, which returns a *build.Package. That contains Imports, TestImports,
	// and XTestImports values that are considered the "real imports".
	//
	// That list of direct imports is then expanded to the transitive closure by populateImportSet,
	// meaning all packages that are indirectly imported are also added to the set.
	//
	// Then, github.com/gopherjs/gopherjs/build.parseAndAugment(*build.Package) returns []*ast.File.
	// Those augmented parsed Go files of the package are checked, one file at at time, one import
	// at a time. Each import is verified to belong in the set of allowed real imports.
	for _, pkg := range gotool.ImportPaths([]string{"std"}) {
		// Normal package.
		{
			// Import the real normal package, and populate its real import set.
			bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment)
			if err != nil {
				t.Fatalf("gobuild.Import: %v", err)
			}
			realImports := make(stringSet)
			populateImportSet(bpkg.Imports, &realImports)

			// Use parseAndAugment to get a list of augmented AST files.
			fset := token.NewFileSet()
			files, err := parseAndAugment(bpkg, false, fset)
			if err != nil {
				t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
			}

			// Verify imports of normal augmented AST files.
			for _, f := range files {
				fileName := fset.File(f.Pos()).Name()
				normalFile := !strings.HasSuffix(fileName, "_test.go")
				if !normalFile {
					continue
				}
				for _, imp := range f.Imports {
					importPath, err := strconv.Unquote(imp.Path.Value)
					if err != nil {
						t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err)
					}
					if importPath == "github.com/gopherjs/gopherjs/js" {
						continue
					}
					if _, ok := realImports[importPath]; !ok {
						t.Errorf("augmented normal package %q imports %q in file %v, but real %q doesn't:\nrealImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realImports)
					}
				}
			}
		}

		// Test package.
		{
			// Import the real test package, and populate its real import set.
			bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment)
			if err != nil {
				t.Fatalf("gobuild.Import: %v", err)
			}
			realTestImports := make(stringSet)
			populateImportSet(bpkg.TestImports, &realTestImports)

			// Use parseAndAugment to get a list of augmented AST files.
			fset := token.NewFileSet()
			files, err := parseAndAugment(bpkg, true, fset)
			if err != nil {
				t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
			}

			// Verify imports of test augmented AST files.
			for _, f := range files {
				fileName, pkgName := fset.File(f.Pos()).Name(), f.Name.String()
				testFile := strings.HasSuffix(fileName, "_test.go") && !strings.HasSuffix(pkgName, "_test")
				if !testFile {
					continue
				}
				for _, imp := range f.Imports {
					importPath, err := strconv.Unquote(imp.Path.Value)
					if err != nil {
						t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err)
					}
					if importPath == "github.com/gopherjs/gopherjs/js" {
						continue
					}
					if _, ok := realTestImports[importPath]; !ok {
						t.Errorf("augmented test package %q imports %q in file %v, but real %q doesn't:\nrealTestImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realTestImports)
					}
				}
			}
		}

		// External test package.
		{
			// Import the real external test package, and populate its real import set.
			bpkg, err := gobuild.Import(pkg, "", gobuild.ImportComment)
			if err != nil {
				t.Fatalf("gobuild.Import: %v", err)
			}
			realXTestImports := make(stringSet)
			populateImportSet(bpkg.XTestImports, &realXTestImports)

			// Add _test suffix to import path to cause parseAndAugment to use external test mode.
			bpkg.ImportPath += "_test"

			// Use parseAndAugment to get a list of augmented AST files, then check only the external test files.
			fset := token.NewFileSet()
			files, err := parseAndAugment(bpkg, true, fset)
			if err != nil {
				t.Fatalf("github.com/gopherjs/gopherjs/build.parseAndAugment: %v", err)
			}

			// Verify imports of external test augmented AST files.
			for _, f := range files {
				fileName, pkgName := fset.File(f.Pos()).Name(), f.Name.String()
				xTestFile := strings.HasSuffix(fileName, "_test.go") && strings.HasSuffix(pkgName, "_test")
				if !xTestFile {
					continue
				}
				for _, imp := range f.Imports {
					importPath, err := strconv.Unquote(imp.Path.Value)
					if err != nil {
						t.Fatalf("strconv.Unquote(%v): %v", imp.Path.Value, err)
					}
					if importPath == "github.com/gopherjs/gopherjs/js" {
						continue
					}
					if _, ok := realXTestImports[importPath]; !ok {
						t.Errorf("augmented external test package %q imports %q in file %v, but real %q doesn't:\nrealXTestImports = %v", bpkg.ImportPath, importPath, fileName, bpkg.ImportPath, realXTestImports)
					}
				}
			}
		}
	}
}

// stringSet is used to print a set of strings in a more readable way.
type stringSet map[string]struct{}

func (m stringSet) String() string {
	s := make([]string, 0, len(m))
	for v := range m {
		s = append(s, v)
	}
	return fmt.Sprintf("%q", s)
}