sync: merge microsoft/vscode-go@b83ad81 to master

resolved conflicts caused by 8f5548a and b83ad81
that were committed to golang/vscode-go first, but had to be
changed to address comments during the upstream PR review.

Change-Id: I767e726e63c1dcff950745c753b1050010d35cbf
diff --git a/package-lock.json b/package-lock.json
index 8673f8c..7de5183 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,51 @@
         "js-tokens": "^4.0.0"
       }
     },
+    "@sinonjs/commons": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz",
+      "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==",
+      "dev": true,
+      "requires": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "@sinonjs/fake-timers": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
+      "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0"
+      }
+    },
+    "@sinonjs/formatio": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz",
+      "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1",
+        "@sinonjs/samsam": "^5.0.2"
+      }
+    },
+    "@sinonjs/samsam": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz",
+      "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.6.0",
+        "lodash.get": "^4.4.2",
+        "type-detect": "^4.0.8"
+      }
+    },
+    "@sinonjs/text-encoding": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+      "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+      "dev": true
+    },
     "@types/events": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -74,6 +119,12 @@
       "integrity": "sha512-1OzrNb4RuAzIT7wHSsgZRlMBlNsJl+do6UblR7JMW4oB7bbR+uBEYtUh7gEc/jM84GGilh68lSOokyM/zNUlBA==",
       "dev": true
     },
+    "@types/sinon": {
+      "version": "7.5.2",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.2.tgz",
+      "integrity": "sha512-T+m89VdXj/eidZyejvmoP9jivXgBDdkOSBVQjU9kF349NEx10QdPNGxHeZUaj1IlJ32/ewdyXJjnJxyxJroYwg==",
+      "dev": true
+    },
     "@types/vscode": {
       "version": "1.41.0",
       "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.41.0.tgz",
@@ -823,6 +874,12 @@
       "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
     },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+      "dev": true
+    },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -909,6 +966,12 @@
         "verror": "1.10.0"
       }
     },
+    "just-extend": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz",
+      "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==",
+      "dev": true
+    },
     "locate-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
@@ -924,6 +987,12 @@
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
       "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
     },
+    "lodash.get": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+      "dev": true
+    },
     "log-symbols": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -1046,6 +1115,19 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
       "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
     },
+    "nise": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz",
+      "integrity": "sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0",
+        "@sinonjs/fake-timers": "^6.0.0",
+        "@sinonjs/text-encoding": "^0.7.1",
+        "just-extend": "^4.0.2",
+        "path-to-regexp": "^1.7.0"
+      }
+    },
     "node-environment-flags": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
@@ -1159,6 +1241,15 @@
       "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
       "dev": true
     },
+    "path-to-regexp": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+      "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+      "dev": true,
+      "requires": {
+        "isarray": "0.0.1"
+      }
+    },
     "performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -1268,6 +1359,38 @@
       "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
       "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="
     },
+    "sinon": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.1.tgz",
+      "integrity": "sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0",
+        "@sinonjs/fake-timers": "^6.0.0",
+        "@sinonjs/formatio": "^5.0.1",
+        "@sinonjs/samsam": "^5.0.3",
+        "diff": "^4.0.2",
+        "nise": "^4.0.1",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -1365,6 +1488,11 @@
         }
       }
     },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
+    },
     "tslib": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
@@ -1422,6 +1550,12 @@
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
       "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
     },
