Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / cmd / guru / guru_test.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/cmd/guru/guru_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/cmd/guru/guru_test.go
new file mode 100644 (file)
index 0000000..0699db9
--- /dev/null
@@ -0,0 +1,335 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main_test
+
+// This file defines a test framework for guru queries.
+//
+// The files beneath testdata/src contain Go programs containing
+// query annotations of the form:
+//
+//   @verb id "select"
+//
+// where verb is the query mode (e.g. "callers"), id is a unique name
+// for this query, and "select" is a regular expression matching the
+// substring of the current line that is the query's input selection.
+//
+// The expected output for each query is provided in the accompanying
+// .golden file.
+//
+// (Location information is not included because it's too fragile to
+// display as text.  TODO(adonovan): think about how we can test its
+// correctness, since it is critical information.)
+//
+// Run this test with:
+//     % go test golang.org/x/tools/cmd/guru -update
+// to update the golden files.
+
+import (
+       "bytes"
+       "flag"
+       "fmt"
+       "go/build"
+       "go/parser"
+       "go/token"
+       "io"
+       "io/ioutil"
+       "log"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "regexp"
+       "runtime"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+       "testing"
+
+       guru "golang.org/x/tools/cmd/guru"
+       "golang.org/x/tools/internal/testenv"
+)
+
+func init() {
+       // This test currently requires GOPATH mode.
+       // Explicitly disabling module mode should suffix, but
+       // we'll also turn off GOPROXY just for good measure.
+       if err := os.Setenv("GO111MODULE", "off"); err != nil {
+               log.Fatal(err)
+       }
+       if err := os.Setenv("GOPROXY", "off"); err != nil {
+               log.Fatal(err)
+       }
+}
+
+var updateFlag = flag.Bool("update", false, "Update the golden files.")
+
+type query struct {
+       id       string         // unique id
+       verb     string         // query mode, e.g. "callees"
+       posn     token.Position // query position
+       filename string
+       queryPos string // query position in command-line syntax
+}
+
+func parseRegexp(text string) (*regexp.Regexp, error) {
+       pattern, err := strconv.Unquote(text)
+       if err != nil {
+               return nil, fmt.Errorf("can't unquote %s", text)
+       }
+       return regexp.Compile(pattern)
+}
+
+// parseQueries parses and returns the queries in the named file.
+func parseQueries(t *testing.T, filename string) []*query {
+       filedata, err := ioutil.ReadFile(filename)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // Parse the file once to discover the test queries.
+       fset := token.NewFileSet()
+       f, err := parser.ParseFile(fset, filename, filedata, parser.ParseComments)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       lines := bytes.Split(filedata, []byte("\n"))
+
+       var queries []*query
+       queriesById := make(map[string]*query)
+
+       // Find all annotations of these forms:
+       expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
+       for _, c := range f.Comments {
+               text := strings.TrimSpace(c.Text())
+               if text == "" || text[0] != '@' {
+                       continue
+               }
+               posn := fset.Position(c.Pos())
+
+               // @verb id "regexp"
+               match := expectRe.FindStringSubmatch(text)
+               if match == nil {
+                       t.Errorf("%s: ill-formed query: %s", posn, text)
+                       continue
+               }
+
+               id := match[2]
+               if prev, ok := queriesById[id]; ok {
+                       t.Errorf("%s: duplicate id %s", posn, id)
+                       t.Errorf("%s: previously used here", prev.posn)
+                       continue
+               }
+
+               q := &query{
+                       id:       id,
+                       verb:     match[1],
+                       filename: filename,
+                       posn:     posn,
+               }
+
+               if match[3] != `"nopos"` {
+                       selectRe, err := parseRegexp(match[3])
+                       if err != nil {
+                               t.Errorf("%s: %s", posn, err)
+                               continue
+                       }
+
+                       // Find text of the current line, sans query.
+                       // (Queries must be // not /**/ comments.)
+                       line := lines[posn.Line-1][:posn.Column-1]
+
+                       // Apply regexp to current line to find input selection.
+                       loc := selectRe.FindIndex(line)
+                       if loc == nil {
+                               t.Errorf("%s: selection pattern %s doesn't match line %q",
+                                       posn, match[3], string(line))
+                               continue
+                       }
+
+                       // Assumes ASCII. TODO(adonovan): test on UTF-8.
+                       linestart := posn.Offset - (posn.Column - 1)
+
+                       // Compute the file offsets.
+                       q.queryPos = fmt.Sprintf("%s:#%d,#%d",
+                               filename, linestart+loc[0], linestart+loc[1])
+               }
+
+               queries = append(queries, q)
+               queriesById[id] = q
+       }
+
+       // Return the slice, not map, for deterministic iteration.
+       return queries
+}
+
+// doQuery poses query q to the guru and writes its response and
+// error (if any) to out.
+func doQuery(out io.Writer, q *query, json bool) {
+       fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)
+
+       var buildContext = build.Default
+       buildContext.GOPATH = "testdata"
+       pkg := filepath.Dir(strings.TrimPrefix(q.filename, "testdata/src/"))
+
+       gopathAbs, _ := filepath.Abs(buildContext.GOPATH)
+
+       var outputMu sync.Mutex // guards outputs
+       var outputs []string    // JSON objects or lines of text
+       outputFn := func(fset *token.FileSet, qr guru.QueryResult) {
+               outputMu.Lock()
+               defer outputMu.Unlock()
+               if json {
+                       jsonstr := string(qr.JSON(fset))
+                       // Sanitize any absolute filenames that creep in.
+                       jsonstr = strings.Replace(jsonstr, gopathAbs, "$GOPATH", -1)
+                       outputs = append(outputs, jsonstr)
+               } else {
+                       // suppress position information
+                       qr.PrintPlain(func(_ interface{}, format string, args ...interface{}) {
+                               outputs = append(outputs, fmt.Sprintf(format, args...))
+                       })
+               }
+       }
+
+       query := guru.Query{
+               Pos:        q.queryPos,
+               Build:      &buildContext,
+               Scope:      []string{pkg},
+               Reflection: true,
+               Output:     outputFn,
+       }
+
+       if err := guru.Run(q.verb, &query); err != nil {
+               fmt.Fprintf(out, "\nError: %s\n", err)
+               return
+       }
+
+       // In a "referrers" query, references are sorted within each
+       // package but packages are visited in arbitrary order,
+       // so for determinism we sort them.  Line 0 is a caption.
+       if q.verb == "referrers" {
+               sort.Strings(outputs[1:])
+       }
+
+       for _, output := range outputs {
+               fmt.Fprintf(out, "%s\n", output)
+       }
+
+       if !json {
+               io.WriteString(out, "\n")
+       }
+}
+
+func TestGuru(t *testing.T) {
+       if testing.Short() {
+               // These tests are super slow.
+               // TODO: make a lighter version of the tests for short mode?
+               t.Skipf("skipping in short mode")
+       }
+       switch runtime.GOOS {
+       case "android":
+               t.Skipf("skipping test on %q (no testdata dir)", runtime.GOOS)
+       case "windows":
+               t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
+       }
+
+       for _, filename := range []string{
+               "testdata/src/alias/alias.go",
+               "testdata/src/calls/main.go",
+               "testdata/src/describe/main.go",
+               "testdata/src/freevars/main.go",
+               "testdata/src/implements/main.go",
+               "testdata/src/implements-methods/main.go",
+               "testdata/src/imports/main.go",
+               "testdata/src/peers/main.go",
+               "testdata/src/pointsto/main.go",
+               "testdata/src/referrers/main.go",
+               "testdata/src/reflection/main.go",
+               "testdata/src/what/main.go",
+               "testdata/src/whicherrs/main.go",
+               "testdata/src/softerrs/main.go",
+               // JSON:
+               // TODO(adonovan): most of these are very similar; combine them.
+               "testdata/src/calls-json/main.go",
+               "testdata/src/peers-json/main.go",
+               "testdata/src/definition-json/main.go",
+               "testdata/src/describe-json/main.go",
+               "testdata/src/implements-json/main.go",
+               "testdata/src/implements-methods-json/main.go",
+               "testdata/src/pointsto-json/main.go",
+               "testdata/src/referrers-json/main.go",
+               "testdata/src/what-json/main.go",
+       } {
+               filename := filename
+               name := strings.Split(filename, "/")[2]
+               t.Run(name, func(t *testing.T) {
+                       t.Parallel()
+                       if filename == "testdata/src/referrers/main.go" && runtime.GOOS == "plan9" {
+                               // Disable this test on plan9 since it expects a particular
+                               // wording for a "no such file or directory" error.
+                               t.Skip()
+                       }
+                       json := strings.Contains(filename, "-json/")
+                       queries := parseQueries(t, filename)
+                       golden := filename + "lden"
+                       gotfh, err := ioutil.TempFile("", filepath.Base(filename)+"t")
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       got := gotfh.Name()
+                       defer func() {
+                               gotfh.Close()
+                               os.Remove(got)
+                       }()
+
+                       // Run the guru on each query, redirecting its output
+                       // and error (if any) to the foo.got file.
+                       for _, q := range queries {
+                               doQuery(gotfh, q, json)
+                       }
+
+                       // Compare foo.got with foo.golden.
+                       var cmd *exec.Cmd
+                       switch runtime.GOOS {
+                       case "plan9":
+                               cmd = exec.Command("/bin/diff", "-c", golden, got)
+                       default:
+                               cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
+                       }
+                       testenv.NeedsTool(t, cmd.Path)
+                       buf := new(bytes.Buffer)
+                       cmd.Stdout = buf
+                       cmd.Stderr = os.Stderr
+                       if err := cmd.Run(); err != nil {
+                               t.Errorf("Guru tests for %s failed: %s.\n%s\n",
+                                       filename, err, buf)
+
+                               if *updateFlag {
+                                       t.Logf("Updating %s...", golden)
+                                       if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
+                                               t.Errorf("Update failed: %s", err)
+                                       }
+                               }
+                       }
+               })
+       }
+}
+
+func TestIssue14684(t *testing.T) {
+       var buildContext = build.Default
+       buildContext.GOPATH = "testdata"
+       query := guru.Query{
+               Pos:   "testdata/src/README.txt:#1",
+               Build: &buildContext,
+       }
+       err := guru.Run("freevars", &query)
+       if err == nil {
+               t.Fatal("guru query succeeded unexpectedly")
+       }
+       if got, want := err.Error(), "testdata/src/README.txt is not a Go source file"; got != want {
+               t.Errorf("query error was %q, want %q", got, want)
+       }
+}