gopls/internal/regtest: clean up TestFillReturnsPanic

The test doesn't necessarily need to require exactly 2 log messages, so
the match doesn't need to be so exact.

Updates golang/go#46546

Change-Id: I6ec5dee820c76c41db7b1d4bad3925fc7afe25e4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/324760
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
index 019ba65..832b85e 100644
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go
@@ -438,7 +438,7 @@
 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 @@
 	})
 }
 
-// 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 @@
 		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 8d98539..5b432e1 100644
--- a/gopls/internal/regtest/watch/watch_test.go
+++ b/gopls/internal/regtest/watch/watch_test.go
@@ -395,7 +395,7 @@
 			env.Await(
 				OnceMet(
 					env.DoneWithOpen(),
-					LogMatching(protocol.Info, "a_unneeded.go", 1),
+					LogMatching(protocol.Info, "a_unneeded.go", 1, false),
 				),
 			)
 
@@ -413,7 +413,7 @@
 					// 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 @@
 			env.Await(
 				OnceMet(
 					env.DoneWithOpen(),
-					LogMatching(protocol.Info, "a_unneeded.go", 1),
+					LogMatching(protocol.Info, "a_unneeded.go", 1, false),
 				),
 			)
 
@@ -447,7 +447,7 @@
 					// 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 748e698..bd7649d 100644
--- a/internal/lsp/regtest/expectation.go
+++ b/internal/lsp/regtest/expectation.go
@@ -79,24 +79,30 @@
 
 // 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 @@
 
 // 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 @@
 				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,
 	}
 }