Skip to content

Commit 13850b3

Browse files
committed
gopls/internal/lsp/regtest: move @symbol to new marker tests
The new @symbol marker (which is used once per file) formats all symbols, sans position information, one per line, sorts them, and compares against the golden. This is less fuss in the source file itself, but we have lost the location assertions. (The old tests used a combination of the "semantic annotations" approach for some things and "big ol' diff" approach for locations. I don't usually like the latter, but perhaps the small quantity of tests and the low rate of churn means that it's fine.) Details: - The fake Editor now sets HierarchicalDocumentSymbolSupport for parity with the LSP tests, but really we should test both cases. - Added check for missing sections in txtar. Change-Id: Idda94fc7473dc55f2e3f7b66e3e61134dd111fc9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/479736 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Alan Donovan <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent e9188f8 commit 13850b3

File tree

12 files changed

+254
-266
lines changed

12 files changed

+254
-266
lines changed

gopls/internal/lsp/fake/editor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ func (e *Editor) initialize(ctx context.Context) error {
265265
"deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary",
266266
}
267267

268+
// The LSP tests have historically enabled this flag,
269+
// but really we should test both ways for older editors.
270+
params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = true
271+
268272
// This is a bit of a hack, since the fake editor doesn't actually support
269273
// watching changed files that match a specific glob pattern. However, the
270274
// editor does send didChangeWatchedFiles notifications, so set this to

gopls/internal/lsp/lsp_test.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"testing"
1717

