Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / packages / packagestest / export.go
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 /*
6 Package packagestest creates temporary projects on disk for testing go tools on.
7
8 By changing the exporter used, you can create projects for multiple build
9 systems from the same description, and run the same tests on them in many
10 cases.
11
12 Example
13
14 As an example of packagestest use, consider the following test that runs
15 the 'go list' command on the specified modules:
16
17         // TestGoList exercises the 'go list' command in module mode and in GOPATH mode.
18         func TestGoList(t *testing.T) { packagestest.TestAll(t, testGoList) }
19         func testGoList(t *testing.T, x packagestest.Exporter) {
20                 e := packagestest.Export(t, x, []packagestest.Module{
21                         {
22                                 Name: "gopher.example/repoa",
23                                 Files: map[string]interface{}{
24                                         "a/a.go": "package a",
25                                 },
26                         },
27                         {
28                                 Name: "gopher.example/repob",
29                                 Files: map[string]interface{}{
30                                         "b/b.go": "package b",
31                                 },
32                         },
33                 })
34                 defer e.Cleanup()
35
36                 cmd := exec.Command("go", "list", "gopher.example/...")
37                 cmd.Dir = e.Config.Dir
38                 cmd.Env = e.Config.Env
39                 out, err := cmd.Output()
40                 if err != nil {
41                         t.Fatal(err)
42                 }
43                 t.Logf("'go list gopher.example/...' with %s mode layout:\n%s", x.Name(), out)
44         }
45
46 TestGoList uses TestAll to exercise the 'go list' command with all
47 exporters known to packagestest. Currently, packagestest includes
48 exporters that produce module mode layouts and GOPATH mode layouts.
49 Running the test with verbose output will print:
50
51         === RUN   TestGoList
52         === RUN   TestGoList/GOPATH
53         === RUN   TestGoList/Modules
54         --- PASS: TestGoList (0.21s)
55             --- PASS: TestGoList/GOPATH (0.03s)
56                 main_test.go:36: 'go list gopher.example/...' with GOPATH mode layout:
57                     gopher.example/repoa/a
58                     gopher.example/repob/b
59             --- PASS: TestGoList/Modules (0.18s)
60                 main_test.go:36: 'go list gopher.example/...' with Modules mode layout:
61                     gopher.example/repoa/a
62                     gopher.example/repob/b
63
64 */
65 package packagestest
66
67 import (
68         "flag"
69         "fmt"
70         "go/token"
71         "io/ioutil"
72         "log"
73         "os"
74         "path/filepath"
75         "strings"
76         "testing"
77
78         "golang.org/x/tools/go/expect"
79         "golang.org/x/tools/go/packages"
80         "golang.org/x/tools/internal/span"
81         "golang.org/x/tools/internal/testenv"
82 )
83
84 var (
85         skipCleanup = flag.Bool("skip-cleanup", false, "Do not delete the temporary export folders") // for debugging
86 )
87
88 // Module is a representation of a go module.
89 type Module struct {
90         // Name is the base name of the module as it would be in the go.mod file.
91         Name string
92         // Files is the set of source files for all packages that make up the module.
93         // The keys are the file fragment that follows the module name, the value can
94         // be a string or byte slice, in which case it is the contents of the
95         // file, otherwise it must be a Writer function.
96         Files map[string]interface{}
97
98         // Overlay is the set of source file overlays for the module.
99         // The keys are the file fragment as in the Files configuration.
100         // The values are the in memory overlay content for the file.
101         Overlay map[string][]byte
102 }
103
104 // A Writer is a function that writes out a test file.
105 // It is provided the name of the file to write, and may return an error if it
106 // cannot write the file.
107 // These are used as the content of the Files map in a Module.
108 type Writer func(filename string) error
109
110 // Exported is returned by the Export function to report the structure that was produced on disk.
111 type Exported struct {
112         // Config is a correctly configured packages.Config ready to be passed to packages.Load.
113         // Exactly what it will contain varies depending on the Exporter being used.
114         Config *packages.Config
115
116         // Modules is the module description that was used to produce this exported data set.
117         Modules []Module
118
119         ExpectFileSet *token.FileSet // The file set used when parsing expectations
120
121         Exporter Exporter                     // the exporter used
122         temp     string                       // the temporary directory that was exported to
123         primary  string                       // the first non GOROOT module that was exported
124         written  map[string]map[string]string // the full set of exported files
125         notes    []*expect.Note               // The list of expectations extracted from go source files
126         markers  map[string]span.Range        // The set of markers extracted from go source files
127 }
128
129 // Exporter implementations are responsible for converting from the generic description of some
130 // test data to a driver specific file layout.
131 type Exporter interface {
132         // Name reports the name of the exporter, used in logging and sub-test generation.
133         Name() string
134         // Filename reports the system filename for test data source file.
135         // It is given the base directory, the module the file is part of and the filename fragment to
136         // work from.
137         Filename(exported *Exported, module, fragment string) string
138         // Finalize is called once all files have been written to write any extra data needed and modify
139         // the Config to match. It is handed the full list of modules that were encountered while writing
140         // files.
141         Finalize(exported *Exported) error
142 }
143
144 // All is the list of known exporters.
145 // This is used by TestAll to run tests with all the exporters.
146 var All []Exporter
147
148 // TestAll invokes the testing function once for each exporter registered in
149 // the All global.
150 // Each exporter will be run as a sub-test named after the exporter being used.
151 func TestAll(t *testing.T, f func(*testing.T, Exporter)) {
152         t.Helper()
153         for _, e := range All {
154                 t.Run(e.Name(), func(t *testing.T) {
155                         t.Helper()
156                         f(t, e)
157                 })
158         }
159 }
160
161 // BenchmarkAll invokes the testing function once for each exporter registered in
162 // the All global.
163 // Each exporter will be run as a sub-test named after the exporter being used.
164 func BenchmarkAll(b *testing.B, f func(*testing.B, Exporter)) {
165         b.Helper()
166         for _, e := range All {
167                 b.Run(e.Name(), func(b *testing.B) {
168                         b.Helper()
169                         f(b, e)
170                 })
171         }
172 }
173
174 // Export is called to write out a test directory from within a test function.
175 // It takes the exporter and the build system agnostic module descriptions, and
176 // uses them to build a temporary directory.
177 // It returns an Exported with the results of the export.
178 // The Exported.Config is prepared for loading from the exported data.
179 // You must invoke Exported.Cleanup on the returned value to clean up.
180 // The file deletion in the cleanup can be skipped by setting the skip-cleanup
181 // flag when invoking the test, allowing the temporary directory to be left for
182 // debugging tests.
183 func Export(t testing.TB, exporter Exporter, modules []Module) *Exported {
184         t.Helper()
185         if exporter == Modules {
186                 testenv.NeedsTool(t, "go")
187         }
188
189         dirname := strings.Replace(t.Name(), "/", "_", -1)
190         dirname = strings.Replace(dirname, "#", "_", -1) // duplicate subtests get a #NNN suffix.
191         temp, err := ioutil.TempDir("", dirname)
192         if err != nil {
193                 t.Fatal(err)
194         }
195         exported := &Exported{
196                 Config: &packages.Config{
197                         Dir:     temp,
198                         Env:     append(os.Environ(), "GOPACKAGESDRIVER=off", "GOROOT="), // Clear GOROOT to work around #32849.
199                         Overlay: make(map[string][]byte),
200                         Tests:   true,
201                         Mode:    packages.LoadImports,
202                 },
203                 Modules:       modules,
204                 Exporter:      exporter,
205                 temp:          temp,
206                 primary:       modules[0].Name,
207                 written:       map[string]map[string]string{},
208                 ExpectFileSet: token.NewFileSet(),
209         }
210         defer func() {
211                 if t.Failed() || t.Skipped() {
212                         exported.Cleanup()
213                 }
214         }()
215         for _, module := range modules {
216                 for fragment, value := range module.Files {
217                         fullpath := exporter.Filename(exported, module.Name, filepath.FromSlash(fragment))
218                         written, ok := exported.written[module.Name]
219                         if !ok {
220                                 written = map[string]string{}
221                                 exported.written[module.Name] = written
222                         }
223                         written[fragment] = fullpath
224                         if err := os.MkdirAll(filepath.Dir(fullpath), 0755); err != nil {
225                                 t.Fatal(err)
226                         }
227                         switch value := value.(type) {
228                         case Writer:
229                                 if err := value(fullpath); err != nil {
230                                         t.Fatal(err)
231                                 }
232                         case string:
233                                 if err := ioutil.WriteFile(fullpath, []byte(value), 0644); err != nil {
234                                         t.Fatal(err)
235                                 }
236                         default:
237                                 t.Fatalf("Invalid type %T in files, must be string or Writer", value)
238                         }
239                 }
240                 for fragment, value := range module.Overlay {
241                         fullpath := exporter.Filename(exported, module.Name, filepath.FromSlash(fragment))
242                         exported.Config.Overlay[fullpath] = value
243                 }
244         }
245         if err := exporter.Finalize(exported); err != nil {
246                 t.Fatal(err)
247         }
248         testenv.NeedsGoPackagesEnv(t, exported.Config.Env)
249         return exported
250 }
251
252 // Script returns a Writer that writes out contents to the file and sets the
253 // executable bit on the created file.
254 // It is intended for source files that are shell scripts.
255 func Script(contents string) Writer {
256         return func(filename string) error {
257                 return ioutil.WriteFile(filename, []byte(contents), 0755)
258         }
259 }
260
261 // Link returns a Writer that creates a hard link from the specified source to
262 // the required file.
263 // This is used to link testdata files into the generated testing tree.
264 func Link(source string) Writer {
265         return func(filename string) error {
266                 return os.Link(source, filename)
267         }
268 }
269
270 // Symlink returns a Writer that creates a symlink from the specified source to the
271 // required file.
272 // This is used to link testdata files into the generated testing tree.
273 func Symlink(source string) Writer {
274         if !strings.HasPrefix(source, ".") {
275                 if abspath, err := filepath.Abs(source); err == nil {
276                         if _, err := os.Stat(source); !os.IsNotExist(err) {
277                                 source = abspath
278                         }
279                 }
280         }
281         return func(filename string) error {
282                 return os.Symlink(source, filename)
283         }
284 }
285
286 // Copy returns a Writer that copies a file from the specified source to the
287 // required file.
288 // This is used to copy testdata files into the generated testing tree.
289 func Copy(source string) Writer {
290         return func(filename string) error {
291                 stat, err := os.Stat(source)
292                 if err != nil {
293                         return err
294                 }
295                 if !stat.Mode().IsRegular() {
296                         // cannot copy non-regular files (e.g., directories,
297                         // symlinks, devices, etc.)
298                         return fmt.Errorf("cannot copy non regular file %s", source)
299                 }
300                 contents, err := ioutil.ReadFile(source)
301                 if err != nil {
302                         return err
303                 }
304                 return ioutil.WriteFile(filename, contents, stat.Mode())
305         }
306 }
307
308 // GroupFilesByModules attempts to map directories to the modules within each directory.
309 // This function assumes that the folder is structured in the following way:
310 // - dir
311 //   - primarymod
312 //     - .go files
313 //               - packages
314 //               - go.mod (optional)
315 //       - modules
316 //               - repoa
317 //                 - mod1
318 //             - .go files
319 //                         -  packages
320 //                       - go.mod (optional)
321 // It scans the directory tree anchored at root and adds a Copy writer to the
322 // map for every file found.
323 // This is to enable the common case in tests where you have a full copy of the
324 // package in your testdata.
325 func GroupFilesByModules(root string) ([]Module, error) {
326         root = filepath.FromSlash(root)
327         primarymodPath := filepath.Join(root, "primarymod")
328
329         _, err := os.Stat(primarymodPath)
330         if os.IsNotExist(err) {
331                 return nil, fmt.Errorf("could not find primarymod folder within %s", root)
332         }
333
334         primarymod := &Module{
335                 Name:    root,
336                 Files:   make(map[string]interface{}),
337                 Overlay: make(map[string][]byte),
338         }
339         mods := map[string]*Module{
340                 root: primarymod,
341         }
342         modules := []Module{*primarymod}
343
344         if err := filepath.Walk(primarymodPath, func(path string, info os.FileInfo, err error) error {
345                 if err != nil {
346                         return err
347                 }
348                 if info.IsDir() {
349                         return nil
350                 }
351                 fragment, err := filepath.Rel(primarymodPath, path)
352                 if err != nil {
353                         return err
354                 }
355                 primarymod.Files[filepath.ToSlash(fragment)] = Copy(path)
356                 return nil
357         }); err != nil {
358                 return nil, err
359         }
360
361         modulesPath := filepath.Join(root, "modules")
362         if _, err := os.Stat(modulesPath); os.IsNotExist(err) {
363                 return modules, nil
364         }
365
366         var currentRepo, currentModule string
367         updateCurrentModule := func(dir string) {
368                 if dir == currentModule {
369                         return
370                 }
371                 // Handle the case where we step into a nested directory that is a module
372                 // and then step out into the parent which is also a module.
373                 // Example:
374                 // - repoa
375                 //   - moda
376                 //     - go.mod
377                 //     - v2
378                 //       - go.mod
379                 //     - what.go
380                 //   - modb
381                 for dir != root {
382                         if mods[dir] != nil {
383                                 currentModule = dir
384                                 return
385                         }
386                         dir = filepath.Dir(dir)
387                 }
388         }
389
390         if err := filepath.Walk(modulesPath, func(path string, info os.FileInfo, err error) error {
391                 if err != nil {
392                         return err
393                 }
394                 enclosingDir := filepath.Dir(path)
395                 // If the path is not a directory, then we want to add the path to
396                 // the files map of the currentModule.
397                 if !info.IsDir() {
398                         updateCurrentModule(enclosingDir)
399                         fragment, err := filepath.Rel(currentModule, path)
400                         if err != nil {
401                                 return err
402                         }
403                         mods[currentModule].Files[filepath.ToSlash(fragment)] = Copy(path)
404                         return nil
405                 }
406                 // If the path is a directory and it's enclosing folder is equal to
407                 // the modules folder, then the path is a new repo.
408                 if enclosingDir == modulesPath {
409                         currentRepo = path
410                         return nil
411                 }
412                 // If the path is a directory and it's enclosing folder is not the same
413                 // as the current repo and it is not of the form `v1`,`v2`,...
414                 // then the path is a folder/package of the current module.
415                 if enclosingDir != currentRepo && !versionSuffixRE.MatchString(filepath.Base(path)) {
416                         return nil
417                 }
418                 // If the path is a directory and it's enclosing folder is the current repo
419                 // then the path is a new module.
420                 module, err := filepath.Rel(modulesPath, path)
421                 if err != nil {
422                         return err
423                 }
424                 mods[path] = &Module{
425                         Name:    filepath.ToSlash(module),
426                         Files:   make(map[string]interface{}),
427                         Overlay: make(map[string][]byte),
428                 }
429                 currentModule = path
430                 modules = append(modules, *mods[path])
431                 return nil
432         }); err != nil {
433                 return nil, err
434         }
435         return modules, nil
436 }
437
438 // MustCopyFileTree returns a file set for a module based on a real directory tree.
439 // It scans the directory tree anchored at root and adds a Copy writer to the
440 // map for every file found.
441 // This is to enable the common case in tests where you have a full copy of the
442 // package in your testdata.
443 // This will panic if there is any kind of error trying to walk the file tree.
444 func MustCopyFileTree(root string) map[string]interface{} {
445         result := map[string]interface{}{}
446         if err := filepath.Walk(filepath.FromSlash(root), func(path string, info os.FileInfo, err error) error {
447                 if err != nil {
448                         return err
449                 }
450                 if info.IsDir() {
451                         return nil
452                 }
453                 fragment, err := filepath.Rel(root, path)
454                 if err != nil {
455                         return err
456                 }
457                 result[filepath.ToSlash(fragment)] = Copy(path)
458                 return nil
459         }); err != nil {
460                 log.Panic(fmt.Sprintf("MustCopyFileTree failed: %v", err))
461         }
462         return result
463 }
464
465 // Cleanup removes the temporary directory (unless the --skip-cleanup flag was set)
466 // It is safe to call cleanup multiple times.
467 func (e *Exported) Cleanup() {
468         if e.temp == "" {
469                 return
470         }
471         if *skipCleanup {
472                 log.Printf("Skipping cleanup of temp dir: %s", e.temp)
473                 return
474         }
475         // Make everything read-write so that the Module exporter's module cache can be deleted.
476         filepath.Walk(e.temp, func(path string, info os.FileInfo, err error) error {
477                 if err != nil {
478                         return nil
479                 }
480                 if info.IsDir() {
481                         os.Chmod(path, 0777)
482                 }
483                 return nil
484         })
485         os.RemoveAll(e.temp) // ignore errors
486         e.temp = ""
487 }
488
489 // Temp returns the temporary directory that was generated.
490 func (e *Exported) Temp() string {
491         return e.temp
492 }
493
494 // File returns the full path for the given module and file fragment.
495 func (e *Exported) File(module, fragment string) string {
496         if m := e.written[module]; m != nil {
497                 return m[fragment]
498         }
499         return ""
500 }
501
502 // FileContents returns the contents of the specified file.
503 // It will use the overlay if the file is present, otherwise it will read it
504 // from disk.
505 func (e *Exported) FileContents(filename string) ([]byte, error) {
506         if content, found := e.Config.Overlay[filename]; found {
507                 return content, nil
508         }
509         content, err := ioutil.ReadFile(filename)
510         if err != nil {
511                 return nil, err
512         }
513         return content, nil
514 }