Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / cmd / test / cmdtest.go
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         "path/filepath"
15         "strconv"
16         "strings"
17         "sync"
18         "testing"
19
20         "golang.org/x/tools/internal/jsonrpc2/servertest"
21         "golang.org/x/tools/internal/lsp/cache"
22         "golang.org/x/tools/internal/lsp/cmd"
23         "golang.org/x/tools/internal/lsp/debug"
24         "golang.org/x/tools/internal/lsp/lsprpc"
25         "golang.org/x/tools/internal/lsp/protocol"
26         "golang.org/x/tools/internal/lsp/source"
27         "golang.org/x/tools/internal/lsp/tests"
28         "golang.org/x/tools/internal/span"
29         "golang.org/x/tools/internal/tool"
30 )
31
32 type runner struct {
33         data        *tests.Data
34         ctx         context.Context
35         options     func(*source.Options)
36         normalizers []normalizer
37         remote      string
38 }
39
40 type normalizer struct {
41         path     string
42         slashed  string
43         escaped  string
44         fragment string
45 }
46
47 func TestCommandLine(t *testing.T, testdata string, options func(*source.Options)) {
48         // On Android, the testdata directory is not copied to the runner.
49         if stat, err := os.Stat(testdata); err != nil || !stat.IsDir() {
50                 t.Skip("testdata directory not present")
51         }
52         tests.RunTests(t, testdata, false, func(t *testing.T, datum *tests.Data) {
53                 ctx := tests.Context(t)
54                 ts := NewTestServer(ctx, options)
55                 tests.Run(t, NewRunner(datum, ctx, ts.Addr, options), datum)
56                 cmd.CloseTestConnections(ctx)
57         })
58 }
59
60 func NewTestServer(ctx context.Context, options func(*source.Options)) *servertest.TCPServer {
61         ctx = debug.WithInstance(ctx, "", "")
62         cache := cache.New(ctx, options)
63         ss := lsprpc.NewStreamServer(cache, false)
64         return servertest.NewTCPServer(ctx, ss, nil)
65 }
66
67 func NewRunner(data *tests.Data, ctx context.Context, remote string, options func(*source.Options)) *runner {
68         r := &runner{
69                 data:        data,
70                 ctx:         ctx,
71                 options:     options,
72                 normalizers: make([]normalizer, 0, len(data.Exported.Modules)),
73                 remote:      remote,
74         }
75         // build the path normalizing patterns
76         for _, m := range data.Exported.Modules {
77                 for fragment := range m.Files {
78                         n := normalizer{
79                                 path:     data.Exported.File(m.Name, fragment),
80                                 fragment: fragment,
81                         }
82                         if n.slashed = filepath.ToSlash(n.path); n.slashed == n.path {
83                                 n.slashed = ""
84                         }
85                         quoted := strconv.Quote(n.path)
86                         if n.escaped = quoted[1 : len(quoted)-1]; n.escaped == n.path {
87                                 n.escaped = ""
88                         }
89                         r.normalizers = append(r.normalizers, n)
90                 }
91         }
92         return r
93 }
94
95 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
96         //TODO: add command line completions tests when it works
97 }
98
99 func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
100         //TODO: add command line completions tests when it works
101 }
102
103 func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
104         //TODO: add command line completions tests when it works
105 }
106
107 func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
108         //TODO: add command line completions tests when it works
109 }
110
111 func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
112         //TODO: add command line completions tests when it works
113 }
114
115 func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
116         //TODO: add command line completions tests when it works
117 }
118
119 func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
120         //TODO: add command line completions tests when it works
121 }
122
123 func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
124         //TODO: add command line completions tests when it works
125 }
126
127 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {
128         //TODO: function extraction not supported on command line
129 }
130
131 func (r *runner) runGoplsCmd(t testing.TB, args ...string) (string, string) {
132         rStdout, wStdout, err := os.Pipe()
133         if err != nil {
134                 t.Fatal(err)
135         }
136         oldStdout := os.Stdout
137         rStderr, wStderr, err := os.Pipe()
138         if err != nil {
139                 t.Fatal(err)
140         }
141         oldStderr := os.Stderr
142         stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
143         var wg sync.WaitGroup
144         wg.Add(2)
145         go func() {
146                 io.Copy(stdout, rStdout)
147                 wg.Done()
148         }()
149         go func() {
150                 io.Copy(stderr, rStderr)
151                 wg.Done()
152         }()
153         os.Stdout, os.Stderr = wStdout, wStderr
154         app := cmd.New("gopls-test", r.data.Config.Dir, r.data.Exported.Config.Env, r.options)
155         remote := r.remote
156         err = tool.Run(tests.Context(t),
157                 app,
158                 append([]string{fmt.Sprintf("-remote=internal@%s", remote)}, args...))
159         if err != nil {
160                 fmt.Fprint(os.Stderr, err)
161         }
162         wStdout.Close()
163         wStderr.Close()
164         wg.Wait()
165         os.Stdout, os.Stderr = oldStdout, oldStderr
166         rStdout.Close()
167         rStderr.Close()
168         return stdout.String(), stderr.String()
169 }
170
171 // NormalizeGoplsCmd runs the gopls command and normalizes its output.
172 func (r *runner) NormalizeGoplsCmd(t testing.TB, args ...string) (string, string) {
173         stdout, stderr := r.runGoplsCmd(t, args...)
174         return r.Normalize(stdout), r.Normalize(stderr)
175 }
176
177 // NormalizePrefix normalizes a single path at the front of the input string.
178 func (r *runner) NormalizePrefix(s string) string {
179         for _, n := range r.normalizers {
180                 if t := strings.TrimPrefix(s, n.path); t != s {
181                         return n.fragment + t
182                 }
183                 if t := strings.TrimPrefix(s, n.slashed); t != s {
184                         return n.fragment + t
185                 }
186                 if t := strings.TrimPrefix(s, n.escaped); t != s {
187                         return n.fragment + t
188                 }
189         }
190         return s
191 }
192
193 // Normalize replaces all paths present in s with just the fragment portion
194 // this is used to make golden files not depend on the temporary paths of the files
195 func (r *runner) Normalize(s string) string {
196         type entry struct {
197                 path     string
198                 index    int
199                 fragment string
200         }
201         match := make([]entry, 0, len(r.normalizers))
202         // collect the initial state of all the matchers
203         for _, n := range r.normalizers {
204                 index := strings.Index(s, n.path)
205                 if index >= 0 {
206                         match = append(match, entry{n.path, index, n.fragment})
207                 }
208                 if n.slashed != "" {
209                         index := strings.Index(s, n.slashed)
210                         if index >= 0 {
211                                 match = append(match, entry{n.slashed, index, n.fragment})
212                         }
213                 }
214                 if n.escaped != "" {
215                         index := strings.Index(s, n.escaped)
216                         if index >= 0 {
217                                 match = append(match, entry{n.escaped, index, n.fragment})
218                         }
219                 }
220         }
221         // result should be the same or shorter than the input
222         buf := bytes.NewBuffer(make([]byte, 0, len(s)))
223         last := 0
224         for {
225                 // find the nearest path match to the start of the buffer
226                 next := -1
227                 nearest := len(s)
228                 for i, c := range match {
229                         if c.index >= 0 && nearest > c.index {
230                                 nearest = c.index
231                                 next = i
232                         }
233                 }
234                 // if there are no matches, we copy the rest of the string and are done
235                 if next < 0 {
236                         buf.WriteString(s[last:])
237                         return buf.String()
238                 }
239                 // we have a match
240                 n := &match[next]
241                 // copy up to the start of the match
242                 buf.WriteString(s[last:n.index])
243                 // skip over the filename
244                 last = n.index + len(n.path)
245                 // add in the fragment instead
246                 buf.WriteString(n.fragment)
247                 // see what the next match for this path is
248                 n.index = strings.Index(s[last:], n.path)
249                 if n.index >= 0 {
250                         n.index += last
251                 }
252         }
253 }