[release] src/goTest: detect Fuzz tests and list them in test explorer UI

Detected fuzz functions are classified as a 'fuzz' type, so
we can later add special handling for fuzzing functionality.
Now they are like other test functions.

For golang/vscode-go#2023

Change-Id: I7f3998ec6acf8a766c05380077d41c459d685079
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/380500
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: Suzy Mueller <suzmue@golang.org>
(cherry picked from commit c42b3cd5c35ddcb1d7b3e32e7d6e9841b40f9dfd)
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/383218
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/src/goTest/resolve.ts b/src/goTest/resolve.ts
index 444e333..f1fb8c1 100644
--- a/src/goTest/resolve.ts
+++ b/src/goTest/resolve.ts
@@ -27,7 +27,7 @@
 
 export type ProvideSymbols = (doc: TextDocument, token: CancellationToken) => Thenable<DocumentSymbol[]>;
 
-const testFuncRegex = /^(?<name>(?<kind>Test|Benchmark|Example)($|\P{Ll}.*))/u;
+const testFuncRegex = /^(?<name>(?<kind>Test|Benchmark|Example|Fuzz)($|\P{Ll}.*))/u;
 const testMethodRegex = /^\(\*(?<type>[^)]+)\)\.(?<name>(?<kind>Test)($|\P{Ll}.*))$/u;
 const runTestSuiteRegex = /^\s*suite\.Run\(\w+,\s*(?:&?(?<type1>\w+)\{\}|new\((?<type2>\w+)\))\)/mu;
 
@@ -393,7 +393,7 @@
 		return suite;
 	}
 
-	// Recursively process a Go AST symbol. If the symbol represents a test,
+	// Recursively process a Go AST symbol. If the symbol represents a test, fuzz test,
 	// benchmark, or example function, a test item will be created for it, if one
 	// does not already exist. If the symbol is not a function and contains
 	// children, those children will be processed recursively.
diff --git a/src/goTest/utils.ts b/src/goTest/utils.ts
index 9cc92c4..95e872d 100644
--- a/src/goTest/utils.ts
+++ b/src/goTest/utils.ts
@@ -13,14 +13,15 @@
 // - A 'package' is a folder that contains .go files (and is not a module)
 // - A 'file' is a file ending with _test.go
 // - A 'test' is a Go test, e.g. func TestXxx(t *testing.T)
-// - A 'benchmark' is a Go benchmark, e.g. func BenchmarkXxx(t *testing.T)
+// - A 'benchmark' is a Go benchmark, e.g. func BenchmarkXxx(t *testing.B)
+// - A 'fuzz' is a Fuzz test, e.g., func TestFuzz(f *testing.F)
 // - An 'example' is a Go example, e.g. func ExampleXxx()
 //
 // The top-level test item for a workspace folder is always either a module or a
 // workspace. If the user opens a file (containing tests) that is not contained
 // within any workspace folder, a top-level package will be created as a parent
 // of that file.
-export type GoTestKind = 'module' | 'workspace' | 'package' | 'file' | 'test' | 'benchmark' | 'example';
+export type GoTestKind = 'module' | 'workspace' | 'package' | 'file' | 'test' | 'benchmark' | 'fuzz' | 'example';
 
 export class GoTest {
 	// Constructs an ID for an item. The ID of a test item consists of the URI
@@ -34,6 +35,7 @@
 	// - File:      file:///path/to/mod/file.go?file
 	// - Test:      file:///path/to/mod/file.go?test#TestXxx
 	// - Benchmark: file:///path/to/mod/file.go?benchmark#BenchmarkXxx
+	// - Fuzz:      file:///path/to/mod/file.go?test#FuzzXxx
 	// - Example:   file:///path/to/mod/file.go?example#ExampleXxx
 	static id(uri: vscode.Uri, kind: GoTestKind, name?: string): string {
 		uri = uri.with({ query: kind });
@@ -49,7 +51,7 @@
 		const u = vscode.Uri.parse(id);
 		const kind = u.query as GoTestKind;
 		const name = u.fragment;
-		return { name, kind };
+		return { kind, name };
 	}
 }
 
diff --git a/test/integration/goTest.resolve.test.ts b/test/integration/goTest.resolve.test.ts
index 835f411..a18e65c 100644
--- a/test/integration/goTest.resolve.test.ts
+++ b/test/integration/goTest.resolve.test.ts
@@ -172,6 +172,7 @@
 						func TestFoo(*testing.T) {}
 						func BenchmarkBar(*testing.B) {}
 						func ExampleBaz() {}
+						func FuzzFuss(*testing.F) {}
 					`
 				},
 				item: [
@@ -181,7 +182,8 @@
 				expect: [
 					'file:///src/proj/main_test.go?test#TestFoo',
 					'file:///src/proj/main_test.go?benchmark#BenchmarkBar',
-					'file:///src/proj/main_test.go?example#ExampleBaz'
+					'file:///src/proj/main_test.go?example#ExampleBaz',
+					'file:///src/proj/main_test.go?fuzz#FuzzFuss'
 				]
 			}
 		}
diff --git a/test/integration/goTest.utils.ts b/test/integration/goTest.utils.ts
index 9250f98..710361f 100644
--- a/test/integration/goTest.utils.ts
+++ b/test/integration/goTest.utils.ts
@@ -13,7 +13,7 @@
 export function getSymbols_Regex(doc: TextDocument, token: unknown): Thenable<DocumentSymbol[]> {
 	const syms: DocumentSymbol[] = [];
 	const range = new Range(new Position(0, 0), new Position(0, 0));
-	doc.getText().replace(/^func (Test|Benchmark|Example)([A-Z]\w+)(\(.*\))/gm, (m, type, name, details) => {
+	doc.getText().replace(/^func (Test|Benchmark|Example|Fuzz)([A-Z]\w+)(\(.*\))/gm, (m, type, name, details) => {
 		syms.push(new DocumentSymbol(type + name, details, SymbolKind.Function, range, range));
 		return m;
 	});