diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go index 019ba657d5..832b85e254 100644 --- a/gopls/internal/regtest/diagnostics/diagnostics_test.go +++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go @@ -438,7 +438,7 @@ func TestResolveDiagnosticWithDownload(t *testing.T) { func TestMissingDependency(t *testing.T) { Run(t, testPackageWithRequire, func(t *testing.T, env *Env) { env.OpenFile("print.go") - env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1)) + env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1, false)) }) } @@ -1886,29 +1886,33 @@ package main }) } -// Tests golang/go#45075, a panic in fillreturns breaks diagnostics. +// Tests golang/go#45075: A panic in fillreturns broke diagnostics. +// Expect an error log indicating that fillreturns panicked, as well type +// errors for the broken code. func TestFillReturnsPanic(t *testing.T) { // At tip, the panic no longer reproduces. testenv.SkipAfterGo1Point(t, 16) + const files = ` -- go.mod -- module mod.com -go 1.16 +go 1.15 -- main.go -- package main - func foo() int { return x, nil } - ` Run(t, files, func(t *testing.T, env *Env) { env.OpenFile("main.go") env.Await( - env.DiagnosticAtRegexpWithMessage("main.go", `return x`, "wrong number of return values"), - LogMatching(protocol.Error, `.*analysis fillreturns.*panicked.*`, 2), + OnceMet( + env.DoneWithOpen(), + LogMatching(protocol.Error, `.*analysis fillreturns.*panicked.*`, 1, true), + env.DiagnosticAtRegexpWithMessage("main.go", `return x`, "wrong number of return values"), + ), ) }) } @@ -1931,7 +1935,7 @@ func main() {} env.Await( OnceMet( env.DoneWithOpen(), - LogMatching(protocol.Info, `.*query=\[builtin mod.com/...\].*`, 1), + LogMatching(protocol.Info, `.*query=\[builtin mod.com/...\].*`, 1, false), ), ) }) diff --git a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/watch/watch_test.go index 8d98539981..5b432e18a7 100644 --- a/gopls/internal/regtest/watch/watch_test.go +++ b/gopls/internal/regtest/watch/watch_test.go @@ -395,7 +395,7 @@ package a env.Await( OnceMet( env.DoneWithOpen(), - LogMatching(protocol.Info, "a_unneeded.go", 1), + LogMatching(protocol.Info, "a_unneeded.go", 1, false), ), ) @@ -413,7 +413,7 @@ package a // There should only be one log message containing // a_unneeded.go, from the initial workspace load, which we // check for earlier. If there are more, there's a bug. - LogMatching(protocol.Info, "a_unneeded.go", 1), + LogMatching(protocol.Info, "a_unneeded.go", 1, false), ), EmptyDiagnostics("a/a.go"), ) @@ -429,7 +429,7 @@ package a env.Await( OnceMet( env.DoneWithOpen(), - LogMatching(protocol.Info, "a_unneeded.go", 1), + LogMatching(protocol.Info, "a_unneeded.go", 1, false), ), ) @@ -447,7 +447,7 @@ package a // There should only be one log message containing // a_unneeded.go, from the initial workspace load, which we // check for earlier. If there are more, there's a bug. - LogMatching(protocol.Info, "a_unneeded.go", 1), + LogMatching(protocol.Info, "a_unneeded.go", 1, false), ), EmptyDiagnostics("a/a.go"), ) diff --git a/internal/lsp/regtest/expectation.go b/internal/lsp/regtest/expectation.go index 748e698b07..bd7649d674 100644 --- a/internal/lsp/regtest/expectation.go +++ b/internal/lsp/regtest/expectation.go @@ -79,24 +79,30 @@ func (e SimpleExpectation) Description() string { // OnceMet returns an Expectation that, once the precondition is met, asserts // that mustMeet is met. -func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation { +func OnceMet(precondition Expectation, mustMeets ...Expectation) *SimpleExpectation { check := func(s State) Verdict { switch pre := precondition.Check(s); pre { case Unmeetable: return Unmeetable case Met: - verdict := mustMeet.Check(s) - if verdict != Met { - return Unmeetable + for _, mustMeet := range mustMeets { + verdict := mustMeet.Check(s) + if verdict != Met { + return Unmeetable + } } return Met default: return Unmet } } + var descriptions []string + for _, mustMeet := range mustMeets { + descriptions = append(descriptions, mustMeet.Description()) + } return &SimpleExpectation{ check: check, - description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), mustMeet.Description()), + description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), strings.Join(descriptions, "\n")), } } @@ -303,7 +309,7 @@ func NoErrorLogs() LogExpectation { // LogMatching asserts that the client has received a log message // of type typ matching the regexp re. -func LogMatching(typ protocol.MessageType, re string, count int) LogExpectation { +func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) LogExpectation { rec, err := regexp.Compile(re) if err != nil { panic(err) @@ -315,14 +321,19 @@ func LogMatching(typ protocol.MessageType, re string, count int) LogExpectation found++ } } - if found == count { + // Check for an exact or "at least" match. + if found == count || (found >= count && atLeast) { return Met } return Unmet } + desc := fmt.Sprintf("log message matching %q expected %v times", re, count) + if atLeast { + desc = fmt.Sprintf("log message matching %q expected at least %v times", re, count) + } return LogExpectation{ check: check, - description: fmt.Sprintf("log message matching %q", re), + description: desc, } }