1
0
mirror of https://github.com/golang/go synced 2024-11-18 12:54:44 -07:00
go/internal/lsp/regtest/bench_test.go
Rob Findley 06cc1d0a36 internal/lsp/regtest: add benchmarks for IWL and completion
Add additional benchmarks following the pattern of symbol benchmarks.
One for initial workspace load, and another for completion.

Change-Id: Iba826b188cb81dffabb1b08287dc7b76250dc54c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/250802
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-08-27 17:51:12 +00:00

160 lines
5.1 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"
"testing"
"golang.org/x/tools/internal/lsp"
"golang.org/x/tools/internal/lsp/fake"
"golang.org/x/tools/internal/lsp/protocol"
)
var iwlBench = struct {
workdir string
}{}
func init() {
flag.StringVar(&iwlBench.workdir, "iwl_workdir", "", "if set, run IWL benchmark in this directory")
}
func TestBenchmarkIWL(t *testing.T) {
if iwlBench.workdir == "" {
t.Skip("-iwl_workdir not configured")
}
opts := stressTestOptions(iwlBench.workdir)
// Don't skip hooks, so that we can wait for IWL.
opts = append(opts, SkipHooks(false))
b := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
withOptions(opts...).run(t, "", func(t *testing.T, env *Env) {
env.Await(
CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1),
)
})
}
})
printBench(b)
}
var symbolBench = struct {
workdir, query, matcher, style string
printResults bool
}{}
func init() {
flag.StringVar(&symbolBench.workdir, "symbol_workdir", "", "if set, run symbol benchmark in this directory")
flag.StringVar(&symbolBench.query, "symbol_query", "test", "symbol query to use in benchmark")
flag.StringVar(&symbolBench.matcher, "symbol_matcher", "", "symbol matcher to use in benchmark")
flag.StringVar(&symbolBench.style, "symbol_style", "", "symbol style to use in benchmark")
flag.BoolVar(&symbolBench.printResults, "symbol_print_results", false, "whether to print symbol query results")
}
func TestBenchmarkSymbols(t *testing.T) {
if symbolBench.workdir == "" {
t.Skip("-symbol_workdir not configured")
}
opts := stressTestOptions(symbolBench.workdir)
conf := fake.EditorConfig{}
if symbolBench.matcher != "" {
conf.SymbolMatcher = &symbolBench.matcher
}
if symbolBench.style != "" {
conf.SymbolStyle = &symbolBench.style
}
opts = append(opts, WithEditorConfig(conf))
withOptions(opts...).run(t, "", func(t *testing.T, env *Env) {
// We can't Await in this test, since we have disabled hooks. Instead, run
// one symbol request to completion to ensure all necessary cache entries
// are populated.
results, err := env.Editor.Server.Symbol(env.Ctx, &protocol.WorkspaceSymbolParams{
Query: symbolBench.query,
})
if err != nil {
t.Fatal(err)
}
if symbolBench.printResults {
fmt.Println("Results:")
for i := 0; i < len(results); i++ {
fmt.Printf("\t%d. %s\n", i, results[i].Name)
}
}
b := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := env.Editor.Server.Symbol(env.Ctx, &protocol.WorkspaceSymbolParams{
Query: symbolBench.query,
}); err != nil {
t.Fatal(err)
}
}
})
printBench(b)
})
}
func printBench(b testing.BenchmarkResult) {
fmt.Println("Benchmark stats:")
fmt.Println(b.String())
fmt.Println(b.MemString())
}
func dummyCompletionBenchmarkFunction() { const s = "placeholder"; fmt.Printf("%s", s) }
var completionBench = struct {
workdir, fileName, locationRegexp string
printResults bool
}{}
func init() {
flag.StringVar(&completionBench.workdir, "completion_workdir", "", "if set run completion benchmark in this directory (other benchmark flags expect an x/tools dir)")
flag.StringVar(&completionBench.fileName, "completion_file", "internal/lsp/regtest/bench_test.go", "relative path to the file to complete")
flag.StringVar(&completionBench.locationRegexp, "completion_regexp", `dummyCompletionBenchmarkFunction.*fmt\.Printf\("%s", s(\))`, "regexp location to complete at")
flag.BoolVar(&completionBench.printResults, "completion_print_results", false, "whether to print completion results")
}
func TestBenchmarkCompletion(t *testing.T) {
if completionBench.workdir == "" {
t.Skip("-completion_workdir not configured")
}
opts := stressTestOptions(completionBench.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.Await(
CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1),
)
env.OpenFile(completionBench.fileName)
params := &protocol.CompletionParams{}
params.Context.TriggerCharacter = "s"
params.Context.TriggerKind = protocol.TriggerCharacter
params.TextDocument.URI = env.Sandbox.Workdir.URI(completionBench.fileName)
params.Position = env.RegexpSearch(completionBench.fileName, completionBench.locationRegexp).ToProtocolPosition()
// Run one completion to make sure everything is warm.
list, err := env.Editor.Server.Completion(env.Ctx, params)
if err != nil {
t.Fatal(err)
}
if completionBench.printResults {
fmt.Println("Results:")
for i := 0; i < len(list.Items); i++ {
fmt.Printf("\t%d. %v\n", i, list.Items[i])
}
}
b := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := env.Editor.Server.Completion(env.Ctx, params)
if err != nil {
t.Fatal(err)
}
}
})
printBench(b)
})
}