mirror of https://github.com/golang/go.git
201 lines
6.3 KiB
Go
201 lines
6.3 KiB
Go
// Copyright 2020 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 regtest
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/tools/internal/lsp/fake"
|
|
)
|
|
|
|
// dummyCompletionFunction to test manually configured completion using CLI.
|
|
func dummyCompletionFunction() { const s = "placeholder"; fmt.Printf("%s", s) }
|
|
|
|
type completionBenchOptions struct {
|
|
workdir, file, locationRegexp string
|
|
printResults bool
|
|
// hook to run edits before initial completion, not supported for manually
|
|
// configured completions.
|
|
preCompletionEdits func(*Env)
|
|
}
|
|
|
|
var completionOptions = completionBenchOptions{}
|
|
|
|
func init() {
|
|
flag.StringVar(&completionOptions.workdir, "completion_workdir", "", "directory to run completion benchmarks in")
|
|
flag.StringVar(&completionOptions.file, "completion_file", "", "relative path to the file to complete in")
|
|
flag.StringVar(&completionOptions.locationRegexp, "completion_regexp", "", "regexp location to complete at")
|
|
flag.BoolVar(&completionOptions.printResults, "completion_print_results", false, "whether to print completion results")
|
|
}
|
|
|
|
func benchmarkCompletion(options completionBenchOptions, t *testing.T) {
|
|
if completionOptions.workdir == "" {
|
|
t.Skip("-completion_workdir not configured, skipping benchmark")
|
|
}
|
|
|
|
opts := stressTestOptions(options.workdir)
|
|
|
|
// Completion gives bad results if IWL is not yet complete, so we must await
|
|
// it first (and therefore need hooks).
|
|
opts = append(opts, SkipHooks(false))
|
|
|
|
withOptions(opts...).run(t, "", func(t *testing.T, env *Env) {
|
|
env.OpenFile(options.file)
|
|
|
|
// Run edits required for this completion.
|
|
if options.preCompletionEdits != nil {
|
|
options.preCompletionEdits(env)
|
|
}
|
|
|
|
// Add a comment as a marker at the start of the file, we'll replace
|
|
// this in every iteration to trigger type checking and hence emulate
|
|
// a more real world scenario.
|
|
env.EditBuffer(options.file, fake.Edit{Text: "// 0\n"})
|
|
|
|
// Run a completion to make sure the system is warm.
|
|
pos := env.RegexpSearch(options.file, options.locationRegexp)
|
|
completions := env.Completion(options.file, pos)
|
|
|
|
if options.printResults {
|
|
fmt.Println("Results:")
|
|
for i := 0; i < len(completions.Items); i++ {
|
|
fmt.Printf("\t%d. %v\n", i, completions.Items[i])
|
|
}
|
|
}
|
|
|
|
results := testing.Benchmark(func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
env.RegexpReplace(options.file, `\/\/ \d*`, fmt.Sprintf("// %d", i))
|
|
|
|
// explicitly garbage collect since we don't want to count this
|
|
// time in completion benchmarks.
|
|
if i%10 == 0 {
|
|
runtime.GC()
|
|
}
|
|
b.StartTimer()
|
|
|
|
env.Completion(options.file, pos)
|
|
}
|
|
})
|
|
|
|
printBenchmarkResults(results)
|
|
})
|
|
}
|
|
|
|
// endPosInBuffer returns the position for last character in the buffer for
|
|
// the given file.
|
|
func endPosInBuffer(env *Env, name string) fake.Pos {
|
|
buffer := env.Editor.BufferText(name)
|
|
lines := strings.Split(buffer, "\n")
|
|
numLines := len(lines)
|
|
|
|
return fake.Pos{
|
|
Line: numLines - 1,
|
|
Column: len([]rune(lines[numLines-1])),
|
|
}
|
|
}
|
|
|
|
// Benchmark completion at a specified file and location. When no CLI options
|
|
// are specified, this test is skipped.
|
|
// To Run (from x/tools/gopls) against the dummy function above:
|
|
// go test -v ./internal/regtest -run=TestBenchmarkConfiguredCompletion
|
|
// -completion_workdir="$HOME/Developer/tools"
|
|
// -completion_file="gopls/internal/regtest/completion_bench_test.go"
|
|
// -completion_regexp="dummyCompletionFunction.*fmt\.Printf\(\"%s\", s(\))"
|
|
func TestBenchmarkConfiguredCompletion(t *testing.T) {
|
|
benchmarkCompletion(completionOptions, t)
|
|
}
|
|
|
|
// To run (from x/tools/gopls):
|
|
// go test -v ./internal/regtest -run TestBenchmark<>Completion
|
|
// -completion_workdir="$HOME/Developer/tools"
|
|
// where <> is one of the tests below. completion_workdir should be path to
|
|
// x/tools on your system.
|
|
|
|
// Benchmark struct completion in tools codebase.
|
|
func TestBenchmarkStructCompletion(t *testing.T) {
|
|
file := "internal/lsp/cache/session.go"
|
|
|
|
preCompletionEdits := func(env *Env) {
|
|
env.OpenFile(file)
|
|
originalBuffer := env.Editor.BufferText(file)
|
|
env.EditBuffer(file, fake.Edit{
|
|
End: endPosInBuffer(env, file),
|
|
Text: originalBuffer + "\nvar testVariable map[string]bool = Session{}.\n",
|
|
})
|
|
}
|
|
|
|
benchmarkCompletion(completionBenchOptions{
|
|
workdir: completionOptions.workdir,
|
|
file: file,
|
|
locationRegexp: `var testVariable map\[string\]bool = Session{}(\.)`,
|
|
preCompletionEdits: preCompletionEdits,
|
|
printResults: completionOptions.printResults,
|
|
}, t)
|
|
}
|
|
|
|
// Benchmark import completion in tools codebase.
|
|
func TestBenchmarkImportCompletion(t *testing.T) {
|
|
benchmarkCompletion(completionBenchOptions{
|
|
workdir: completionOptions.workdir,
|
|
file: "internal/lsp/source/completion/completion.go",
|
|
locationRegexp: `go\/()`,
|
|
printResults: completionOptions.printResults,
|
|
}, t)
|
|
}
|
|
|
|
// Benchmark slice completion in tools codebase.
|
|
func TestBenchmarkSliceCompletion(t *testing.T) {
|
|
file := "internal/lsp/cache/session.go"
|
|
|
|
preCompletionEdits := func(env *Env) {
|
|
env.OpenFile(file)
|
|
originalBuffer := env.Editor.BufferText(file)
|
|
env.EditBuffer(file, fake.Edit{
|
|
End: endPosInBuffer(env, file),
|
|
Text: originalBuffer + "\nvar testVariable []byte = \n",
|
|
})
|
|
}
|
|
|
|
benchmarkCompletion(completionBenchOptions{
|
|
workdir: completionOptions.workdir,
|
|
file: file,
|
|
locationRegexp: `var testVariable \[\]byte (=)`,
|
|
preCompletionEdits: preCompletionEdits,
|
|
printResults: completionOptions.printResults,
|
|
}, t)
|
|
}
|
|
|
|
// Benchmark deep completion in function call in tools codebase.
|
|
func TestBenchmarkFuncDeepCompletion(t *testing.T) {
|
|
file := "internal/lsp/source/completion/completion.go"
|
|
fileContent := `
|
|
func (c *completer) _() {
|
|
c.inference.kindMatches(c.)
|
|
}
|
|
`
|
|
preCompletionEdits := func(env *Env) {
|
|
env.OpenFile(file)
|
|
originalBuffer := env.Editor.BufferText(file)
|
|
env.EditBuffer(file, fake.Edit{
|
|
End: endPosInBuffer(env, file),
|
|
Text: originalBuffer + fileContent,
|
|
})
|
|
}
|
|
|
|
benchmarkCompletion(completionBenchOptions{
|
|
workdir: completionOptions.workdir,
|
|
file: file,
|
|
locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`,
|
|
preCompletionEdits: preCompletionEdits,
|
|
printResults: completionOptions.printResults,
|
|
}, t)
|
|
}
|