1 // Copyright 2020 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.
14 "golang.org/x/tools/internal/lsp/fake"
17 // dummyCompletionFunction to test manually configured completion using CLI.
18 func dummyCompletionFunction() { const s = "placeholder"; fmt.Printf("%s", s) }
20 type completionBenchOptions struct {
21 workdir, file, locationRegexp string
23 // hook to run edits before initial completion, not supported for manually
24 // configured completions.
25 preCompletionEdits func(*Env)
28 var completionOptions = completionBenchOptions{}
31 flag.StringVar(&completionOptions.workdir, "completion_workdir", "", "directory to run completion benchmarks in")
32 flag.StringVar(&completionOptions.file, "completion_file", "", "relative path to the file to complete in")
33 flag.StringVar(&completionOptions.locationRegexp, "completion_regexp", "", "regexp location to complete at")
34 flag.BoolVar(&completionOptions.printResults, "completion_print_results", false, "whether to print completion results")
37 func benchmarkCompletion(options completionBenchOptions, t *testing.T) {
38 if completionOptions.workdir == "" {
39 t.Skip("-completion_workdir not configured, skipping benchmark")
42 opts := stressTestOptions(options.workdir)
44 // Completion gives bad results if IWL is not yet complete, so we must await
45 // it first (and therefore need hooks).
46 opts = append(opts, SkipHooks(false))
48 withOptions(opts...).run(t, "", func(t *testing.T, env *Env) {
49 env.OpenFile(options.file)
51 // Run edits required for this completion.
52 if options.preCompletionEdits != nil {
53 options.preCompletionEdits(env)
56 // Add a comment as a marker at the start of the file, we'll replace
57 // this in every iteration to trigger type checking and hence emulate
58 // a more real world scenario.
59 env.EditBuffer(options.file, fake.Edit{Text: "// 0\n"})
61 // Run a completion to make sure the system is warm.
62 pos := env.RegexpSearch(options.file, options.locationRegexp)
63 completions := env.Completion(options.file, pos)
65 if options.printResults {
66 fmt.Println("Results:")
67 for i := 0; i < len(completions.Items); i++ {
68 fmt.Printf("\t%d. %v\n", i, completions.Items[i])
72 results := testing.Benchmark(func(b *testing.B) {
73 for i := 0; i < b.N; i++ {
75 env.RegexpReplace(options.file, `\/\/ \d*`, fmt.Sprintf("// %d", i))
77 // explicitly garbage collect since we don't want to count this
78 // time in completion benchmarks.
84 env.Completion(options.file, pos)
88 printBenchmarkResults(results)
92 // endPosInBuffer returns the position for last character in the buffer for
94 func endPosInBuffer(env *Env, name string) fake.Pos {
95 buffer := env.Editor.BufferText(name)
96 lines := strings.Split(buffer, "\n")
97 numLines := len(lines)
101 Column: len([]rune(lines[numLines-1])),
105 // Benchmark completion at a specified file and location. When no CLI options
106 // are specified, this test is skipped.
107 // To Run (from x/tools/gopls) against the dummy function above:
108 // go test -v ./internal/regtest -run=TestBenchmarkConfiguredCompletion
109 // -completion_workdir="$HOME/Developer/tools"
110 // -completion_file="gopls/internal/regtest/completion_bench_test.go"
111 // -completion_regexp="dummyCompletionFunction.*fmt\.Printf\(\"%s\", s(\))"
112 func TestBenchmarkConfiguredCompletion(t *testing.T) {
113 benchmarkCompletion(completionOptions, t)
116 // To run (from x/tools/gopls):
117 // go test -v ./internal/regtest -run TestBenchmark<>Completion
118 // -completion_workdir="$HOME/Developer/tools"
119 // where <> is one of the tests below. completion_workdir should be path to
120 // x/tools on your system.
122 // Benchmark struct completion in tools codebase.
123 func TestBenchmarkStructCompletion(t *testing.T) {
124 file := "internal/lsp/cache/session.go"
126 preCompletionEdits := func(env *Env) {
128 originalBuffer := env.Editor.BufferText(file)
129 env.EditBuffer(file, fake.Edit{
130 End: endPosInBuffer(env, file),
131 Text: originalBuffer + "\nvar testVariable map[string]bool = Session{}.\n",
135 benchmarkCompletion(completionBenchOptions{
136 workdir: completionOptions.workdir,
138 locationRegexp: `var testVariable map\[string\]bool = Session{}(\.)`,
139 preCompletionEdits: preCompletionEdits,
140 printResults: completionOptions.printResults,
144 // Benchmark import completion in tools codebase.
145 func TestBenchmarkImportCompletion(t *testing.T) {
146 benchmarkCompletion(completionBenchOptions{
147 workdir: completionOptions.workdir,
148 file: "internal/lsp/source/completion/completion.go",
149 locationRegexp: `go\/()`,
150 printResults: completionOptions.printResults,
154 // Benchmark slice completion in tools codebase.
155 func TestBenchmarkSliceCompletion(t *testing.T) {
156 file := "internal/lsp/cache/session.go"
158 preCompletionEdits := func(env *Env) {
160 originalBuffer := env.Editor.BufferText(file)
161 env.EditBuffer(file, fake.Edit{
162 End: endPosInBuffer(env, file),
163 Text: originalBuffer + "\nvar testVariable []byte = \n",
167 benchmarkCompletion(completionBenchOptions{
168 workdir: completionOptions.workdir,
170 locationRegexp: `var testVariable \[\]byte (=)`,
171 preCompletionEdits: preCompletionEdits,
172 printResults: completionOptions.printResults,
176 // Benchmark deep completion in function call in tools codebase.
177 func TestBenchmarkFuncDeepCompletion(t *testing.T) {
178 file := "internal/lsp/source/completion/completion.go"
180 func (c *completer) _() {
181 c.inference.kindMatches(c.)
184 preCompletionEdits := func(env *Env) {
186 originalBuffer := env.Editor.BufferText(file)
187 env.EditBuffer(file, fake.Edit{
188 End: endPosInBuffer(env, file),
189 Text: originalBuffer + fileContent,
193 benchmarkCompletion(completionBenchOptions{
194 workdir: completionOptions.workdir,
196 locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`,
197 preCompletionEdits: preCompletionEdits,
198 printResults: completionOptions.printResults,