// Copyright 2019 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 cmdtest contains the test suite for the command line behavior of gopls. package cmdtest import ( "bytes" "context" "fmt" "io" "os" "sync" "testing" "golang.org/x/tools/internal/jsonrpc2/servertest" "golang.org/x/tools/internal/lsp/cache" "golang.org/x/tools/internal/lsp/cmd" "golang.org/x/tools/internal/lsp/debug" "golang.org/x/tools/internal/lsp/lsprpc" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/lsp/tests" "golang.org/x/tools/internal/span" "golang.org/x/tools/internal/tool" ) type runner struct { data *tests.Data ctx context.Context options func(*source.Options) normalizers []tests.Normalizer remote string } func TestCommandLine(t *testing.T, testdata string, options func(*source.Options)) { // On Android, the testdata directory is not copied to the runner. if stat, err := os.Stat(testdata); err != nil || !stat.IsDir() { t.Skip("testdata directory not present") } tests.RunTests(t, testdata, false, func(t *testing.T, datum *tests.Data) { ctx := tests.Context(t) ts := NewTestServer(ctx, options) tests.Run(t, NewRunner(datum, ctx, ts.Addr, options), datum) cmd.CloseTestConnections(ctx) }) } func NewTestServer(ctx context.Context, options func(*source.Options)) *servertest.TCPServer { ctx = debug.WithInstance(ctx, "", "") cache := cache.New(ctx, options) ss := lsprpc.NewStreamServer(cache, false) return servertest.NewTCPServer(ctx, ss, nil) } func NewRunner(data *tests.Data, ctx context.Context, remote string, options func(*source.Options)) *runner { return &runner{ data: data, ctx: ctx, options: options, normalizers: tests.CollectNormalizers(data.Exported), remote: remote, } } func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) { //TODO: add command line completions tests when it works } func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { //TODO: add command line completions tests when it works } func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) { //TODO: function extraction not supported on command line } func (r *runner) runGoplsCmd(t testing.TB, args ...string) (string, string) { rStdout, wStdout, err := os.Pipe() if err != nil { t.Fatal(err) } oldStdout := os.Stdout rStderr, wStderr, err := os.Pipe() if err != nil { t.Fatal(err) } oldStderr := os.Stderr stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} var wg sync.WaitGroup wg.Add(2) go func() { io.Copy(stdout, rStdout) wg.Done() }() go func() { io.Copy(stderr, rStderr) wg.Done() }() os.Stdout, os.Stderr = wStdout, wStderr app := cmd.New("gopls-test", r.data.Config.Dir, r.data.Exported.Config.Env, r.options) remote := r.remote err = tool.Run(tests.Context(t), app, append([]string{fmt.Sprintf("-remote=internal@%s", remote)}, args...)) if err != nil { fmt.Fprint(os.Stderr, err) } wStdout.Close() wStderr.Close() wg.Wait() os.Stdout, os.Stderr = oldStdout, oldStderr rStdout.Close() rStderr.Close() return stdout.String(), stderr.String() } // NormalizeGoplsCmd runs the gopls command and normalizes its output. func (r *runner) NormalizeGoplsCmd(t testing.TB, args ...string) (string, string) { stdout, stderr := r.runGoplsCmd(t, args...) return r.Normalize(stdout), r.Normalize(stderr) } func (r *runner) Normalize(s string) string { return tests.Normalize(s, r.normalizers) } func (r *runner) NormalizePrefix(s string) string { return tests.NormalizePrefix(s, r.normalizers) }