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  }