1818
"github.com/google/go-cmp/cmp"
19-
"github.com/google/go-cmp/cmp/cmpopts"
2019
"golang.org/x/tools/gopls/internal/lsp/cache"
2120
"golang.org/x/tools/gopls/internal/lsp/command"
2221
"golang.org/x/tools/gopls/internal/lsp/protocol"
@@ -992,43 +991,6 @@ func applyTextDocumentEdits(r *runner, edits []protocol.DocumentChanges) (map[sp
992991
return res, nil
993992
}
994993

995-
func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
996-
params := &protocol.DocumentSymbolParams{
997-
TextDocument: protocol.TextDocumentIdentifier{
998-
URI: protocol.URIFromSpanURI(uri),
999-
},
1000-
}
1001-
got, err := r.server.DocumentSymbol(r.ctx, params)
1002-
if err != nil {
1003-
t.Fatal(err)
1004-
}
1005-
1006-
symbols := make([]protocol.DocumentSymbol, len(got))
1007-
for i, s := range got {
1008-
s, ok := s.(protocol.DocumentSymbol)
1009-
if !ok {
1010-
t.Fatalf("%v: wanted []DocumentSymbols but got %v", uri, got)
1011-
}
1012-
symbols[i] = s
1013-
}
1014-
1015-
// Sort by position to make it easier to find errors.
1016-
sortSymbols := func(s []protocol.DocumentSymbol) {
1017-
sort.Slice(s, func(i, j int) bool {
1018-
return protocol.CompareRange(s[i].SelectionRange, s[j].SelectionRange) < 0
1019-
})
1020-
}
1021-
sortSymbols(expectedSymbols)
1022-
sortSymbols(symbols)
1023-
1024-
// Ignore 'Range' here as it is difficult (impossible?) to express
1025-
// multi-line ranges in the packagestest framework.
1026-
ignoreRange := cmpopts.IgnoreFields(protocol.DocumentSymbol{}, "Range")
1027-
if diff := cmp.Diff(expectedSymbols, symbols, ignoreRange); diff != "" {
1028-
t.Errorf("mismatching symbols (-want +got)\n%s", diff)
1029-
}
1030-
}
1031-
1032994
func (r *runner) WorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) {
1033995
matcher := tests.WorkspaceSymbolsTestTypeToMatcher(typ)
1034996

gopls/internal/lsp/regtest/marker.go

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"fmt"
1414
"go/token"
1515
"io/fs"
16+
"log"
1617
"os"
1718
"path"
1819
"path/filepath"
@@ -45,6 +46,10 @@ var update = flag.Bool("update", false, "if set, update test data during marker
4546
// RunMarkerTests runs "marker" tests in the given test data directory.
4647
// (In practice: ../../regtest/marker/testdata)
4748
//
49+
// Use this command to run the tests:
50+
//
51+
// $ go test ./gopls/internal/regtest/marker [-update]
52+
//
4853
// A marker test uses the '//@' marker syntax of the x/tools/go/expect package
4954
// to annotate source code with various information such as locations and
5055
// arguments of LSP operations to be executed by the test. The syntax following
@@ -153,6 +158,18 @@ var update = flag.Bool("update", false, "if set, update test data during marker
153158
// first location and asserts that the result is the set of 'want' locations.
154159
// The first want location must be the declaration (assumedly unique).
155160
//
161+
// - symbol(golden): makes a textDocument/documentSymbol request
162+
// for the enclosing file, formats the response with one symbol
163+
// per line, sorts it, and compares against the named golden file.
164+
// Each line is of the form:
165+
//
166+
// dotted.symbol.name kind "detail" +n lines
167+
//
168+
// where the "+n lines" part indicates that the declaration spans
169+
// several lines. The test otherwise makes no attempt to check
170+
// location information. There is no point to using more than one
171+
// @symbol marker in a given file.
172+
//
156173
// # Argument conversion
157174
//
158175
// Marker arguments are first parsed by the go/expect package, which accepts
@@ -242,7 +259,7 @@ var update = flag.Bool("update", false, "if set, update test data during marker
242259
// - reorganize regtest packages (and rename to just 'test'?)
243260
// - Rename the files .txtar.
244261
//
245-
// Existing marker tests to port:
262+
// Existing marker tests (in ../testdata) to port:
246263
// - CallHierarchy
247264
// - CodeLens
248265
// - Diagnostics
@@ -260,11 +277,9 @@ var update = flag.Bool("update", false, "if set, update test data during marker
260277
// - SemanticTokens
261278
// - FunctionExtractions
262279
// - MethodExtractions
263-
// - Definitions
264280
// - Highlights
265281
// - Renames
266282
// - PrepareRenames
267-
// - Symbols
268283
// - InlayHints
269284
// - WorkspaceSymbols
270285
// - Signatures
@@ -476,6 +491,7 @@ var markerFuncs = map[string]markerFunc{
476491
"rename": makeMarkerFunc(renameMarker),
477492
"renameerr": makeMarkerFunc(renameErrMarker),
478493
"suggestedfix": makeMarkerFunc(suggestedfixMarker),
494+
"symbol": makeMarkerFunc(symbolMarker),
479495
"refs": makeMarkerFunc(refsMarker),
480496
}
481497

@@ -598,6 +614,13 @@ func loadMarkerTests(dir string) ([]*markerTest, error) {
598614

599615
func loadMarkerTest(name string, content []byte) (*markerTest, error) {
600616
archive := txtar.Parse(content)
617+
if len(archive.Files) == 0 {
618+
return nil, fmt.Errorf("txtar file has no '-- filename --' sections")
619+
}
620+
if bytes.Contains(archive.Comment, []byte("\n-- ")) {
621+
// This check is conservative, but the comment is only a comment.
622+
return nil, fmt.Errorf("ill-formed '-- filename --' header in comment")
623+
}
601624
test := &markerTest{
602625
name: name,
603626
fset: token.NewFileSet(),
@@ -650,6 +673,13 @@ func loadMarkerTest(name string, content []byte) (*markerTest, error) {
650673
test.notes = append(test.notes, notes...)
651674
test.files[file.Name] = file.Data
652675
}
676+
677+
// Print a warning if we see what looks like "-- filename --"
678+
// without the second "--". It's not necessarily wrong,
679+
// but it should almost never appear in our test inputs.
680+
if bytes.Contains(file.Data, []byte("\n-- ")) {
681+
log.Printf("ill-formed '-- filename --' header in %s?", file.Name)
682+
}
653683
}
654684

655685
return test, nil
@@ -792,6 +822,11 @@ func (c *marker) sprintf(format string, args ...interface{}) string {
792822
return fmt.Sprintf(format, args2...)
793823
}
794824

825+
// uri returns the URI of the file containing the marker.
826+
func (mark marker) uri() protocol.DocumentURI {
827+
return mark.run.env.Sandbox.Workdir.URI(mark.run.test.fset.File(mark.note.Pos).Name())
828+
}
829+
795830
// fmtLoc formats the given pos in the context of the test, using
796831
// archive-relative paths for files and including the line number in the full
797832
// archive file.
@@ -1405,6 +1440,75 @@ func implementationMarker(mark marker, src protocol.Location, want ...protocol.L
14051440
}
14061441
}
14071442

1443+
// symbolMarker implements the @symbol marker.
1444+
func symbolMarker(mark marker, golden *Golden) {
1445+
// Retrieve information about all symbols in this file.
1446+
symbols, err := mark.run.env.Editor.Server.DocumentSymbol(mark.run.env.Ctx, &protocol.DocumentSymbolParams{
1447+
TextDocument: protocol.TextDocumentIdentifier{URI: mark.uri()},
1448+
})
1449+
if err != nil {
1450+
mark.errorf("DocumentSymbol request failed: %v", err)
1451+
return
1452+
}
1453+
1454+
// Format symbols one per line, sorted (in effect) by first column, a dotted name.
1455+
var lines []string
1456+
for _, symbol := range symbols {
1457+
// Each result element is a union of (legacy)
1458+
// SymbolInformation and (new) DocumentSymbol,
1459+
// so we ascertain which one and then transcode.
1460+
data, err := json.Marshal(symbol)
1461+
if err != nil {
1462+
mark.run.env.T.Fatal(err)
1463+
}
1464+
if _, ok := symbol.(map[string]interface{})["location"]; ok {
1465+
// This case is not reached because Editor initialization
1466+
// enables HierarchicalDocumentSymbolSupport.
1467+
// TODO(adonovan): test this too.
1468+
var sym protocol.SymbolInformation
1469+
if err := json.Unmarshal(data, &sym); err != nil {
1470+
mark.run.env.T.Fatal(err)
1471+
}
1472+
mark.errorf("fake Editor doesn't support SymbolInformation")
1473+
1474+
} else {
1475+
var sym protocol.DocumentSymbol // new hierarchical hotness
1476+
if err := json.Unmarshal(data, &sym); err != nil {
1477+
mark.run.env.T.Fatal(err)
1478+
}
1479+
1480+
// Print each symbol in the response tree.
1481+
var visit func(sym protocol.DocumentSymbol, prefix []string)
1482+
visit = func(sym protocol.DocumentSymbol, prefix []string) {
1483+
var out strings.Builder
1484+
out.WriteString(strings.Join(prefix, "."))
1485+
fmt.Fprintf(&out, " %q", sym.Detail)
1486+
if delta := sym.Range.End.Line - sym.Range.Start.Line; delta > 0 {
1487+
fmt.Fprintf(&out, " +%d lines", delta)
1488+
}
1489+
lines = append(lines, out.String())
1490+
1491+
for _, child := range sym.Children {
1492+
visit(child, append(prefix, child.Name))
1493+
}
1494+
}
1495+
visit(sym, []string{sym.Name})
1496+
}
1497+
}
1498+
sort.Strings(lines)
1499+
lines = append(lines, "") // match trailing newline in .txtar file
1500+
got := []byte(strings.Join(lines, "\n"))
1501+
1502+
// Compare with golden.
1503+
want, ok := golden.Get(mark.run.env.T, "", got)
1504+
if !ok {
1505+
mark.errorf("%s: missing golden file @%s", mark.note.Name, golden.id)
1506+
} else if diff := cmp.Diff(string(got), string(want)); diff != "" {
1507+
mark.errorf("%s: unexpected output: got:\n%s\nwant:\n%s\ndiff:\n%s",
1508+
mark.note.Name, got, want, diff)
1509+
}
1510+
}
1511+
14081512
// compareLocations returns an error message if got and want are not
14091513
// the same set of locations. The marker is used only for fmtLoc.
14101514
func compareLocations(mark marker, got, want []protocol.Location) error {

gopls/internal/lsp/testdata/summary.txt.golden

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ HighlightsCount = 69
2222
InlayHintsCount = 4
2323
RenamesCount = 41
2424
PrepareRenamesCount = 7
25-
SymbolsCount = 1
2625
WorkspaceSymbolsCount = 20
2726
SignaturesCount = 33
2827
LinksCount = 7

gopls/internal/lsp/testdata/summary_go1.18.txt.golden

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ HighlightsCount = 69
2222
InlayHintsCount = 5
2323
RenamesCount = 48
2424
PrepareRenamesCount = 7
25-
SymbolsCount = 2
2625
WorkspaceSymbolsCount = 20
2726
SignaturesCount = 33
2827
LinksCount = 7

gopls/internal/lsp/testdata/symbols/go1.18.go

Lines changed: 0 additions & 16 deletions
This file was deleted.

gopls/internal/lsp/testdata/symbols/go1.18.go.golden

Lines changed: 0 additions & 7 deletions
This file was deleted.

gopls/internal/lsp/testdata/symbols/main.go

Lines changed: 0 additions & 91 deletions
This file was deleted.

0 commit comments

Comments
 (0)