codelens: skip run-test and debug-test links and explorer entry for TestMain(*testing.M)

<img width="673" alt="image" src="https://user-images.githubusercontent.com/51177946/160227163-0c38a63d-32a5-43cc-883f-39730df5d421.png">
<img width="674" alt="image" src="https://user-images.githubusercontent.com/51177946/160227174-0ed987ff-f587-4085-b47a-2b13d86e3dbe.png">
<img width="672" alt="image" src="https://user-images.githubusercontent.com/51177946/160227189-59d572fe-8847-4d62-b1ce-b0355fe4fca7.png">
<img width="670" alt="image" src="https://user-images.githubusercontent.com/51177946/160227203-3a8bcc4d-bf0d-486b-8ec1-68821ecdeb36.png">

<img width="871" alt="image" src="https://user-images.githubusercontent.com/51177946/160894934-f772660d-f100-495f-95c0-6d0f88ea0005.png">
<img width="874" alt="image" src="https://user-images.githubusercontent.com/51177946/160895017-c5493dab-99f3-4567-a8f4-b822d2616ad4.png">

Fixes golang/vscode-go#482
Fixes golang/vscode-go#2039

Change-Id: I9b69d762b8a968f94931455d771c03ff6e73182e
GitHub-Last-Rev: 650fb1226a7de0da5b9548b51c38bb112a67539d
GitHub-Pull-Request: golang/vscode-go#2129
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/394136
Reviewed-by: Polina Sokolova <polina@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Polina Sokolova <polina@google.com>
diff --git a/src/testUtils.ts b/src/testUtils.ts
index 5497d8b..53228a5 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -44,6 +44,8 @@
 const testMethodRegex = /^\(([^)]+)\)\.(Test|Test\P{Ll}.*)$/u;
 const benchmarkRegex = /^Benchmark$|^Benchmark\P{Ll}.*/u;
 const fuzzFuncRegx = /^Fuzz$|^Fuzz\P{Ll}.*/u;
+const testMainRegex = /TestMain\(.*\*testing.M\)/;
+
 /**
  * Input to goTest.
  */
@@ -160,6 +162,8 @@
 	return children.filter(
 		(sym) =>
 			(sym.kind === vscode.SymbolKind.Function || sym.kind === vscode.SymbolKind.Method) &&
+			// Skip TestMain(*testing.M) - see https://github.com/golang/vscode-go/issues/482
+			!testMainRegex.test(doc.lineAt(sym.range.start.line).text) &&
 			(testFuncRegex.test(sym.name) || fuzzFuncRegx.test(sym.name) || (testify && testMethodRegex.test(sym.name)))
 	);
 }
diff --git a/test/integration/codelens.test.ts b/test/integration/codelens.test.ts
index 56b825a..f3c07d9 100644
--- a/test/integration/codelens.test.ts
+++ b/test/integration/codelens.test.ts
@@ -143,7 +143,7 @@
 		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, 18, JSON.stringify(codeLenses, null, 2));
+		assert.equal(codeLenses.length, 20, JSON.stringify(codeLenses, null, 2));
 		const found = [] as string[];
 		for (let i = 0; i < codeLenses.length; i++) {
 			const lens = codeLenses[i];
@@ -159,6 +159,7 @@
 			'Test',
 			'Test1Function',
 			'TestFunction',
+			'TestMain',
 			'Test_foobar',
 			'TestΣυνάρτηση',
 			'Test함수'
@@ -184,4 +185,21 @@
 		// Results should match `go test -list`.
 		assert.deepStrictEqual(found, ['Fuzz', 'FuzzFunc', 'TestGo118']);
 	});
+
+	test('Test codelenses skip TestMain', async () => {
+		const uri = vscode.Uri.file(path.join(fixturePath, 'testmain/testmain_test.go'));
+		const testDocument = await vscode.workspace.openTextDocument(uri);
+		const codeLenses = await codeLensProvider.provideCodeLenses(testDocument, cancellationTokenSource.token);
+		assert.equal(codeLenses.length, 4, 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, ['TestNotMain']);
+	});
 });
diff --git a/test/testdata/codelens/codelens2_test.go b/test/testdata/codelens/codelens2_test.go
index 888f534..7984167 100644
--- a/test/testdata/codelens/codelens2_test.go
+++ b/test/testdata/codelens/codelens2_test.go
@@ -5,13 +5,17 @@
 	"testing"
 )
 
-// As of Go1.16, `go test -list` returns
+// As of Go1.16, `go test codelens2_test.go -list .` returns
 //   TestFunction
 //   Test1Function
 //   TestΣυνάρτηση
 //   Test함수
 //   Test_foobar
 //   Test
+//   TestMain
+//   ExampleFunction
+//   Example
+
 func TestFunction(t *testing.T) {
 	t.Log("this is a valid test function")
 }
@@ -44,6 +48,10 @@
 	t.Log("this is a valid test function")
 }
 
+func TestMain(m *testing.T) {
+	m.Log("this is a valid test function")
+}
+
 func ExampleFunction() {
 	fmt.Println("this is a valid example function")
 	// Output:
diff --git a/test/testdata/codelens/testmain/testmain_test.go b/test/testdata/codelens/testmain/testmain_test.go
new file mode 100644
index 0000000..f793815
--- /dev/null
+++ b/test/testdata/codelens/testmain/testmain_test.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+	"testing"
+)
+
+func TestNotMain(t *testing.T) {
+}
+
+func TestMain(m *testing.M) {
+}