gopls/internal/test/integration/completion: make test go1.26-robust

As the API of the testing package evolves, TestFuzzFunc, which uses
it to exercise completions, keeps breaking. This CL introduces
optional items to make it easier to fix the tests as it breaks
each time the API evolves.

CL 696399 added (*testing.F).Artifact to go1.26, causing the test
to break spuriously. See also golang/go#74987, about the same
issue when go1.25 added the Attr method.

Updates golang/go#74987

Change-Id: I9653dae29bff8a976f7a4d9eb4b785b4b28b05f5
Reviewed-on: https://go-review.googlesource.com/c/tools/+/702077
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/test/integration/completion/completion18_test.go b/gopls/internal/test/integration/completion/completion18_test.go
index 748f0db..f0fd1db 100644
--- a/gopls/internal/test/integration/completion/completion18_test.go
+++ b/gopls/internal/test/integration/completion/completion18_test.go
@@ -58,6 +58,8 @@
 }
 
 func TestFuzzFunc(t *testing.T) {
+	// The behavior under test is derived from the std module,
+	// not the x/tools/internal/stdlib linked into gopls.
 	testenv.NeedsGoCommand1Point(t, 25) // go1.25 added TBF.Attr
 
 	// use the example from the package documentation
@@ -103,7 +105,9 @@
 		offset uint32 // UTF16 length from the beginning of pat to what the user just typed
 		want   []string
 	}{
-		{"a_test.go", "f.Ad", 3, []string{"Add", "Attr"}},
+		// To avoid breaking these assertions as the "testing" package evolves,
+		// use an optional (?) suffix for newer symbols.
+		{"a_test.go", "f.Ad", 3, []string{"Add", "Attr", "Artifact?"}}, // Attr is 1.25, Artifact is 1.26
 		{"c_test.go", " f.F", 4, []string{"Failed"}},
 		{"c_test.go", "f.N", 3, []string{"Name"}},
 		{"b_test.go", "f.F", 3, []string{"Fuzz(func(t *testing.T, a []byte)", "Fail", "FailNow",
diff --git a/gopls/internal/test/integration/completion/completion_test.go b/gopls/internal/test/integration/completion/completion_test.go
index 7386e71..1d0230d 100644
--- a/gopls/internal/test/integration/completion/completion_test.go
+++ b/gopls/internal/test/integration/completion/completion_test.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 	"os"
+	"slices"
 	"sort"
 	"strings"
 	"testing"
@@ -258,6 +259,12 @@
 	})
 }
 
+// compareCompletionLabels returns a non-empty string reporting the
+// difference (if any) between the labels of the actual completion
+// items (gotItems) and the expected list (want).
+//
+// A want item with a "?" suffix is optional.
+//
 // TODO(rfindley): audit/clean up call sites for this helper, to ensure
 // consistent test errors.
 func compareCompletionLabels(want []string, gotItems []protocol.CompletionItem) string {
@@ -274,6 +281,19 @@
 		return "" // treat nil and the empty slice as equivalent
 	}
 
+	// A 'want' item with a '?' suffix is optional, to ease
+	// migration across versions. Remove any that are not present
+	// in the 'got' set.
+	out := want[:0] // in-place compaction
+	for _, item := range want {
+		item, optional := strings.CutSuffix(item, "?")
+		if optional && !slices.Contains(got, item) {
+			continue // optional item is missing
+		}
+		out = append(out, item)
+	}
+	want = out
+
 	if diff := cmp.Diff(want, got); diff != "" {
 		return fmt.Sprintf("completion item mismatch (-want +got):\n%s", diff)
 	}