all: add MockMemento for use during mocha test

This CL adds a class MockMemento, which extends vscode's memento to
provide a substitute for Memento during testing.

Update golang/vscode-go#357

Change-Id: I694d5b1883feb54750fc4e207a62a718262ef25b
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/244060
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/src/goEnvironmentStatus.ts b/src/goEnvironmentStatus.ts
index 9fc73b3..bcdbf3f 100644
--- a/src/goEnvironmentStatus.ts
+++ b/src/goEnvironmentStatus.ts
@@ -160,6 +160,9 @@
  * update the selected go path and label in the workspace state
  */
 export async function setSelectedGo(goOption: GoEnvironmentOption, promptReload = true) {
+	if (!goOption) {
+		return;
+	}
 	const execFile = promisify(cp.execFile);
 	// if the selected go version is not installed, install it
 	if (goOption.binpath?.startsWith('go get')) {
@@ -253,7 +256,6 @@
 			return;
 		}
 
-		console.log('updating selectedGo: ', goOption);
 		await updateWorkspaceState('selectedGo', goOption);
 		goEnvStatusbarItem.text = goOption.label;
 	}
diff --git a/src/stateUtils.ts b/src/stateUtils.ts
index c7b49c2..7a83832 100644
--- a/src/stateUtils.ts
+++ b/src/stateUtils.ts
@@ -43,3 +43,7 @@
 export function setWorkspaceState(state: vscode.Memento) {
 	workspaceState = state;
 }
+
+export function getWorkspaceState(): vscode.Memento {
+	return workspaceState;
+}
diff --git a/test/integration/statusbar.test.ts b/test/integration/statusbar.test.ts
index 7873ab4..6f982ad 100644
--- a/test/integration/statusbar.test.ts
+++ b/test/integration/statusbar.test.ts
@@ -23,8 +23,10 @@
 	setSelectedGo,
 } from '../../src/goEnvironmentStatus';
 import { updateGoVarsFromConfig } from '../../src/goInstallTools';
+import { getWorkspaceState, setWorkspaceState } from '../../src/stateUtils';
 import ourutil = require('../../src/util');
 import { getCurrentGoRoot } from '../../src/utils/goPath';
+import { MockMemento } from '../mocks/MockMemento';
 
 describe('#initGoStatusBar()', function () {
 	this.beforeAll(async () => {
@@ -50,15 +52,21 @@
 	});
 });
 
