src/testUtils: tighten test function detection regex

According to https://golang.org/pkg/testing/
test function names should be of form
  func TestXxx(*testing.T)
where Xxx does not start with a lowercase letter.

Go uses unicode for this. Mimic the behavior encoded in
https://github.com/golang/go/blob/117b1c84d3678a586c168a5f7f2f0a750c27f0c2/src/cmd/go/internal/load/test.go#L48
by using unicode regexp and property.

Fixes golang/vscode-go#1417

Change-Id: I3fa510e5e567722b5b09c2618034686a9c5c3d90
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/309631
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/src/testUtils.ts b/src/testUtils.ts
index 2433a4b..77e812d 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -32,9 +32,14 @@
  */
 const runningTestProcesses: cp.ChildProcess[] = [];
 
-const testFuncRegex = /^Test.*|^Example.*/;
-const testMethodRegex = /^\(([^)]+)\)\.(Test.*)$/;
-const benchmarkRegex = /^Benchmark.*/;
+// https://github.com/golang/go/blob/117b1c84d3678a586c168a5f7f2f0a750c27f0c2/src/cmd/go/internal/load/test.go#L487
+// uses !unicode.isLower to find test/example/benchmark functions.
+// There could be slight difference between \P{Ll} (not lowercase letter)
+// & go unicode package's uppercase detection. But hopefully
+// these will be replaced by gopls's codelens computation soon.
+const testFuncRegex = /^Test\P{Ll}.*|^Example\P{Ll}.*/u;
+const testMethodRegex = /^\(([^)]+)\)\.(Test\P{Ll}.*)$/u;
+const benchmarkRegex = /^Benchmark\P{Ll}.*/u;
 
 /**
  * Input to goTest.
diff --git a/test/integration/codelens.test.ts b/test/integration/codelens.test.ts
index bee6b27..bbe2f61 100644
--- a/test/integration/codelens.test.ts
+++ b/test/integration/codelens.test.ts
@@ -129,4 +129,21 @@
 			assert.equal(codeLenses[i].command.command, wantCommands[i]);
 		}
 	});
+
+	test('Test codelenses include only valid test function names', async () => {
+		const uri = vscode.Uri.file(path.join(fixturePath, 'codelens2_test.go'));
+		const benchmarkDocument = await vscode.workspace.openTextDocument(uri);
+		const codeLenses = await codeLensProvider.provideCodeLenses(benchmarkDocument, cancellationTokenSource.token);
+		assert.equal(codeLenses.length, 12, JSON.stringify(codeLenses, null, 2));
+		const found = [] as string[];
+		for (let i = 0; i < codeLenses.length; i++) {
+			const lens = codeLenses[i];
+			if (lens.command.command === 'go.test.cursor') {
+				found.push(lens.command.arguments[0].functionName);
+			}
+		}
+		found.sort();
+		// Results should match `go test -list`.
+		assert.deepStrictEqual(found, ['Test1Function', 'TestFunction', 'Test_foobar', 'TestΣυνάρτηση', 'Test함수']);
+	});
 });
diff --git a/test/testdata/codelens/codelens2_test.go b/test/testdata/codelens/codelens2_test.go
new file mode 100644
index 0000000..405f4ef
--- /dev/null
+++ b/test/testdata/codelens/codelens2_test.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+	"testing"
+)
+
+// As of Go1.16, `go test -list` returns
+//   TestFunction
+//   Test1Function
+//   TestΣυνάρτηση
+//   Test함수
+//   Test_foobar
+func TestFunction(t *testing.T) {
+	t.Log("this is a valid test function")
+}
+
+func Testfunction(t *testing.T) {
+	t.Fatal("this is not a valid test function")
+}
+
+func Test1Function(t *testing.T) {
+	t.Log("this is an acceptable test function")
+}
+
+func TestΣυνάρτηση(t *testing.T) {
+	t.Log("this is a valid test function")
+}
+
+func Testσυνάρτηση(t *testing.T) {
+	t.Fatal("this is not a valid test function")
+}
+
+func Test함수(t *testing.T) {
+	t.Log("this is a valid test function")
+}
+
+func Test_foobar(t *testing.T) {
+	t.Log("this is an acceptable test function")
+}
\ No newline at end of file