src/testUtils: convert a goTest promise chain to async-await

fewer indentation levels is better.

And simplify the pkgMap computation in GOPATH mode a bit -
we don't need goVersion. We don't need to set targets again.

Change-Id: Iaff5df21bce9caff2f6b58e4f3b1201ed10b703c
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/268837
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: Peter Weinberger <pjw@google.com>
diff --git a/src/testUtils.ts b/src/testUtils.ts
index 9c8a311..ceb05af 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -15,7 +15,6 @@
 import {
 	getBinPath,
 	getCurrentGoPath,
-	getGoVersion,
 	getTempFilePath,
 	LineBuffer,
 	resolvePath
@@ -249,169 +248,168 @@
 	if (testconfig.outputChannel) {
 		outputChannel = testconfig.outputChannel;
 	}
-	const tmpCoverPath = getTempFilePath('go-code-cover');
-	const testResult = await new Promise<boolean>(async (resolve, reject) => {
-		// We do not want to clear it if tests are already running, as that could
-		// lose valuable output.
-		if (runningTestProcesses.length < 1) {
-			outputChannel.clear();
-		}
 
-		if (!testconfig.background) {
-			outputChannel.show(true);
-		}
-
-		const testTags: string = getTestTags(testconfig.goConfig);
-		const args: Array<string> = ['test'];
-		const testType: string = testconfig.isBenchmark ? 'Benchmarks' : 'Tests';
-
-		if (testconfig.isBenchmark) {
-			args.push('-benchmem', '-run=^$');
-		} else {
-			args.push('-timeout', testconfig.goConfig['testTimeout']);
-			if (testconfig.applyCodeCoverage) {
-				args.push('-coverprofile=' + tmpCoverPath);
-				const coverMode = testconfig.goConfig['coverMode'];
-				switch (coverMode) {
-					case 'default':
-						break;
-					case 'set': case 'count': case 'atomic':
-						args.push('-covermode', coverMode);
-						break;
-					default:
-						vscode.window.showWarningMessage(
-							`go.coverMode=${coverMode} is illegal. Use 'set', 'count', 'atomic', or 'default'.`
-						);
-				}
-			}
-		}
-		if (testTags && testconfig.flags.indexOf('-tags') === -1) {
-			args.push('-tags', testTags);
-		}
-
-		const testEnvVars = getTestEnvVars(testconfig.goConfig);
-		const goRuntimePath = getBinPath('go');
-
-		if (!goRuntimePath) {
-			vscode.window.showErrorMessage(
-				`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
-			);
-			return Promise.resolve();
-		}
-
-		let targets = testconfig.includeSubDirectories ? ['./...'] : targetArgs(testconfig);
-
-		let currentGoWorkspace = '';
-		let getCurrentPackagePromise = Promise.resolve('');
-		let pkgMapPromise: Promise<Map<string, string> | null> = Promise.resolve(null);
-
-		if (testconfig.isMod) {
-			getCurrentPackagePromise = getCurrentPackage(testconfig.dir);
-			// We need the mapping to get absolute paths for the files in the test output.
-			pkgMapPromise = getNonVendorPackages(testconfig.dir, !!testconfig.includeSubDirectories);
-		} else {  // GOPATH mode
-			currentGoWorkspace = getCurrentGoWorkspaceFromGOPATH(getCurrentGoPath(), testconfig.dir);
-			if (currentGoWorkspace) {
-				getCurrentPackagePromise = Promise.resolve(testconfig.dir.substr(currentGoWorkspace.length + 1));
-			}
-			if (testconfig.includeSubDirectories) {
-				pkgMapPromise = getGoVersion().then((goVersion) => {
-					targets = ['./...'];
-					return null; // We dont need mapping, as we can derive the absolute paths from package path
-				});
-			}
-		}
-
-		Promise.all([pkgMapPromise, getCurrentPackagePromise]).then(
-			([pkgMap, currentPackage]) => {
-				if (!pkgMap) {
-					pkgMap = new Map<string, string>();
-				}
-				// Use the package name to be in the args to enable running tests in symlinked directories
-				// TODO(hyangah): check why modules mode didn't set currentPackage.
-				if (!testconfig.includeSubDirectories && currentPackage) {
-					targets.splice(0, 0, currentPackage);
-				}
-
-				const outTargets = args.slice(0);
-				if (targets.length > 4) {
-					outTargets.push('<long arguments omitted>');
-				} else {
-					outTargets.push(...targets);
-				}
-
-				if (args.includes('-v') && !args.includes('-json')) {
-					args.push('-json');
-				}
-
-				args.push(...targets);
-
-				// ensure that user provided flags are appended last (allow use of -args ...)
-				// ignore user provided -run flag if we are already using it
-				if (args.indexOf('-run') > -1) {
-					removeRunFlag(testconfig.flags);
-				}
-				args.push(...testconfig.flags);
-
-				outTargets.push(...testconfig.flags);
-				outputChannel.appendLine(['Running tool:', goRuntimePath, ...outTargets].join(' '));
-				outputChannel.appendLine('');
-
-				const tp = cp.spawn(goRuntimePath, args, { env: testEnvVars, cwd: testconfig.dir });
-				const outBuf = new LineBuffer();
-				const errBuf = new LineBuffer();
-
-				const testResultLines: string[] = [];
-				const processTestResultLine = args.includes('-json') ?
-					processTestResultLineInJSONMode(pkgMap, currentGoWorkspace, outputChannel) :
-					processTestResultLineInStandardMode(pkgMap, currentGoWorkspace, testResultLines, outputChannel);
-
-				outBuf.onLine((line) => processTestResultLine(line));
-				outBuf.onDone((last) => {
-					if (last) {
-						processTestResultLine(last);
-					}
-
-					// If there are any remaining test result lines, emit them to the output channel.
-					if (testResultLines.length > 0) {
-						testResultLines.forEach((line) => outputChannel.appendLine(line));
-					}
-				});
-
-				// go test emits build errors on stderr, which contain paths relative to the cwd
-				errBuf.onLine((line) => outputChannel.appendLine(expandFilePathInOutput(line, testconfig.dir)));
-				errBuf.onDone((last) => last && outputChannel.appendLine(expandFilePathInOutput(last, testconfig.dir)));
-
-				tp.stdout.on('data', (chunk) => outBuf.append(chunk.toString()));
-				tp.stderr.on('data', (chunk) => errBuf.append(chunk.toString()));
-
-				statusBarItem.show();
-
-				tp.on('close', (code, signal) => {
-					outBuf.done();
-					errBuf.done();
-
-					const index = runningTestProcesses.indexOf(tp, 0);
-					if (index > -1) {
-						runningTestProcesses.splice(index, 1);
-					}
-
-					if (!runningTestProcesses.length) {
-						statusBarItem.hide();
-					}
-
-					resolve(code === 0);
-				});
-
-				runningTestProcesses.push(tp);
-			},
-			(err) => {
-				outputChannel.appendLine(`Error: ${testType} failed.`);
-				outputChannel.appendLine(err);
-				resolve(false);
-			}
+	const goRuntimePath = getBinPath('go');
+	if (!goRuntimePath) {
+		vscode.window.showErrorMessage(
+			`Failed to run "go test" as the "go" binary cannot be found in either GOROOT(${getCurrentGoRoot()}) or PATH(${envPath})`
 		);
-	});
+		return Promise.resolve(false);
+	}
+
+	const tmpCoverPath = testconfig.applyCodeCoverage ? getTempFilePath('go-code-cover') : undefined;
+	// We do not want to clear it if tests are already running, as that could
+	// lose valuable output.
+	if (runningTestProcesses.length < 1) {
+		outputChannel.clear();
+	}
+
+	if (!testconfig.background) {
+		outputChannel.show(true);
+	}
+
+	const testTags: string = getTestTags(testconfig.goConfig);
+	const args: Array<string> = ['test'];
+	const testType: string = testconfig.isBenchmark ? 'Benchmarks' : 'Tests';
+
+	if (testconfig.isBenchmark) {
+		args.push('-benchmem', '-run=^$');
+	} else {
+		args.push('-timeout', testconfig.goConfig['testTimeout']);
+		if (testconfig.applyCodeCoverage) {
+			args.push('-coverprofile=' + tmpCoverPath);
+			const coverMode = testconfig.goConfig['coverMode'];
+			switch (coverMode) {
+				case 'default':
+					break;
+				case 'set': case 'count': case 'atomic':
+					args.push('-covermode', coverMode);
+					break;
+				default:
+					vscode.window.showWarningMessage(
+						`go.coverMode=${coverMode} is illegal. Use 'set', 'count', 'atomic', or 'default'.`
+					);
+			}
+		}
+	}
+
+	if (testTags && testconfig.flags.indexOf('-tags') === -1) {
+		args.push('-tags', testTags);
+	}
+
+	const targets = testconfig.includeSubDirectories ? ['./...'] : targetArgs(testconfig);
+
+	let currentGoWorkspace = '';
+	let getCurrentPackagePromise: Promise<string>;
+	let pkgMapPromise: Promise<Map<string, string>>;
+	if (testconfig.isMod) {
+		getCurrentPackagePromise = getCurrentPackage(testconfig.dir);
+		// We need the mapping to get absolute paths for the files in the test output.
+		pkgMapPromise = getNonVendorPackages(testconfig.dir, !!testconfig.includeSubDirectories);
+	} else {  // GOPATH mode
+		currentGoWorkspace = getCurrentGoWorkspaceFromGOPATH(getCurrentGoPath(), testconfig.dir);
+		getCurrentPackagePromise = Promise.resolve(
+			currentGoWorkspace ? testconfig.dir.substr(currentGoWorkspace.length + 1) : '');
+		// We dont need mapping, as we can derive the absolute paths from package path
+		pkgMapPromise = Promise.resolve(null);
+	}
+
+	let pkgMap = new Map<string, string>();
+	// run go list to populate pkgMap and currentPackage necessary to adjust the test output later.
+	try {
+		const [pkgMap0, currentPackage] = await Promise.all([pkgMapPromise, getCurrentPackagePromise]);
+		if (pkgMap0) {
+			pkgMap = pkgMap0;
+		}
+		// Use the package name to be in the args to enable running tests in symlinked directories
+		// TODO(hyangah): check why modules mode didn't set currentPackage.
+		if (!testconfig.includeSubDirectories && currentPackage) {
+			targets.splice(0, 0, currentPackage);
+		}
+	} catch (err) {
+		outputChannel.appendLine(`warning: failed to compute package mapping... ${err}`);
+	}
+
+	const outTargets = args.slice(0);
+	if (targets.length > 4) {
+		outTargets.push('<long arguments omitted>');
+	} else {
+		outTargets.push(...targets);
+	}
+
+	if (args.includes('-v') && !args.includes('-json')) {
+		args.push('-json');
+	}
+
+	args.push(...targets);
+
+	// ensure that user provided flags are appended last (allow use of -args ...)
+	// ignore user provided -run flag if we are already using it
+	if (args.indexOf('-run') > -1) {
+		removeRunFlag(testconfig.flags);
+	}
+	args.push(...testconfig.flags);
+	outTargets.push(...testconfig.flags);
+
+	outputChannel.appendLine(['Running tool:', goRuntimePath, ...outTargets].join(' '));
+	outputChannel.appendLine('');
+
+	let testResult = false;
+	try {
+		testResult = await new Promise<boolean>(async (resolve, reject) => {
+			const testEnvVars = getTestEnvVars(testconfig.goConfig);
+			const tp = cp.spawn(goRuntimePath, args, { env: testEnvVars, cwd: testconfig.dir });
+			const outBuf = new LineBuffer();
+			const errBuf = new LineBuffer();
+
+			const testResultLines: string[] = [];
+			const processTestResultLine = args.includes('-json') ?
+				processTestResultLineInJSONMode(pkgMap, currentGoWorkspace, outputChannel) :
+				processTestResultLineInStandardMode(pkgMap, currentGoWorkspace, testResultLines, outputChannel);
+
+			outBuf.onLine((line) => processTestResultLine(line));
+			outBuf.onDone((last) => {
+				if (last) {
+					processTestResultLine(last);
+				}
+
+				// If there are any remaining test result lines, emit them to the output channel.
+				if (testResultLines.length > 0) {
+					testResultLines.forEach((line) => outputChannel.appendLine(line));
+				}
+			});
+
+			// go test emits build errors on stderr, which contain paths relative to the cwd
+			errBuf.onLine((line) => outputChannel.appendLine(expandFilePathInOutput(line, testconfig.dir)));
+			errBuf.onDone((last) => last && outputChannel.appendLine(expandFilePathInOutput(last, testconfig.dir)));
+
+			tp.stdout.on('data', (chunk) => outBuf.append(chunk.toString()));
+			tp.stderr.on('data', (chunk) => errBuf.append(chunk.toString()));
+
+			statusBarItem.show();
+
+			tp.on('close', (code, signal) => {
+				outBuf.done();
+				errBuf.done();
+
+				const index = runningTestProcesses.indexOf(tp, 0);
+				if (index > -1) {
+					runningTestProcesses.splice(index, 1);
+				}
+
+				if (!runningTestProcesses.length) {
+					statusBarItem.hide();
+				}
+
+				resolve(code === 0);
+			});
+
+			runningTestProcesses.push(tp);
+		});
+	} catch (err) {
+		outputChannel.appendLine(`Error: ${testType} failed.`);
+		outputChannel.appendLine(err);
+	}
 	if (testconfig.applyCodeCoverage) {
 		await applyCodeCoverageToAllEditors(tmpCoverPath, testconfig.dir);
 	}