-describe.skip('#setSelectedGo()', function () {
-	// TODO: reenable this once the problem is fixed https://github.com/golang/vscode-go/issues/357
+describe('#setSelectedGo()', async function () {
 	this.timeout(40000);
 	let sandbox: sinon.SinonSandbox | undefined;
 	let goOption: GoEnvironmentOption;
-	let defaultGoConfig: vscode.WorkspaceConfiguration;
+	let defaultMemento: vscode.Memento;
+	const version = await ourutil.getGoVersion();
+	const defaultGoOption = new GoEnvironmentOption(version.binaryPath, formatGoVersion(version.format()));
 
 	this.beforeAll(async () => {
-		defaultGoConfig = ourutil.getGoConfig();
+		defaultMemento = getWorkspaceState();
+		setWorkspaceState(new MockMemento());
+		await setSelectedGo(defaultGoOption);
+	});
+	this.afterAll(async () => {
+		setWorkspaceState(defaultMemento);
 	});
 	this.beforeEach(async () => {
 		goOption = await getSelectedGo();
@@ -69,34 +77,13 @@
 		sandbox.restore();
 	});
 
-	it('should update the workspace settings.json', async () => {
-		let tmpAltToolsWorkspace: any = {};
-		let tmpAltToolsGlobal: any = {};
-		const getGoConfigStub = sandbox.stub(ourutil, 'getGoConfig').returns({
-			get: (s: string) => {
-				if (s === 'alternateTools') { return tmpAltToolsWorkspace || tmpAltToolsGlobal; }
-				return defaultGoConfig.get(s);
-			},
-			update: (key: string, value: any, scope: vscode.ConfigurationTarget) => {
-				if (key === 'alternateTools') {
-					if (scope === vscode.ConfigurationTarget.Global) {
-						tmpAltToolsGlobal = value;
-					} else {
-						tmpAltToolsWorkspace = value;
-					}
-				}
-			},
-		} as vscode.WorkspaceConfiguration);
-
+	it('should update the workspace memento storage', async () => {
 		// set workspace setting
 		const workspaceTestOption = new GoEnvironmentOption('workspacetestpath', 'testlabel');
 		await setSelectedGo(workspaceTestOption, false);
 
-		// check that the stub was called
-		sandbox.assert.calledWith(getGoConfigStub);
-
 		// check that the new config is set
-		assert.equal(tmpAltToolsWorkspace['go'], 'workspacetestpath');
+		assert.equal(getSelectedGo()?.binpath, 'workspacetestpath');
 	});
 
 	it('should download an uninstalled version of Go', async () => {
@@ -104,24 +91,6 @@
 			return;
 		}
 
-		let tmpAltToolsWorkspace = {};
-		let tmpAltToolsGlobal = {};
-		const getGoConfigStub = sandbox.stub(ourutil, 'getGoConfig').returns({
-			get: (s: string) => {
-				if (s === 'alternateTools') { return tmpAltToolsWorkspace || tmpAltToolsGlobal; }
-				return defaultGoConfig.get(s);
-			},
-			update: (key: string, value: any, scope: vscode.ConfigurationTarget) => {
-				if (key === 'alternateTools') {
-					if (scope === vscode.ConfigurationTarget.Global) {
-						tmpAltToolsGlobal = value;
-					} else {
-						tmpAltToolsWorkspace = value;
-					}
-				}
-			},
-		} as vscode.WorkspaceConfiguration);
-
 		// setup tmp home directory for sdk installation
 		const envCache = Object.assign({}, process.env);
 		process.env.HOME = os.tmpdir();
@@ -129,7 +98,6 @@
 		// set selected go as a version to download
 		const option = new GoEnvironmentOption('go get golang.org/dl/go1.13.12', 'Go 1.13.12');
 		await setSelectedGo(option, false);
-		sandbox.assert.calledWith(getGoConfigStub);
 
 		// the temp sdk directory should now contain go1.13.12
 		const subdirs = await fs.readdir(path.join(os.tmpdir(), 'sdk'));
@@ -140,17 +108,20 @@
 	});
 });
 
-describe.skip('#updateGoVarsFromConfig()', function () {
-	// TODO: reenable this once the problem is fixed: https://github.com/golang/vscode-go/issues/357
+describe('#updateGoVarsFromConfig()', async function () {
 	this.timeout(10000);
 
-	let defaultGoConfig: vscode.WorkspaceConfiguration | undefined;
+	let defaultMemento: vscode.Memento;
 	let tmpRoot: string | undefined;
 	let tmpRootBin: string | undefined;
 	let sandbox: sinon.SinonSandbox | undefined;
+	const version = await ourutil.getGoVersion();
+	const defaultGoOption = new GoEnvironmentOption(version.binaryPath, formatGoVersion(version.format()));
 
 	this.beforeAll(async () => {
-		defaultGoConfig = ourutil.getGoConfig();
+		defaultMemento = getWorkspaceState();
+		setWorkspaceState(new MockMemento());
+		await setSelectedGo(defaultGoOption);
 
 		tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'rootchangetest'));
 		tmpRootBin = path.join(tmpRoot, 'bin');
@@ -161,7 +132,7 @@
 		const fixtureSourcePath = path.join(__dirname, '..', '..', '..', 'test', 'fixtures', 'testhelpers');
 		const execFile = util.promisify(cp.execFile);
 		const goRuntimePath = ourutil.getBinPath('go');
-		const { stdout, stderr } = await execFile(
+		const { stderr } = await execFile(
 			goRuntimePath, ['build', '-o', path.join(tmpRootBin, 'go'), path.join(fixtureSourcePath, 'fakego.go')]);
 		if (stderr) {
 			assert.fail(`failed to build the fake go binary required for testing: ${stderr}`);
@@ -169,6 +140,7 @@
 	});
 
 	this.afterAll(async () => {
+		setWorkspaceState(defaultMemento);
 		ourutil.rmdirRecursive(tmpRoot);
 		await updateGoVarsFromConfig();
 	});
@@ -201,21 +173,12 @@
 	});
 
 	it('should recognize the adjusted goroot using go.goroot', async () => {
-		// stub geteGoConfig to return "go.goroot": tmpRoot.
-		const getGoConfigStub = sandbox.stub(ourutil, 'getGoConfig').returns({
-			get: (s: string) => {
-				if (s === 'goroot') { return tmpRoot; }
-				return defaultGoConfig.get(s);
-			},
-		} as vscode.WorkspaceConfiguration);
-
 		// adjust the fake go binary's behavior.
 		process.env['FAKEGOROOT'] = tmpRoot;
 		process.env['FAKEGOVERSION'] = 'go version go2.0.0 darwin/amd64';
 
 		await updateGoVarsFromConfig();
 
-		sandbox.assert.calledWith(getGoConfigStub);
 		assert.equal((await ourutil.getGoVersion()).format(), '2.0.0');
 		assert.equal(getGoEnvironmentStatusbarItem().text, 'Go 2.0.0');
 		assert.equal(pathEnvVar()[0], [path.join(getCurrentGoRoot(), 'bin')],
@@ -226,21 +189,11 @@
 		// "go.alternateTools" : {"go": "go3"}
 		fs.copyFileSync(path.join(tmpRootBin, 'go'), path.join(tmpRootBin, 'go3'));
 
-		const getGoConfigStub = sandbox.stub(ourutil, 'getGoConfig').returns({
-			get: (s: string) => {
-				if (s === 'alternateTools') {
-					return { go: path.join(tmpRootBin, 'go3') };
-				}
-				return defaultGoConfig.get(s);
-			},
-		} as vscode.WorkspaceConfiguration);
-
 		process.env['FAKEGOROOT'] = tmpRoot;
 		process.env['FAKEGOVERSION'] = 'go version go3.0.0 darwin/amd64';
 
 		await updateGoVarsFromConfig();
 
-		sandbox.assert.calledWith(getGoConfigStub);
 		assert.equal((await ourutil.getGoVersion()).format(), '3.0.0');
 		assert.equal(getGoEnvironmentStatusbarItem().text, 'Go 3.0.0');
 		assert.equal(pathEnvVar()[0], [path.join(getCurrentGoRoot(), 'bin')],
diff --git a/test/mocks/MockMemento.ts b/test/mocks/MockMemento.ts
new file mode 100644
index 0000000..4c55f16
--- /dev/null
+++ b/test/mocks/MockMemento.ts
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ * Modification copyright 2020 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+import { Memento } from 'vscode';
+
+export class MockMemento implements Memento {
+	// _value must be named this way in order to match vscode's memento
+	private _value: Record<string, {}> = {};
+
+	public get(key: any, defaultValue?: any): any;
+	public get<T>(key: string, defaultValue?: T): T {
+		const exists = this._value.hasOwnProperty(key);
+		return exists ? this._value[key] : (defaultValue! as any);
+	}
+
+	public update(key: string, value: any): Thenable<void> {
+		this._value[key] = value;
+		return Promise.resolve();
+	}
+	public clear() {
+		this._value = {};
+	}
+}
diff --git a/tslint.json b/tslint.json
index 41739c7..d9108ea 100644
--- a/tslint.json
+++ b/tslint.json
@@ -151,7 +151,8 @@
 			true,
 			"ban-keywords",
 			"check-format",
-			"allow-pascal-case"
+			"allow-pascal-case",
+			"allow-leading-underscore"
 		],
 		"whitespace": [
 			true,