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