Repository: OmniSharp/omnisharp-vscode
Pull request #4738 (2021-09-02 23:10:34)

Implement the "dotNetCliPaths" option to support custom .NET SDK locations
Depends on https://github.com/OmniSharp/omnisharp-roslyn/pull/2227


Patch:
diff --git a/package.json b/package.json
index 49b4fa56..992f898d 100644
--- a/package.json
+++ b/package.json
@@ -988,7 +988,7 @@
         "omnisharp.dotnetPath": {
           "type": "string",
           "scope": "window",
-          "description": "Specified the path to a dotnet installation to use when \"useModernNet\" is set to true, instead of the default system one. Example: \"/home/username/mycustomdotnetdirectory\"."
+          "description": "Specified the path to a dotnet installation to use when \"useModernNet\" is set to true, instead of the default system one. This only influences the dotnet installation to use for hosting Omnisharp itself. Example: \"/home/username/mycustomdotnetdirectory\"."
         },
         "omnisharp.waitForDebugger": {
           "type": "boolean",
@@ -1092,6 +1092,14 @@
           "type": "string",
           "description": "Path to the .runsettings file which should be used when running unit tests."
         },
+        "omnisharp.dotNetCliPaths": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "description": "Paths to a local download of the .NET CLI to use for running any user code.",
+          "uniqueItems": true
+        },
         "razor.plugin.path": {
           "type": "string",
           "scope": "machine",
diff --git a/src/common.ts b/src/common.ts
index 94b54148..5460f47c 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -56,9 +56,9 @@ export function safeLength<T>(arr: T[] | undefined) {
     return arr ? arr.length : 0;
 }
 
-export async function execChildProcess(command: string, workingDirectory: string = getExtensionPath()): Promise<string> {
+export async function execChildProcess(command: string, workingDirectory: string = getExtensionPath(), env: NodeJS.ProcessEnv = {}): Promise<string> {
     return new Promise<string>((resolve, reject) => {
-        cp.exec(command, { cwd: workingDirectory, maxBuffer: 500 * 1024 }, (error, stdout, stderr) => {
+        cp.exec(command, { cwd: workingDirectory, maxBuffer: 500 * 1024, env: env }, (error, stdout, stderr) => {
             if (error) {
                 reject(new Error(`${error}
 ${stdout}
diff --git a/src/constants/IGetDotnetInfo.ts b/src/constants/IGetDotnetInfo.ts
index 15c22a76..ad2f346b 100644
--- a/src/constants/IGetDotnetInfo.ts
+++ b/src/constants/IGetDotnetInfo.ts
@@ -6,5 +6,5 @@
 import { DotnetInfo } from "../utils/getDotnetInfo";
 
 export interface IGetDotnetInfo {
-    (): Promise<DotnetInfo>;
+    (dotNetCliPaths: string[]): Promise<DotnetInfo>;
 }
\ No newline at end of file
diff --git a/src/coreclr-debug/activate.ts b/src/coreclr-debug/activate.ts
index cca5a6d5..71494649 100644
--- a/src/coreclr-debug/activate.ts
+++ b/src/coreclr-debug/activate.ts
@@ -14,10 +14,11 @@ import CSharpExtensionExports from '../CSharpExtensionExports';
 import { getRuntimeDependencyPackageWithId } from '../tools/RuntimeDependencyPackageUtils';
 import { getDotnetInfo, DotnetInfo } from '../utils/getDotnetInfo';
 import { DotnetDebugConfigurationProvider } from './debugConfigurationProvider';
+import { Options } from '../omnisharp/options';
 
 let _debugUtil: CoreClrDebugUtil = null;
 
-export async function activate(thisExtension: vscode.Extension<CSharpExtensionExports>, context: vscode.ExtensionContext, platformInformation: PlatformInformation, eventStream: EventStream) {
+export async function activate(thisExtension: vscode.Extension<CSharpExtensionExports>, context: vscode.ExtensionContext, platformInformation: PlatformInformation, eventStream: EventStream, options: Options) {
     _debugUtil = new CoreClrDebugUtil(context.extensionPath);
 
     if (!CoreClrDebugUtil.existsSync(_debugUtil.debugAdapterDir())) {
@@ -29,10 +30,10 @@ export async function activate(thisExtension: vscode.Extension<CSharpExtensionEx
             showInstallErrorMessage(eventStream);
         }
     } else if (!CoreClrDebugUtil.existsSync(_debugUtil.installCompleteFilePath())) {
-        completeDebuggerInstall(platformInformation, eventStream);
+        completeDebuggerInstall(platformInformation, eventStream, options);
     }
 
-    const factory = new DebugAdapterExecutableFactory(platformInformation, eventStream, thisExtension.packageJSON, thisExtension.extensionPath);
+    const factory = new DebugAdapterExecutableFactory(platformInformation, eventStream, thisExtension.packageJSON, thisExtension.extensionPath, options);
     context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('coreclr', new DotnetDebugConfigurationProvider(platformInformation)));
     context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('clr', new DotnetDebugConfigurationProvider(platformInformation)));
     context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('coreclr', factory));
@@ -73,8 +74,8 @@ async function checkIsValidArchitecture(platformInformation: PlatformInformation
     return false;
 }
 
-async function completeDebuggerInstall(platformInformation: PlatformInformation, eventStream: EventStream): Promise<boolean> {
-    return _debugUtil.checkDotNetCli()
+async function completeDebuggerInstall(platformInformation: PlatformInformation, eventStream: EventStream, options: Options): Promise<boolean> {
+    return _debugUtil.checkDotNetCli(options.dotNetCliPaths)
         .then(async (dotnetInfo: DotnetInfo) => {
 
             let isValidArchitecture: boolean = await checkIsValidArchitecture(platformInformation, eventStream);
@@ -132,7 +133,7 @@ function showDotnetToolsWarning(message: string): void {
 // Else it will launch the debug adapter
 export class DebugAdapterExecutableFactory implements vscode.DebugAdapterDescriptorFactory {
 
-    constructor(private readonly platformInfo: PlatformInformation, private readonly eventStream: EventStream, private readonly packageJSON: any, private readonly extensionPath: string) {
+    constructor(private readonly platformInfo: PlatformInformation, private readonly eventStream: EventStream, private readonly packageJSON: any, private readonly extensionPath: string, private readonly options: Options) {
     }
 
     async createDebugAdapterDescriptor(_session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined): Promise<vscode.DebugAdapterDescriptor> {
@@ -160,7 +161,7 @@ export class DebugAdapterExecutableFactory implements vscode.DebugAdapterDescrip
             }
             // install.complete does not exist, check dotnetCLI to see if we can complete.
             else if (!CoreClrDebugUtil.existsSync(util.installCompleteFilePath())) {
-                let success: boolean = await completeDebuggerInstall(this.platformInfo, this.eventStream);
+                let success: boolean = await completeDebuggerInstall(this.platformInfo, this.eventStream, this.options);
 
                 if (!success) {
                     this.eventStream.post(new DebuggerNotInstalledFailure());
@@ -172,12 +173,13 @@ export class DebugAdapterExecutableFactory implements vscode.DebugAdapterDescrip
         // debugger has finished installation, kick off our debugger process
 
         // Check for targetArchitecture
-        const targetArchitecture: string = getTargetArchitecture(this.platformInfo, _session.configuration.targetArchitecture, await getDotnetInfo());
+        let dotNetInfo = await getDotnetInfo(this.options.dotNetCliPaths);
+        const targetArchitecture: string = getTargetArchitecture(this.platformInfo, _session.configuration.targetArchitecture, dotNetInfo);
 
         // use the executable specified in the package.json if it exists or determine it based on some other information (e.g. the session)
         if (!executable) {
             const command = path.join(common.getExtensionPath(), ".debugger", targetArchitecture, "vsdbg-ui" + CoreClrDebugUtil.getPlatformExeExtension());
-            executable = new vscode.DebugAdapterExecutable(command);
+            executable = new vscode.DebugAdapterExecutable(command, [], { env: { 'DOTNET_ROOT' : dotNetInfo.CliPath ? path.dirname(dotNetInfo.CliPath) : '' } });
         }
 
         // make VS Code launch the DA executable
diff --git a/src/coreclr-debug/util.ts b/src/coreclr-debug/util.ts
index 4ffc78b4..962816c2 100644
--- a/src/coreclr-debug/util.ts
+++ b/src/coreclr-debug/util.ts
@@ -69,8 +69,8 @@ export class CoreClrDebugUtil {
     // is new enough for us.
     // Returns: a promise that returns a DotnetInfo class
     // Throws: An DotNetCliError() from the return promise if either dotnet does not exist or is too old.
-    public async checkDotNetCli(): Promise<DotnetInfo> {
-        let dotnetInfo = await getDotnetInfo();
+    public async checkDotNetCli(dotNetCliPaths: string[]): Promise<DotnetInfo> {
+        let dotnetInfo = await getDotnetInfo(dotNetCliPaths);
 
         if (dotnetInfo.FullInfo === DOTNET_MISSING_MESSAGE) {
             // something went wrong with spawning 'dotnet --info'
diff --git a/src/features/commands.ts b/src/features/commands.ts
index 166e1c46..70c2c0fc 100644
--- a/src/features/commands.ts
+++ b/src/features/commands.ts
@@ -23,7 +23,7 @@ import { IHostExecutableResolver } from '../constants/IHostExecutableResolver';
 import { getDotnetInfo } from '../utils/getDotnetInfo';
 import { getDecompilationAuthorization, resetDecompilationAuthorization } from '../omnisharp/decompilationPrompt';
 
-export default function registerCommands(context: vscode.ExtensionContext, server: OmniSharpServer, platformInfo: PlatformInformation, eventStream: EventStream, optionProvider: OptionProvider, monoResolver: IHostExecutableResolver, packageJSON: any, extensionPath: string): CompositeDisposable {
+export default function registerCommands(context: vscode.ExtensionContext, server: OmniSharpServer, platformInfo: PlatformInformation, eventStream: EventStream, optionProvider: OptionProvider, monoResolver: IHostExecutableResolver, dotnetResolver: IHostExecutableResolver, packageJSON: any, extensionPath: string): CompositeDisposable {
     let disposable = new CompositeDisposable();
     disposable.add(vscode.commands.registerCommand('o.restart', async () => restartOmniSharp(context, server, optionProvider)));
     disposable.add(vscode.commands.registerCommand('o.pickProjectAndStart', async () => pickProjectAndStart(server, optionProvider)));
@@ -53,7 +53,7 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
     // Register command for generating tasks.json and launch.json assets.
     disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
 
-    disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), monoResolver)));
+    disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), monoResolver, dotnetResolver)));
 
     disposable.add(vscode.commands.registerCommand('csharp.showDecompilationTerms', async () => showDecompilationTerms(context, server, optionProvider)));
 
diff --git a/src/features/reportIssue.ts b/src/features/reportIssue.ts
index be8bcf37..75bd97c7 100644
--- a/src/features/reportIssue.ts
+++ b/src/features/reportIssue.ts
@@ -11,11 +11,13 @@ import { OpenURL } from "../omnisharp/loggingEvents";
 import { Options } from "../omnisharp/options";
 import { IHostExecutableResolver } from "../constants/IHostExecutableResolver";
 import { IGetDotnetInfo } from "../constants/IGetDotnetInfo";
+import { dirname } from "path";
 
 const issuesUrl = "https://github.com/OmniSharp/omnisharp-vscode/issues/new";
 
-export default async function reportIssue(vscode: vscode, eventStream: EventStream, getDotnetInfo: IGetDotnetInfo, isValidPlatformForMono: boolean, options: Options, monoResolver: IHostExecutableResolver) {
-    const dotnetInfo = await getDotnetInfo();
+export default async function reportIssue(vscode: vscode, eventStream: EventStream, getDotnetInfo: IGetDotnetInfo, isValidPlatformForMono: boolean, options: Options, monoResolver: IHostExecutableResolver, dotnetResolver: IHostExecutableResolver) {
+    // Get info for the dotnet that the Omnisharp executable is run on, not the dotnet Omnisharp will execute user code on.
+    const dotnetInfo = await getDotnetInfo([ dirname((await dotnetResolver.getHostExecutableInfo(options)).path) ]);
     const monoInfo = await getMonoIfPlatformValid(isValidPlatformForMono, options, monoResolver);
     let extensions = getInstalledExtensions(vscode);
     let csharpExtVersion = getCsharpExtensionVersion(vscode);
diff --git a/src/main.ts b/src/main.ts
index 61fb6779..4448194f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -173,7 +173,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<CSharp
     let coreClrDebugPromise = Promise.resolve();
     if (runtimeDependenciesExist) {
         // activate coreclr-debug
-        coreClrDebugPromise = coreclrdebug.activate(context.extension, context, platformInfo, eventStream);
+        coreClrDebugPromise = coreclrdebug.activate(context.extension, context, platformInfo, eventStream, optionProvider.GetLatestOptions());
     }
 
     let razorPromise = Promise.resolve();
diff --git a/src/observers/TelemetryObserver.ts b/src/observers/TelemetryObserver.ts
index 3c93663a..e99cb104 100644
--- a/src/observers/TelemetryObserver.ts
+++ b/src/observers/TelemetryObserver.ts
@@ -68,7 +68,7 @@ export class TelemetryObserver {
     }
 
     private async handleOmnisharpInitialisation(event: OmnisharpInitialisation) {
-        this.dotnetInfo = await getDotnetInfo();
+        this.dotnetInfo = await getDotnetInfo(event.dotNetCliPaths);
         this.solutionId = this.createSolutionId(event.solutionPath);
     }
 
diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts
index 1060c9ab..7e064985 100644
--- a/src/omnisharp/extension.ts
+++ b/src/omnisharp/extension.ts
@@ -133,7 +133,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
         localDisposables = undefined;
     }));
 
-    disposables.add(registerCommands(context, server, platformInfo, eventStream, optionProvider, omnisharpMonoResolver, packageJSON, extensionPath));
+    disposables.add(registerCommands(context, server, platformInfo, eventStream, optionProvider, omnisharpMonoResolver, omnisharpDotnetResolver, packageJSON, extensionPath));
 
     if (!context.workspaceState.get<boolean>('assetPromptDisabled')) {
         disposables.add(server.onServerStart(async () => {
diff --git a/src/omnisharp/loggingEvents.ts b/src/omnisharp/loggingEvents.ts
index 89d84383..b67b40ff 100644
--- a/src/omnisharp/loggingEvents.ts
+++ b/src/omnisharp/loggingEvents.ts
@@ -41,7 +41,7 @@ export class OmnisharpStart extends TelemetryEventWithMeasures {
 
 export class OmnisharpInitialisation implements BaseEvent {
     type = EventType.OmnisharpInitialisation;
-    constructor(public timeStamp: Date, public solutionPath: string) { }
+    constructor(public dotNetCliPaths: string[], public timeStamp: Date, public solutionPath: string) { }
 }
 
 export class OmnisharpLaunch implements BaseEvent {
diff --git a/src/omnisharp/options.ts b/src/omnisharp/options.ts
index 6204896c..9ca8bb53 100644
--- a/src/omnisharp/options.ts
+++ b/src/omnisharp/options.ts
@@ -56,7 +56,8 @@ export class Options {
         public dotnetPath: string,
         public excludePaths: string[],
         public maxProjectFileCountForDiagnosticAnalysis: number,
-        public testRunSettings: string) {
+        public testRunSettings: string,
+        public dotNetCliPaths: string[]) {
     }
 
     public static Read(vscode: vscode): Options {
@@ -148,6 +149,8 @@ export class Options {
 
         const excludePaths = this.getExcludedPaths(vscode);
 
+        const dotNetCliPaths = omnisharpConfig.get<string[]>('dotNetCliPaths', []);
+
         return new Options(
             path,
             useModernNet,
@@ -198,7 +201,8 @@ export class Options {
             dotnetPath,
             excludePaths,
             maxProjectFileCountForDiagnosticAnalysis,
-            testRunSettings
+            testRunSettings,
+            dotNetCliPaths
         );
     }
 
diff --git a/src/omnisharp/server.ts b/src/omnisharp/server.ts
index 8c54289d..a0bdda57 100644
--- a/src/omnisharp/server.ts
+++ b/src/omnisharp/server.ts
@@ -433,6 +433,10 @@ export class OmniSharpServer {
             args.push('RoslynExtensionsOptions:AnalyzeOpenDocumentsOnly=true');
         }
 
+        for (let i = 0; i < options.dotNetCliPaths.length; i++) {
+            args.push(`DotNetCliOptions:LocationPaths:${i}=${options.dotNetCliPaths[i]}`);
+        }
+
         let launchInfo: LaunchInfo;
         try {
             launchInfo = await this._omnisharpManager.GetOmniSharpLaunchInfo(this.packageJSON.defaults.omniSharp, options.path, /* useFramework */ !options.useModernNet, serverUrl, latestVersionFileServerPath, installPath, this.extensionPath);
@@ -443,7 +447,7 @@ export class OmniSharpServer {
             return;
         }
 
-        this.eventStream.post(new ObservableEvents.OmnisharpInitialisation(new Date(), solutionPath));
+        this.eventStream.post(new ObservableEvents.OmnisharpInitialisation(options.dotNetCliPaths, new Date(), solutionPath));
         this._fireEvent(Events.BeforeServerStart, solutionPath);
 
         try {
diff --git a/src/utils/getDotnetInfo.ts b/src/utils/getDotnetInfo.ts
index d4e5dfdc..ec23b27b 100644
--- a/src/utils/getDotnetInfo.ts
+++ b/src/utils/getDotnetInfo.ts
@@ -3,7 +3,9 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 
+import { join } from "path";
 import { execChildProcess } from "../common";
+import { CoreClrDebugUtil } from "../coreclr-debug/util";
 
 export const DOTNET_MISSING_MESSAGE = "A valid dotnet installation could not be found.";
 
@@ -13,15 +15,28 @@ let _dotnetInfo: DotnetInfo;
 // is new enough for us.
 // Returns: a promise that returns a DotnetInfo class
 // Throws: An DotNetCliError() from the return promise if either dotnet does not exist or is too old.
-export async function getDotnetInfo(): Promise<DotnetInfo> {
+export async function getDotnetInfo(dotNetCliPaths: string[]): Promise<DotnetInfo> {
     if (_dotnetInfo !== undefined) {
         return _dotnetInfo;
     }
 
+    let dotnetExeName = CoreClrDebugUtil.getPlatformExeExtension();
+    let dotnetExecutablePath = undefined;
+
+    for (const dotnetPath of dotNetCliPaths) {
+        let dotnetFullPath = join(dotnetPath, dotnetExeName);
+        if (CoreClrDebugUtil.existsSync(dotnetFullPath)) {
+            dotnetExecutablePath = dotnetFullPath;
+            break;
+        }
+    }
+
     let dotnetInfo = new DotnetInfo();
 
     try {
-        let data = await execChildProcess('dotnet --info', process.cwd());
+        let data = await execChildProcess(`${dotnetExecutablePath || 'dotnet'} --info`, process.cwd());
+
+        dotnetInfo.CliPath = dotnetExecutablePath; 
 
         dotnetInfo.FullInfo = data;
 
@@ -49,6 +64,7 @@ export async function getDotnetInfo(): Promise<DotnetInfo> {
 }
 
 export class DotnetInfo {
+    public CliPath?: string;
     public FullInfo: string;
     public Version: string;
     public OsVersion: string;


Test patch:
diff --git a/test/unitTests/Fakes/FakeDotnetResolver.ts b/test/unitTests/Fakes/FakeDotnetResolver.ts
new file mode 100644
index 00000000..0b1b8dbf
--- /dev/null
+++ b/test/unitTests/Fakes/FakeDotnetResolver.ts
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { IHostExecutableResolver } from "../../../src/constants/IHostExecutableResolver";
+import { HostExecutableInformation } from "../../../src/constants/HostExecutableInformation";
+
+export const fakeMonoInfo: HostExecutableInformation = {
+    version: "someDotNetVersion",
+    path: "someDotNetPath",
+    env: undefined
+};
+
+export class FakeDotnetResolver implements IHostExecutableResolver {
+    public getDotnetCalled: boolean;
+
+    constructor(public willReturnDotnetInfo = true) {
+        this.getDotnetCalled = false;
+    }
+
+    async getHostExecutableInfo(): Promise<HostExecutableInformation> {
+        this.getDotnetCalled = true;
+        if (this.willReturnDotnetInfo) {
+            return Promise.resolve(fakeMonoInfo);
+        }
+
+        return Promise.resolve(undefined);
+    }
+}
diff --git a/test/unitTests/Fakes/FakeOptions.ts b/test/unitTests/Fakes/FakeOptions.ts
index 2c3c1ba9..0a3d55ee 100644
--- a/test/unitTests/Fakes/FakeOptions.ts
+++ b/test/unitTests/Fakes/FakeOptions.ts
@@ -56,5 +56,6 @@ export function getEmptyOptions(): Options {
         /* dotnetPath */"",
         /* excludePaths */null,
         /* maxProjectFileCountForDiagnosticAnalysis */null,
-        /* testRunSettings */"");
+        /* testRunSettings */"",
+        /* dotNetCliPaths */[]);
 }
diff --git a/test/unitTests/features/reportIssue.test.ts b/test/unitTests/features/reportIssue.test.ts
index c87b7bea..9d4dcb4b 100644
--- a/test/unitTests/features/reportIssue.test.ts
+++ b/test/unitTests/features/reportIssue.test.ts
@@ -13,6 +13,7 @@ import { vscode } from "../../../src/vscodeAdapter";
 import { Options } from "../../../src/omnisharp/options";
 import { FakeGetDotnetInfo, fakeDotnetInfo } from "../Fakes/FakeGetDotnetInfo";
 import { FakeMonoResolver, fakeMonoInfo } from "../Fakes/FakeMonoResolver";
+import { FakeDotnetResolver } from "../Fakes/FakeDotnetResolver";
 
 suite(`${reportIssue.name}`, () => {
     const vscodeVersion = "myVersion";
@@ -40,6 +41,7 @@ suite(`${reportIssue.name}`, () => {
     };
 
     let fakeMonoResolver: FakeMonoResolver;
+    let fakeDotnetResolver : FakeDotnetResolver;
     let eventStream: EventStream;
     let eventBus: TestEventBus;
     let getDotnetInfo = FakeGetDotnetInfo;
@@ -67,55 +69,56 @@ suite(`${reportIssue.name}`, () => {
         eventStream = new EventStream();
         eventBus = new TestEventBus(eventStream);
         fakeMonoResolver = new FakeMonoResolver();
+        fakeDotnetResolver = new FakeDotnetResolver();
     });
 
     test(`${OpenURL.name} event is created`, async () => {
-        await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+        await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
         let events = eventBus.getEvents();
         expect(events).to.have.length(1);
         expect(events[0].constructor.name).to.be.equal(`${OpenURL.name}`);
     });
 
     test(`${OpenURL.name} event is created with the omnisharp-vscode github repo issues url`, async () => {
-        await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver);
+        await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver, fakeDotnetResolver);
         let url = (<OpenURL>eventBus.getEvents()[0]).url;
         expect(url).to.include("https://github.com/OmniSharp/omnisharp-vscode/issues/new?body=Please paste the output from your clipboard");
     });
 
     suite("The body is passed to the vscode clipboard and", () => {
         test("it contains the vscode version", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(issueBody).to.include(`**VSCode version**: ${vscodeVersion}`);
         });
 
         test("it contains the csharp extension version", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(issueBody).to.include(`**C# Extension**: ${csharpExtVersion}`);
         });
 
         test("it contains dotnet info", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(issueBody).to.contain(fakeDotnetInfo.FullInfo);
         });
 
         test("mono information is obtained when it is a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(true);
         });
 
         test("mono version is put in the body when it is a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(true);
             expect(issueBody).to.contain(fakeMonoInfo.version);
         });
 
         test("mono information is not obtained when it is not a valid mono platform", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, false, options, fakeMonoResolver, fakeDotnetResolver);
             expect(fakeMonoResolver.getMonoCalled).to.be.equal(false);
         });
 
         test("The url contains the name, publisher and version for all the extensions that are not builtin", async () => {
-            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver);
+            await reportIssue(vscode, eventStream, getDotnetInfo, isValidForMono, options, fakeMonoResolver, fakeDotnetResolver);
             expect(issueBody).to.contain(extension2.packageJSON.name);
             expect(issueBody).to.contain(extension2.packageJSON.publisher);
             expect(issueBody).to.contain(extension2.packageJSON.version);
diff --git a/test/unitTests/logging/OmnisharpLoggerObserver.test.ts b/test/unitTests/logging/OmnisharpLoggerObserver.test.ts
index 3ec769cd..94c566bf 100644
--- a/test/unitTests/logging/OmnisharpLoggerObserver.test.ts
+++ b/test/unitTests/logging/OmnisharpLoggerObserver.test.ts
@@ -95,7 +95,7 @@ suite("OmnisharpLoggerObserver", () => {
 
 
     [
-        new OmnisharpInitialisation(new Date(5), "somePath"),
+        new OmnisharpInitialisation([], new Date(5), "somePath"),
     ].forEach((event: OmnisharpInitialisation) => {
         test(`${event.constructor.name}: TimeStamp and SolutionPath are logged`, () => {
             observer.post(event);