X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Fcmd%2Fguru%2Fguru_test.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Fcmd%2Fguru%2Fguru_test.go;h=0699db9ee0c4bd4df6813847961a01bd35376975;hb=4d07c77cf4d78cab8639e13ddc3c22495e585b0b;hp=0000000000000000000000000000000000000000;hpb=b3950616b54221c40a7dab9099bda675007e5b6e;p=dotfiles%2F.git 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 index 00000000..0699db9e --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/cmd/guru/guru_test.go @@ -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) + } +}