github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/internal/lsp/cmd/test/cmdtest.go (about) 1 // Copyright 2019 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 // Package cmdtest contains the test suite for the command line behavior of gopls. 6 package cmdtest 7 8 import ( 9 "bytes" 10 "context" 11 "flag" 12 "fmt" 13 "io" 14 "os" 15 "sync" 16 "testing" 17 18 "github.com/jhump/golang-x-tools/internal/jsonrpc2/servertest" 19 "github.com/jhump/golang-x-tools/internal/lsp/cache" 20 "github.com/jhump/golang-x-tools/internal/lsp/cmd" 21 "github.com/jhump/golang-x-tools/internal/lsp/debug" 22 "github.com/jhump/golang-x-tools/internal/lsp/lsprpc" 23 "github.com/jhump/golang-x-tools/internal/lsp/protocol" 24 "github.com/jhump/golang-x-tools/internal/lsp/source" 25 "github.com/jhump/golang-x-tools/internal/lsp/tests" 26 "github.com/jhump/golang-x-tools/internal/span" 27 "github.com/jhump/golang-x-tools/internal/tool" 28 ) 29 30 type runner struct { 31 data *tests.Data 32 ctx context.Context 33 options func(*source.Options) 34 normalizers []tests.Normalizer 35 remote string 36 } 37 38 func TestCommandLine(t *testing.T, testdata string, options func(*source.Options)) { 39 // On Android, the testdata directory is not copied to the runner. 40 if stat, err := os.Stat(testdata); err != nil || !stat.IsDir() { 41 t.Skip("testdata directory not present") 42 } 43 tests.RunTests(t, testdata, false, func(t *testing.T, datum *tests.Data) { 44 ctx := tests.Context(t) 45 ts := NewTestServer(ctx, options) 46 tests.Run(t, NewRunner(datum, ctx, ts.Addr, options), datum) 47 cmd.CloseTestConnections(ctx) 48 }) 49 } 50 51 func NewTestServer(ctx context.Context, options func(*source.Options)) *servertest.TCPServer { 52 ctx = debug.WithInstance(ctx, "", "") 53 cache := cache.New(options) 54 ss := lsprpc.NewStreamServer(cache, false) 55 return servertest.NewTCPServer(ctx, ss, nil) 56 } 57 58 func NewRunner(data *tests.Data, ctx context.Context, remote string, options func(*source.Options)) *runner { 59 return &runner{ 60 data: data, 61 ctx: ctx, 62 options: options, 63 normalizers: tests.CollectNormalizers(data.Exported), 64 remote: remote, 65 } 66 } 67 68 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) { 69 //TODO: add command line completions tests when it works 70 } 71 72 func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 73 //TODO: add command line completions tests when it works 74 } 75 76 func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) { 77 //TODO: add command line completions tests when it works 78 } 79 80 func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 81 //TODO: add command line completions tests when it works 82 } 83 84 func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 85 //TODO: add command line completions tests when it works 86 } 87 88 func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 89 //TODO: add command line completions tests when it works 90 } 91 92 func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 93 //TODO: add command line completions tests when it works 94 } 95 96 func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { 97 //TODO: add command line completions tests when it works 98 } 99 100 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) { 101 //TODO: function extraction not supported on command line 102 } 103 104 func (r *runner) MethodExtraction(t *testing.T, start span.Span, end span.Span) { 105 //TODO: function extraction not supported on command line 106 } 107 108 func (r *runner) AddImport(t *testing.T, uri span.URI, expectedImport string) { 109 //TODO: import addition not supported on command line 110 } 111 112 func (r *runner) Hover(t *testing.T, spn span.Span, info string) { 113 //TODO: hovering not supported on command line 114 } 115 116 func (r *runner) runGoplsCmd(t testing.TB, args ...string) (string, string) { 117 rStdout, wStdout, err := os.Pipe() 118 if err != nil { 119 t.Fatal(err) 120 } 121 oldStdout := os.Stdout 122 rStderr, wStderr, err := os.Pipe() 123 if err != nil { 124 t.Fatal(err) 125 } 126 oldStderr := os.Stderr 127 stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} 128 var wg sync.WaitGroup 129 wg.Add(2) 130 go func() { 131 io.Copy(stdout, rStdout) 132 wg.Done() 133 }() 134 go func() { 135 io.Copy(stderr, rStderr) 136 wg.Done() 137 }() 138 os.Stdout, os.Stderr = wStdout, wStderr 139 app := cmd.New("gopls-test", r.data.Config.Dir, r.data.Exported.Config.Env, r.options) 140 remote := r.remote 141 s := flag.NewFlagSet(app.Name(), flag.ExitOnError) 142 err = tool.Run(tests.Context(t), s, 143 app, 144 append([]string{fmt.Sprintf("-remote=internal@%s", remote)}, args...)) 145 if err != nil { 146 fmt.Fprint(os.Stderr, err) 147 } 148 wStdout.Close() 149 wStderr.Close() 150 wg.Wait() 151 os.Stdout, os.Stderr = oldStdout, oldStderr 152 rStdout.Close() 153 rStderr.Close() 154 return stdout.String(), stderr.String() 155 } 156 157 // NormalizeGoplsCmd runs the gopls command and normalizes its output. 158 func (r *runner) NormalizeGoplsCmd(t testing.TB, args ...string) (string, string) { 159 stdout, stderr := r.runGoplsCmd(t, args...) 160 return r.Normalize(stdout), r.Normalize(stderr) 161 } 162 163 func (r *runner) Normalize(s string) string { 164 return tests.Normalize(s, r.normalizers) 165 } 166 167 func (r *runner) NormalizePrefix(s string) string { 168 return tests.NormalizePrefix(s, r.normalizers) 169 }