+    "type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true
+    },
     "typescript": {
       "version": "3.7.5",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
diff --git a/package.json b/package.json
index b08c3a0..23348e0 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
     "json-rpc2": "^1.0.2",
     "moment": "^2.24.0",
     "semver": "^6.3.0",
+    "tree-kill": "^1.2.2",
     "vscode-debugadapter": "^1.36.0",
     "vscode-debugprotocol": "^1.36.0",
     "vscode-extension-telemetry": "^0.1.2",
@@ -61,14 +62,16 @@
     "@types/mocha": "^5.2.7",
     "@types/node": "^12.7.2",
     "@types/semver": "^6.0.1",
+    "@types/sinon": "^7.5.2",
     "@types/vscode": "^1.25.0",
     "fs-extra": "^8.1.0",
     "glob": "^7.1.4",
     "mocha": "^6.2.0",
+    "prettier": "^1.16.4",
+    "sinon": "^9.0.1",
     "tslint": "^5.19.0",
     "typescript": "^3.7.2",
-    "vscode-test": "^1.2.3",
-    "prettier": "^1.16.4"
+    "vscode-test": "^1.2.3"
   },
   "engines": {
     "vscode": "^1.41.0"
diff --git a/scripts/terminateProcess.sh b/scripts/terminateProcess.sh
deleted file mode 100755
index 9b06884..0000000
--- a/scripts/terminateProcess.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-terminateTree() {
-	for cpid in $(/usr/bin/pgrep -P $1); do
-		terminateTree $cpid
-	done
-	kill -9 $1 > /dev/null 2>&1
-}
-
-for pid in $*; do
-	terminateTree $pid
-done
diff --git a/src/debugAdapter/goDebug.ts b/src/debugAdapter/goDebug.ts
index 5ddfc90..4310ed7 100644
--- a/src/debugAdapter/goDebug.ts
+++ b/src/debugAdapter/goDebug.ts
@@ -9,9 +9,9 @@
 import { Client, RPCConnection } from 'json-rpc2';
 import * as os from 'os';
 import * as path from 'path';
+import kill = require('tree-kill');
 import * as util from 'util';
 import {
-	Breakpoint,
 	DebugSession,
 	Handles,
 	InitializedEvent,
@@ -661,7 +661,7 @@
 
 		const isLocalDebugging: boolean = this.request === 'launch' && !!this.debugProcess;
 		const forceCleanup = async () => {
-			killTree(this.debugProcess.pid);
+			kill(this.debugProcess.pid, (err) => console.log('Error killing debug process: ' + err));
 			await removeFile(this.localDebugeePath);
 		};
 		return new Promise(async (resolve) => {
@@ -1927,28 +1927,6 @@
 	return Math.floor(Math.random() * (high - low) + low);
 }
 
-function killTree(processId: number): void {
-	if (process.platform === 'win32') {
-		const TASK_KILL = 'C:\\Windows\\System32\\taskkill.exe';
-
-		// when killing a process in Windows its child processes are *not* killed but become root processes.
-		// Therefore we use TASKKILL.EXE
-		try {
-			execSync(`${TASK_KILL} /F /T /PID ${processId}`);
-		} catch (err) {
-			logError(`Error killing process tree: ${err.toString() || ''}`);
-		}
-	} else {
-		// on linux and OS X we kill all direct and indirect child processes as well
-		try {
-			const cmd = path.join(__dirname, '../../../scripts/terminateProcess.sh');
-			spawnSync(cmd, [processId.toString()]);
-		} catch (err) {
-			logError(`Error killing process tree: ${err.toString() || ''}`);
-		}
-	}
-}
-
 async function removeFile(filePath: string): Promise<void> {
 	try {
 		const fileExists = await fsAccess(filePath)
diff --git a/src/goInstallTools.ts b/src/goInstallTools.ts
index 524bd3e..dc9859d 100644
--- a/src/goInstallTools.ts
+++ b/src/goInstallTools.ts
@@ -278,7 +278,7 @@
 			const failures = res.filter((x) => x != null);
 			if (failures.length === 0) {
 				if (containsString(missing, 'gopls')) {
-					outputChannel.appendLine('Reload VS Code window to use the Go language server.');
+					outputChannel.appendLine('Reload VS Code window to use the Go language server');
 				}
 				outputChannel.appendLine('All tools successfully installed. You are ready to Go :).');
 				return;
diff --git a/src/goLanguageServer.ts b/src/goLanguageServer.ts
index 6031a34..f2dcb92 100644
--- a/src/goLanguageServer.ts
+++ b/src/goLanguageServer.ts
@@ -17,10 +17,20 @@
 	HandleDiagnosticsSignature,
 	LanguageClient,
 	ProvideCompletionItemsSignature,
+	ProvideDefinitionSignature,
 	ProvideDocumentFormattingEditsSignature,
+	ProvideDocumentHighlightsSignature,
 	ProvideDocumentLinksSignature,
+	ProvideDocumentSymbolsSignature,
+	ProvideHoverSignature,
+	ProvideReferencesSignature,
+	ProvideRenameEditsSignature,
+	ProvideSignatureHelpSignature,
+	ProvideWorkspaceSymbolsSignature,
 	RevealOutputChannelOn
 } from 'vscode-languageclient';
+import { ProvideImplementationSignature } from 'vscode-languageclient/lib/implementation';
+import { ProvideTypeDefinitionSignature } from 'vscode-languageclient/lib/typeDefinition';
 import WebRequest = require('web-request');
 import { GoDefinitionProvider } from './goDeclaration';
 import { GoHoverProvider } from './goExtraInfo';
@@ -334,11 +344,13 @@
 }
 
 /**
+ *
  * If the user has enabled the language server, return the absolute path to the
  * correct binary. If the required tool is not available, prompt the user to
  * install it. Only gopls is officially supported.
  */
 export function getLanguageServerToolPath(): string {
+	// If language server is not enabled, return
 	const goConfig = getGoConfig();
 	if (!goConfig['useLanguageServer']) {
 		return;
@@ -351,43 +363,32 @@
 		);
 		return;
 	}
-
-	// Determine which language server the user has selected.
-	// gopls is the default choice.
-	let languageServerOfChoice = 'gopls';
-	if (goConfig['alternateTools']) {
-		const goplsAlternate = goConfig['alternateTools']['gopls'];
-
-		// Check if the user has set the deprecated "go-langserver" setting.
-		if (goConfig['alternateTools']['go-langserver']) {
-			vscode.window.showErrorMessage(`The "go.alternateTools" setting for "go-langserver" has been deprecated.
-Please set "gopls" instead, and then reload the VS Code window.`);
+	// Get the path to gopls (getBinPath checks for alternate tools).
+	const goplsBinaryPath = getBinPath('gopls');
+	if (path.isAbsolute(goplsBinaryPath)) {
+		return goplsBinaryPath;
+	}
+	const alternateTools = goConfig['alternateTools'];
+	if (alternateTools) {
+		// The user's alternate language server was not found.
+		const goplsAlternate = alternateTools['gopls'];
+		if (goplsAlternate) {
+			vscode.window.showErrorMessage(
+				`Cannot find the alternate tool ${goplsAlternate} configured for gopls.
+Please install it and reload this VS Code window.`
+			);
 			return;
 		}
-		if (goplsAlternate) {
-			if (typeof goplsAlternate !== 'string') {
-				vscode.window.showErrorMessage(`Unexpected type for "go.alternateTools" setting for "gopls": ${typeof goplsAlternate}.`);
-				return;
-			}
-			languageServerOfChoice = getToolFromToolPath(goplsAlternate);
+		// Check if the user has the deprecated "go-langserver" setting.
+		// Suggest deleting it if the alternate tool is gopls.
+		if (alternateTools['go-langserver']) {
+			vscode.window.showErrorMessage(`Support for "go-langserver" has been deprecated.
+The recommended language server is gopls. Delete the alternate tool setting for "go-langserver" to use gopls, or change "go-langserver" to "gopls" in your settings.json and reload the VS Code window.`);
 		}
-	}
-	// Get the path to the language server binary.
-	const languageServerBinPath = getBinPath(languageServerOfChoice);
-	if (path.isAbsolute(languageServerBinPath)) {
-		return languageServerBinPath;
-	}
-
-	// Installation of gopls is supported. Other language servers must be installed manually.
-	if (languageServerOfChoice !== 'gopls') {
-		vscode.window.showErrorMessage(
-			`Cannot find the language server ${languageServerOfChoice}. Please install it and reload this VS Code window.`
-		);
 		return;
 	}
-
-	// Otherwise, prompt the user to install the language server.
-	promptForMissingTool(languageServerOfChoice);
+	// Prompt the user to install gopls.
+	promptForMissingTool('gopls');
 }
 
 function allFoldersHaveSameGopath(): boolean {
diff --git a/src/goTools.ts b/src/goTools.ts
index 0121a4a..9030a5b 100644
--- a/src/goTools.ts
+++ b/src/goTools.ts
@@ -122,7 +122,6 @@
 	maybeAddTool(goConfig['lintTool']);
 
 	// Add the language server for Go versions > 1.10 if user has choosen to do so.
-	// Respect the go.alternateTools setting.
 	if (goConfig['useLanguageServer'] && goVersion.gt('1.10')) {
 		maybeAddTool('gopls');
 	}
diff --git a/src/util.ts b/src/util.ts
index 933b059..99b4d21 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -8,6 +8,7 @@
 import os = require('os');
 import path = require('path');
 import semver = require('semver');
+import kill = require('tree-kill');
 import vscode = require('vscode');
 import { NearestNeighborDict, Node } from './avlTree';
 import { buildDiagnosticCollection, lintDiagnosticCollection, vetDiagnosticCollection } from './goMain';
@@ -867,27 +868,13 @@
 	}
 }
 
-export function killTree(processId: number): void {
-	if (process.platform === 'win32') {
-		const TASK_KILL = 'C:\\Windows\\System32\\taskkill.exe';
-
-		// when killing a process in Windows its child processes are *not* killed but become root processes.
-		// Therefore we use TASKKILL.EXE
-		try {
-			cp.execSync(`${TASK_KILL} /F /T /PID ${processId}`);
-		} catch (err) {
+export const killTree = (processId: number): void => {
+	kill(processId, (err) => {
+		if (err) {
 			console.log('Error killing process tree: ' + err);
 		}
-	} else {
-		// on linux and OS X we kill all direct and indirect child processes as well
-		try {
-			const cmd = path.join(__dirname, '../../../scripts/terminateProcess.sh');
-			cp.spawnSync(cmd, [processId.toString()]);
-		} catch (err) {
-			console.log('Error killing process tree: ' + err);
-		}
-	}
-}
+	});
+};
 
 export function makeMemoizedByteOffsetConverter(buffer: Buffer): (byteOffset: number) => number {
 	const defaultValue = new Node<number, number>(0, 0); // 0 bytes will always be 0 characters
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 5ff5c67..1998061 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -7,6 +7,7 @@
 import cp = require('child_process');
 import * as fs from 'fs-extra';
 import * as path from 'path';
+import * as sinon from 'sinon';
 import * as vscode from 'vscode';
 import { FilePatch, getEdits, getEditsFromUnifiedDiffStr } from '../../src/diffUtils';
 import { check } from '../../src/goCheck';
@@ -19,6 +20,8 @@
 	generateTestCurrentPackage
 } from '../../src/goGenerateTests';
 import { getTextEditForAddImport, listPackages } from '../../src/goImport';
+import { updateGoPathGoRootFromConfig } from '../../src/goInstallTools';
+import { goLint } from '../../src/goLint';
 import { documentSymbols, GoDocumentSymbolProvider, GoOutlineImportsOptions } from '../../src/goOutline';
 import { getAllPackages } from '../../src/goPackages';
 import { goPlay } from '../../src/goPlayground';
@@ -36,46 +39,39 @@
 	isVendorSupported
 } from '../../src/util';
 
-function queryDefaultGopathSync(): string | null {
-	const goExecutable = getBinPath('go');
-	if (!goExecutable) {
-		console.warn(`Failed to run "go env GOPATH" to find mod file as the "go" binary cannot be found`);
-		return null;
-	}
-	let gopath: string;
-	try {
-		const stdout = cp.execFileSync(goExecutable, ['env', 'GOPATH']);
-		console.log(`Got go env GOPATH result: ${stdout}`);
-		gopath = stdout.toString().trim();
-	} catch (err) {
-		console.warn(`Error when running go env GOPATH: ${err}`);
-	}
-	return gopath;
-}
-
 suite('Go Extension Tests', function() {
 	this.timeout(10000);
-	let gopath = getCurrentGoPath();
-	if (!gopath) {
-			gopath = queryDefaultGopathSync();
-	}
-	if (!gopath) {
-		assert.ok(gopath, 'Cannot run tests if GOPATH is not set as environment variable');
-		return;
-	}
-	console.log(`Using GOPATH: ${gopath}`);
-
-	const repoPath = path.join(gopath, 'src', 'test');
-	const fixturePath = path.join(repoPath, 'testfixture');
-	const fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'fixtures');
-	const generateTestsSourcePath = path.join(repoPath, 'generatetests');
-	const generateFunctionTestSourcePath = path.join(repoPath, 'generatefunctiontest');
-	const generatePackageTestSourcePath = path.join(repoPath, 'generatePackagetest');
-	const toolsGopath = getToolsGopath() || gopath;
 
 	const dummyCancellationSource = new vscode.CancellationTokenSource();
 
-	suiteSetup(() => {
+	// suiteSetup will initialize the following vars.
+	let gopath: string;
+	let repoPath: string;
+	let fixturePath: string;
+	let fixtureSourcePath: string;
+	let generateTestsSourcePath: string;
+	let generateFunctionTestSourcePath: string;
+	let generatePackageTestSourcePath: string;
+	let toolsGopath: string;
+
+	suiteSetup(async () => {
+		await updateGoPathGoRootFromConfig();
+
+		gopath = getCurrentGoPath();
+		if (!gopath) {
+			assert.ok(gopath, 'Cannot run tests if GOPATH is not set as environment variable');
+			return;
+		}
+		console.log(`Using GOPATH: ${gopath}`);
+
+		repoPath = path.join(gopath, 'src', 'test');
+		fixturePath = path.join(repoPath, 'testfixture');
+		fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'fixtures');
+		generateTestsSourcePath = path.join(repoPath, 'generatetests');
+		generateFunctionTestSourcePath = path.join(repoPath, 'generatefunctiontest');
+		generatePackageTestSourcePath = path.join(repoPath, 'generatePackagetest');
+		toolsGopath = getToolsGopath() || gopath;
+
 		fs.removeSync(repoPath);
 		fs.copySync(path.join(fixtureSourcePath, 'baseTest', 'test.go'), path.join(fixturePath, 'baseTest', 'test.go'));
 		fs.copySync(
@@ -196,6 +192,10 @@
 		fs.removeSync(repoPath);
 	});
 
+	teardown(() => {
+		sinon.restore();
+	});
+
 	async function testDefinitionProvider(goConfig: vscode.WorkspaceConfiguration): Promise<any> {
 		const provider = new GoDefinitionProvider(goConfig);
 		const uri = vscode.Uri.file(path.join(fixturePath, 'baseTest', 'test.go'));
@@ -436,6 +436,29 @@
 		await testHoverProvider(config, testCases);
 	});
 
+	test('Linting - concurrent process cancelation', async () => {
+		const util = require('../../src/util');
+		sinon.spy(util, 'runTool');
+		sinon.spy(util, 'killTree');
+
+		const config = Object.create(vscode.workspace.getConfiguration('go'), {
+			vetOnSave: { value: 'package' },
+			vetFlags: { value: ['-all'] },
+			buildOnSave: { value: 'package' },
+			lintOnSave: { value: 'package' },
+			// simulate a long running lint process by sleeping for a couple seconds
+			lintTool: { value: 'sleep' },
+			lintFlags: { value: ['2'] }
+		});
+
+		const results = await Promise.all([
+			goLint(vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_1.go')), config),
+			goLint(vscode.Uri.file(path.join(fixturePath, 'linterTest', 'linter_2.go')), config)
+		]);
+		assert.equal(util.runTool.callCount, 2, 'should have launched 2 lint jobs');
+		assert.equal(util.killTree.callCount, 1, 'should have killed 1 lint job before launching the next');
+	});
+
 	test('Error checking', async () => {
 		const config = Object.create(vscode.workspace.getConfiguration('go'), {
 			vetOnSave: { value: 'package' },
@@ -1283,7 +1306,7 @@
 					expected.length,
 					labels.length,
 					`expected number of completions: ${expected.length} Actual: ${
-						labels.length
+					labels.length
 					} at position(${position.line + 1},${position.character + 1}) ${labels}`
 				);
 				expected.forEach((entry, index) => {
@@ -1458,8 +1481,8 @@
 					diagnostics[0].errors.length,
 					'check without buildtags failed. Unexpected errors found'
 				);
-				assert.equal(
-					diagnostics[0].errors[0].msg.indexOf(`can't load package: package test/testfixture/buildTags`) > -1,
+				const errMsg = diagnostics[0].errors[0].msg;
+				assert.equal(errMsg.includes(`can't load package: package test/testfixture/buildTags`) || errMsg.includes(`build constraints exclude all Go files`),
 					true,
 					`check without buildtags failed. Go files not excluded. ${diagnostics[0].errors[0].msg}`
 				);