Update classes to all have worker contexts

Do this to deduplicate event stream, timeout time, extension state, proxy url, arch, and install key so we can throw errors from other places

uses a hack for the telemetry observer because it needs to be created before the acquisition context can be created
This commit is contained in:
Noah Gilson 2023-11-08 10:18:25 -08:00
Родитель b73b453068
Коммит c1680ff603
31 изменённых файлов: 465 добавлений и 451 удалений

Просмотреть файл

@ -45,6 +45,7 @@ import {
CommandExecutor,
} from 'vscode-dotnet-runtime-library';
import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId';
import { IAcquisitionWorkerContext } from 'vscode-dotnet-runtime-library/dist/Acquisition/IAcquisitionWorkerContext';
// tslint:disable no-var-requires
const packageJson = require('../package.json');
@ -105,17 +106,21 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
showLogCommand: `${commandPrefix}.${commandKeys.showAcquisitionLog}`,
packageJson
} as IEventStreamContext;
const [eventStream, outputChannel, loggingObserver, eventStreamObservers] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext);
const [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext);
const versionResolver = new VersionResolver(context.globalState, eventStream, resolvedTimeoutSeconds, proxyLink);
// Setting up command-shared classes for Runtime & SDK Acquisition
const existingPathConfigWorker = new ExtensionConfigurationWorker(extensionConfiguration, configKeys.existingPath);
const runtimeIssueContextFunctor = getIssueContext(existingPathConfigWorker);
const runtimeAcquisitionWorker = getAcquisitionWorker(true);
const runtimeContext = getAcquisitionWorkerContext(true);
telemetryObserver?.setAcquisitionContext(runtimeContext);
const runtimeVersionResolver = new VersionResolver(runtimeContext);
const runtimeIssueContextFunctor = getIssueContext(existingPathConfigWorker);
const runtimeAcquisitionWorker = getAcquisitionWorker(runtimeContext);
const sdkContext = getAcquisitionWorkerContext(false);
const sdkIssueContextFunctor = getIssueContext(existingPathConfigWorker);
const sdkAcquisitionWorker = getAcquisitionWorker(false);
const sdkAcquisitionWorker = getAcquisitionWorker(sdkContext);
// Creating API Surfaces
const dotnetAcquireRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquire}`, async (commandContext: IDotnetAcquireContext) => {
@ -124,6 +129,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
eventStream.post(new DotnetRuntimeAcquisitionStarted(commandContext.requestingExtensionId));
eventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId));
telemetryObserver?.setAcquisitionContext(runtimeContext);
runtimeAcquisitionWorker.setAcquisitionContext(commandContext);
if (!commandContext.version || commandContext.version === 'latest') {
@ -136,15 +142,17 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
return existingPath;
}
const version = await versionResolver.getFullRuntimeVersion(commandContext.version);
const version = await runtimeVersionResolver.getFullRuntimeVersion(commandContext.version);
fullyResolvedVersion = version;
if(commandContext.architecture !== undefined)
{
runtimeAcquisitionWorker.installingArchitecture = commandContext.architecture;
}
return runtimeAcquisitionWorker.acquireRuntime(version);
}, runtimeIssueContextFunctor(commandContext.errorConfiguration, 'acquire', commandContext.version), commandContext.requestingExtensionId);
const acquisitionInvoker = new AcquisitionInvoker(runtimeContext, utilContext);
return runtimeAcquisitionWorker.acquireRuntime(version, acquisitionInvoker);
}, runtimeIssueContextFunctor(commandContext.errorConfiguration, 'acquire', commandContext.version), commandContext.requestingExtensionId, runtimeContext);
const installKey = runtimeAcquisitionWorker.getInstallKey(fullyResolvedVersion);
eventStream.post(new DotnetRuntimeAcquisitionTotalSuccessEvent(commandContext.version, installKey, commandContext.requestingExtensionId ?? '', dotnetPath?.dotnetPath ?? ''));
@ -169,6 +177,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
return Promise.resolve(existingPath);
}
telemetryObserver?.setAcquisitionContext(sdkContext);
sdkAcquisitionWorker.setAcquisitionContext(commandContext);
if(commandContext.version === '' || !commandContext.version)
@ -176,12 +185,12 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
throw Error(`No version was defined to install.`);
}
const globalInstallerResolver = new GlobalInstallerResolver(context.globalState, eventStream, commandContext.version, resolvedTimeoutSeconds, proxyLink);
const globalInstallerResolver = new GlobalInstallerResolver(sdkContext, commandContext.version);
const dotnetPath = await sdkAcquisitionWorker.acquireGlobalSDK(globalInstallerResolver);
new CommandExecutor(eventStream, utilContext).setPathEnvVar(dotnetPath.dotnetPath, moreInfoUrl, displayWorker, vsCodeExtensionContext, true);
new CommandExecutor(sdkContext, utilContext).setPathEnvVar(dotnetPath.dotnetPath, moreInfoUrl, displayWorker, vsCodeExtensionContext, true);
return dotnetPath;
}, sdkIssueContextFunctor(commandContext.errorConfiguration, commandKeys.acquireGlobalSDK));
}, sdkIssueContextFunctor(commandContext.errorConfiguration, commandKeys.acquireGlobalSDK), commandContext.requestingExtensionId, sdkContext);
return pathResult;
});
@ -189,7 +198,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
const dotnetAcquireStatusRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquireStatus}`, async (commandContext: IDotnetAcquireContext) => {
const pathResult = callWithErrorHandling(async () => {
eventStream.post(new DotnetAcquisitionStatusRequested(commandContext.version, commandContext.requestingExtensionId));
const resolvedVersion = await versionResolver.getFullRuntimeVersion(commandContext.version);
const resolvedVersion = await runtimeVersionResolver.getFullRuntimeVersion(commandContext.version);
const dotnetPath = await runtimeAcquisitionWorker.acquireStatus(resolvedVersion, true);
return dotnetPath;
}, runtimeIssueContextFunctor(commandContext.errorConfiguration, 'acquireRuntimeStatus'));
@ -241,19 +250,23 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
});
}
function getAcquisitionWorker(isRuntimeWorker : boolean) : DotnetCoreAcquisitionWorker
function getAcquisitionWorkerContext(isRuntimeInstall : boolean) : IAcquisitionWorkerContext
{
return new DotnetCoreAcquisitionWorker({
return {
storagePath: context.globalStoragePath,
extensionState: context.globalState,
eventStream,
acquisitionInvoker: new AcquisitionInvoker(context.globalState, eventStream, resolvedTimeoutSeconds, utilContext),
installationValidator: new InstallationValidator(eventStream),
timeoutValue: resolvedTimeoutSeconds,
installDirectoryProvider: isRuntimeWorker ? new RuntimeInstallationDirectoryProvider(context.globalStoragePath): new SdkInstallationDirectoryProvider(context.globalStoragePath),
timeoutSeconds: resolvedTimeoutSeconds,
installDirectoryProvider: isRuntimeInstall ? new RuntimeInstallationDirectoryProvider(context.globalStoragePath): new SdkInstallationDirectoryProvider(context.globalStoragePath),
proxyUrl: proxyLink,
isExtensionTelemetryInitiallyEnabled: isExtensionTelemetryEnabled
}, utilContext, vsCodeExtensionContext);
}
}
function getAcquisitionWorker(workerContext : IAcquisitionWorkerContext) : DotnetCoreAcquisitionWorker
{
return new DotnetCoreAcquisitionWorker(workerContext, utilContext, vsCodeExtensionContext);
}
function getIssueContext(configResolver : ExtensionConfigurationWorker)

Просмотреть файл

@ -11,9 +11,14 @@
"cSpell.words": [
"aspnet",
"aspnetcore",
"hasher",
"HKCU",
"HKEY",
"norestart",
"OPTOUT",
"pkexec",
"REGHEXVALUE",
"REGTYPE"
"REGTYPE",
"setx"
]
}

Просмотреть файл

@ -18,7 +18,6 @@ import {
DotnetCommandNotFoundEvent,
DotnetOfflineFailure,
} from '../EventStream/EventStreamEvents';
import { IExtensionState } from '../IExtensionState';
import { timeoutConstants } from '../Utils/ErrorHandler';
import { IAcquisitionInvoker } from './IAcquisitionInvoker';
import { IDotnetInstallationContext } from './IDotnetInstallationContext';
@ -30,19 +29,18 @@ import { FileUtilities } from '../Utils/FileUtilities';
import { CommandExecutor } from '../Utils/CommandExecutor';
import { IUtilityContext } from '../Utils/IUtilityContext';
import path = require('path');
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
export class AcquisitionInvoker extends IAcquisitionInvoker {
protected readonly scriptWorker: IInstallScriptAcquisitionWorker;
protected fileUtilities : FileUtilities;
protected utilityContext : IUtilityContext;
private noPowershellError = `powershell.exe is not discoverable on your system. Is PowerShell added to your PATH and correctly installed? Please visit: https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-windows.
You will need to restart VS Code after these changes. If PowerShell is still not discoverable, try setting a custom existingDotnetPath following our instructions here: https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md.`
constructor(extensionState: IExtensionState, eventStream: IEventStream, timeoutTime : number, utilContext : IUtilityContext) {
constructor(private readonly workerContext : IAcquisitionWorkerContext, private readonly utilityContext : IUtilityContext) {
super(eventStream);
this.utilityContext = utilContext;
this.scriptWorker = new InstallScriptAcquisitionWorker(extensionState, eventStream, timeoutTime);
super(workerContext.eventStream);
this.scriptWorker = new InstallScriptAcquisitionWorker(workerContext);
this.fileUtilities = new FileUtilities();
}
@ -152,7 +150,7 @@ You will need to restart VS Code after these changes. If PowerShell is still not
try
{
// Check if PowerShell exists and is on the path.
command = await new CommandExecutor(this.eventStream, this.utilityContext).tryFindWorkingCommand(possibleCommands);
command = await new CommandExecutor(this.workerContext, this.utilityContext).tryFindWorkingCommand(possibleCommands);
if(!command)
{
knownError = true;

Просмотреть файл

@ -45,6 +45,7 @@ import { IDotnetAcquireContext} from '../IDotnetAcquireContext';
import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { TelemetryUtilities } from '../EventStream/TelemetryUtilities';
import { IAcquisitionInvoker } from './IAcquisitionInvoker';
/* tslint:disable:no-any */
export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
@ -90,8 +91,8 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
* @remarks this is simply a wrapper around the acquire function.
* @returns the requested dotnet path.
*/
public async acquireSDK(version: string): Promise<IDotnetAcquireResult> {
return this.acquire(version, false);
public async acquireSDK(version: string, invoker : IAcquisitionInvoker): Promise<IDotnetAcquireResult> {
return this.acquire(version, false, undefined, invoker);
}
public async acquireGlobalSDK(installerResolver: GlobalInstallerResolver): Promise<IDotnetAcquireResult>
@ -105,8 +106,8 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
* @remarks this is simply a wrapper around the acquire function.
* @returns the requested dotnet path.
*/
public async acquireRuntime(version: string): Promise<IDotnetAcquireResult> {
return this.acquire(version, true);
public async acquireRuntime(version: string, invoker : IAcquisitionInvoker): Promise<IDotnetAcquireResult> {
return this.acquire(version, true, undefined, invoker);
}
public async acquireStatus(version: string, installRuntime: boolean, architecture? : string): Promise<IDotnetAcquireResult | undefined> {
@ -148,7 +149,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
* @param globalInstallerResolver Create this and add it to install globally.
* @returns the dotnet acquisition result.
*/
private async acquire(version: string, installRuntime: boolean, globalInstallerResolver : GlobalInstallerResolver | null = null): Promise<IDotnetAcquireResult>
private async acquire(version: string, installRuntime: boolean, globalInstallerResolver : GlobalInstallerResolver | null = null, localInvoker? : IAcquisitionInvoker): Promise<IDotnetAcquireResult>
{
const installKey = this.getInstallKey(version);
this.context.eventStream.post(new DotnetInstallKeyCreatedEvent(`The requested version ${version} is now marked under the install key: ${installKey}.`));
@ -178,7 +179,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
{
Debugging.log(`The Acquisition Worker has Determined a Local Install was requested.`, this.context.eventStream);
acquisitionPromise = this.acquireCore(version, installRuntime, installKey).catch((error: Error) => {
acquisitionPromise = this.acquireLocalCore(version, installRuntime, installKey, localInvoker!).catch((error: Error) => {
delete this.acquisitionPromises[installKey];
throw new Error(`.NET Acquisition Failed: ${error.message}`);
});
@ -216,7 +217,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
*
* @remarks it is called "core" because it is the meat of the actual acquisition work; this has nothing to do with .NET core vs framework.
*/
private async acquireCore(version: string, installRuntime: boolean, installKey : string): Promise<string>
private async acquireLocalCore(version: string, installRuntime: boolean, installKey : string, acquisitionInvoker : IAcquisitionInvoker): Promise<string>
{
this.checkForPartialInstalls(installKey, version, installRuntime, !installRuntime);
@ -250,7 +251,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
architecture: this.installingArchitecture
} as IDotnetInstallationContext;
this.context.eventStream.post(new DotnetAcquisitionStarted(installKey, version, this.context.acquisitionContext?.requestingExtensionId));
await this.context.acquisitionInvoker.installDotnet(installContext).catch((reason) => {
await acquisitionInvoker.installDotnet(installContext).catch((reason) => {
throw Error(`Installation failed: ${reason}`);
});
this.context.installationValidator.validateDotnetInstall(installKey, dotnetPath);
@ -319,7 +320,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker
const installedSDKPath : string = await installer.getExpectedGlobalSDKPath(installingVersion, os.arch());
TelemetryUtilities.setDotnetSDKTelemetryToMatch(this.context.isExtensionTelemetryInitiallyEnabled, this.extensionContext, this.context.eventStream, this.utilityContext);
TelemetryUtilities.setDotnetSDKTelemetryToMatch(this.context.isExtensionTelemetryInitiallyEnabled, this.extensionContext, this.context, this.utilityContext);
this.context.installationValidator.validateDotnetInstall(installingVersion, installedSDKPath, os.platform() !== 'win32');

Просмотреть файл

@ -4,8 +4,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { IEventStream } from '../EventStream/EventStream';
import { IExtensionState } from '../IExtensionState';
import * as os from 'os';
import * as path from 'path';
import { VersionResolver } from './VersionResolver';
@ -16,12 +14,11 @@ import { DotnetFeatureBandDoesNotExistError,
DotnetUnexpectedInstallerArchitectureError,
DotnetUnexpectedInstallerOSError,
DotnetVersionCategorizedEvent,
DotnetVersionResolutionCompleted,
DotnetVersionResolutionError
} from '../EventStream/EventStreamEvents';
import { Debugging } from '../Utils/Debugging';
import { IVersionResolver } from './IVersionResolver';
import { FileUtilities } from '../Utils/FileUtilities';
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
import { getInstallKeyFromContext } from '..';
/* tslint:disable:no-any */
/* tslint:disable:only-arrow-functions */
@ -44,10 +41,6 @@ export class GlobalInstallerResolver {
private expectedInstallerHash : string;
private timeoutSecs : number;
private proxyUrl : string | undefined;
protected fileUtilities : FileUtilities;
private versionResolver : VersionResolver;
@ -75,21 +68,17 @@ export class GlobalInstallerResolver {
*/
public customWebRequestWorker? : WebRequestWorker | null = null;
constructor(
private readonly extensionState: IExtensionState,
private readonly eventStream: IEventStream,
constructor
(
private readonly context : IAcquisitionWorkerContext,
requestedVersion : string,
timeoutTime : number,
proxyUrl : string | undefined
)
{
this.requestedVersion = requestedVersion;
this.discoveredInstallerUrl = '';
this.fullySpecifiedVersionRequested = '';
this.expectedInstallerHash = '';
this.versionResolver = new VersionResolver(extensionState, eventStream, timeoutTime, proxyUrl);
this.timeoutSecs = timeoutTime;
this.proxyUrl = proxyUrl;
this.versionResolver = new VersionResolver(context);
this.fileUtilities = new FileUtilities();
}
@ -144,7 +133,7 @@ export class GlobalInstallerResolver {
{
if(this.versionResolver.isNonSpecificMajorOrMajorMinorVersion(version))
{
this.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be major, or major.minor.`));
this.context.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be major, or major.minor.`));
const numberOfPeriods = version.split('.').length - 1;
const indexUrl = this.getIndexUrl(numberOfPeriods === 0 ? `${version}.0` : version);
const indexJsonData = await this.fetchJsonObjectFromUrl(indexUrl);
@ -154,14 +143,14 @@ export class GlobalInstallerResolver {
}
else if(this.versionResolver.isNonSpecificFeatureBandedVersion(version))
{
this.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be a N.Y.XXX version.`));
this.context.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be a N.Y.XXX version.`));
const fullySpecifiedVersion = await this.getNewestSpecificVersionFromFeatureBand(version);
const installerUrlAndHash = await this.findCorrectInstallerUrlAndHash(fullySpecifiedVersion, this.getIndexUrl(this.versionResolver.getMajorMinor(fullySpecifiedVersion)));
return [installerUrlAndHash[0], fullySpecifiedVersion, installerUrlAndHash[1]];
}
else if(this.versionResolver.isFullySpecifiedVersion(version))
{
this.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be a fully specified version.`));
this.context.eventStream.post(new DotnetVersionCategorizedEvent(`The VersionResolver resolved the version ${version} to be a fully specified version.`));
const fullySpecifiedVersionRequested = version;
const indexUrl = this.getIndexUrl(this.versionResolver.getMajorMinor(fullySpecifiedVersionRequested));
const installerUrlAndHash = await this.findCorrectInstallerUrlAndHash(fullySpecifiedVersionRequested, indexUrl);
@ -169,7 +158,7 @@ export class GlobalInstallerResolver {
}
const err = new DotnetVersionResolutionError(new Error(`${this.badResolvedVersionErrorString} ${version}`), version);
this.eventStream.post(err);
this.context.eventStream.post(err);
throw err.error;
}
@ -185,24 +174,24 @@ export class GlobalInstallerResolver {
if(specificVersion === null || specificVersion === undefined || specificVersion === '')
{
const versionErr = new DotnetVersionResolutionError(new Error(`${this.badResolvedVersionErrorString} ${specificVersion}.`), specificVersion);
this.eventStream.post(versionErr);
this.context.eventStream.post(versionErr);
throw versionErr.error;
}
const convertedOs = this.fileUtilities.nodeOSToDotnetOS(os.platform(), this.eventStream);
const convertedOs = this.fileUtilities.nodeOSToDotnetOS(os.platform(), this.context.eventStream);
if(convertedOs === 'auto')
{
const osErr = new DotnetUnexpectedInstallerOSError(new Error(`The OS ${os.platform()} is currently unsupported or unknown.`));
this.eventStream.post(osErr);
const osErr = new DotnetUnexpectedInstallerOSError(new Error(`The OS ${os.platform()} is currently unsupported or unknown.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(osErr);
throw osErr.error;
}
const convertedArch = this.fileUtilities.nodeArchToDotnetArch(os.arch(), this.eventStream);
const convertedArch = this.fileUtilities.nodeArchToDotnetArch(os.arch(), this.context.eventStream);
if(convertedArch === 'auto')
{
const archErr = new DotnetUnexpectedInstallerArchitectureError(new Error(`The architecture ${os.arch()} is currently unsupported or unknown.
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`));
this.eventStream.post(archErr);
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(archErr);
throw archErr.error;
}
@ -212,8 +201,8 @@ export class GlobalInstallerResolver {
const releases = indexJson![this.releasesJsonKey];
if(releases.length === 0)
{
const jsonErr = new DotnetInvalidReleasesJSONError(new Error(`${this.releasesJsonErrorString}${indexUrl}`));
this.eventStream.post(jsonErr);
const jsonErr = new DotnetInvalidReleasesJSONError(new Error(`${this.releasesJsonErrorString}${indexUrl}`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(jsonErr);
throw jsonErr.error;
}
@ -238,21 +227,22 @@ export class GlobalInstallerResolver {
if(installerUrl === undefined)
{
const releaseJsonErr = new DotnetInvalidReleasesJSONError(new Error(`URL for ${desiredRidPackage} on ${specificVersion} is unavailable:
The version may be Out of Support, or the releases json format used by ${indexUrl} may be invalid and the extension needs to be updated.`));
this.eventStream.post(releaseJsonErr);
The version may be Out of Support, or the releases json format used by ${indexUrl} may be invalid and the extension needs to be updated.`),
getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(releaseJsonErr);
throw releaseJsonErr.error;
}
if(!(installerUrl as string).startsWith('https://download.visualstudio.microsoft.com/'))
{
const releaseJsonErr = new DotnetInvalidReleasesJSONError(new Error(`The url: ${installerUrl} is hosted on an unexpected domain.
We cannot verify that .NET downloads are hosted in a secure location, so we have rejected .NET. The url should be download.visualstudio.microsoft.com.
Please report this issue so it can be remedied or investigated.`));
this.eventStream.post(releaseJsonErr);
Please report this issue so it can be remedied or investigated.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(releaseJsonErr);
throw releaseJsonErr.error;
}
else
{
this.eventStream.post(new DotnetFileIntegrityCheckEvent(`This installer file is hosted on an expected domain https://download.visualstudio.microsoft.com/.`));
this.context.eventStream.post(new DotnetFileIntegrityCheckEvent(`This installer file is hosted on an expected domain https://download.visualstudio.microsoft.com/.`));
}
let installerHash = installer[this.releasesHashKey];
@ -264,14 +254,17 @@ Please report this issue so it can be remedied or investigated.`));
}
}
const installerErr = new DotnetNoInstallerFileExistsError(new Error(`An installer for the runtime ${desiredRidPackage} could not be found for version ${specificVersion}.`));
this.eventStream.post(installerErr);
const installerErr = new DotnetNoInstallerFileExistsError(new Error(`An installer for the runtime ${desiredRidPackage} could not be found for version ${specificVersion}.`),
getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(installerErr);
throw installerErr.error;
}
}
const fileErr = new DotnetNoInstallerFileExistsError(new Error(`The SDK installation files for version ${specificVersion} running on ${desiredRidPackage} couldn't be found. Is the version in support? Note that -preview versions or versions with build numbers aren't yet supported. Visit https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core for support information.`));
this.eventStream.post(fileErr);
const fileErr = new DotnetNoInstallerFileExistsError(new Error(`The SDK installation files for version ${specificVersion} running on ${desiredRidPackage} couldn't be found.
Is the version in support? Note that -preview versions or versions with build numbers aren't yet supported.
Visit https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core for support information.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(fileErr);
throw fileErr.error;
}
@ -299,8 +292,9 @@ Please report this issue so it can be remedied or investigated.`));
{
const err = new DotnetInvalidReleasesJSONError(new Error(`${this.releasesJsonErrorString}
${this.getIndexUrl(this.versionResolver.getMajorMinor(version))}.
The json does not have the parameter ${this.releasesSdkNameKey} which means the API publisher has published invalid dotnet release data. Please file an issue at https://github.com/dotnet/vscode-dotnet-runtime.`));
this.eventStream.post(err);
The json does not have the parameter ${this.releasesSdkNameKey} which means the API publisher has published invalid dotnet release data.
Please file an issue at https://github.com/dotnet/vscode-dotnet-runtime.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(err);
throw err.error;
}
@ -323,8 +317,8 @@ Please report this issue so it can be remedied or investigated.`));
default:
{
const err = new DotnetUnexpectedInstallerOSError(new Error(`The SDK Extension failed to map the OS ${operatingSystemInDotnetFormat} to a proper package type.
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`));
this.eventStream.post(err);
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(err);
throw err.error;
}
}
@ -348,8 +342,8 @@ Please report this issue so it can be remedied or investigated.`));
if(releases.length === 0)
{
const badJsonErr = new DotnetInvalidReleasesJSONError(new Error(`${this.releasesJsonErrorString}${indexUrl}`));
this.eventStream.post(badJsonErr);
const badJsonErr = new DotnetInvalidReleasesJSONError(new Error(`${this.releasesJsonErrorString}${indexUrl}`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(badJsonErr);
throw badJsonErr.error;
}
@ -367,8 +361,9 @@ Please report this issue so it can be remedied or investigated.`));
}
const availableBands = Array.from(new Set(sdks.map((x : any) => this.versionResolver.getFeatureBandFromVersion(x[this.releasesSdkVersionKey]))));
const err = new DotnetFeatureBandDoesNotExistError(new Error(`The feature band '${band}' doesn't exist for the SDK major version '${version}'. Available feature bands for this SDK version are ${availableBands}.`));
this.eventStream.post(err);
const err = new DotnetFeatureBandDoesNotExistError(new Error(`The feature band '${band}' doesn't exist for the SDK major version '${version}'.
Available feature bands for this SDK version are ${availableBands}.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(err);
throw err.error;
}
@ -380,7 +375,7 @@ Please report this issue so it can be remedied or investigated.`));
*/
private async fetchJsonObjectFromUrl(url : string)
{
const webWorker = this.customWebRequestWorker ? this.customWebRequestWorker : new WebRequestWorker(this.extensionState, this.eventStream, url, this.timeoutSecs * 1000, this.proxyUrl);
const webWorker = this.customWebRequestWorker ? this.customWebRequestWorker : new WebRequestWorker(this.context, url);
return webWorker.getCachedData();
}
}

Просмотреть файл

@ -2,10 +2,9 @@
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*--------------------------------------------------------------------------------------------*/
import { IDotnetAcquireContext, IWindowDisplayWorker } from '..';
import { IDotnetAcquireContext } from '../IDotnetAcquireContext';
import { IEventStream } from '../EventStream/EventStream';
import { IExtensionState } from '../IExtensionState';
import { IAcquisitionInvoker } from './IAcquisitionInvoker';
import { IInstallationDirectoryProvider } from './IInstallationDirectoryProvider';
import { IInstallationValidator } from './IInstallationValidator';
@ -13,7 +12,6 @@ export interface IAcquisitionWorkerContext {
storagePath: string;
extensionState: IExtensionState;
eventStream: IEventStream;
acquisitionInvoker: IAcquisitionInvoker;
installationValidator: IInstallationValidator;
timeoutSeconds: number;
installDirectoryProvider: IInstallationDirectoryProvider;

Просмотреть файл

@ -60,7 +60,7 @@ export abstract class IDistroDotnetSDKProvider {
constructor(distroVersion : DistroVersionPair, context : IAcquisitionWorkerContext, utilContext : IUtilityContext, executor : ICommandExecutor | null = null)
{
this.commandRunner = executor ?? new CommandExecutor(context.eventStream, utilContext);
this.commandRunner = executor ?? new CommandExecutor(context, utilContext);
this.context = context;
this.distroVersion = distroVersion;
this.versionResolver = new VersionResolver(context);

Просмотреть файл

@ -5,13 +5,15 @@
import { IDotnetAcquireResult } from '../IDotnetAcquireResult';
import { GlobalInstallerResolver } from './GlobalInstallerResolver';
import { IAcquisitionInvoker } from './IAcquisitionInvoker';
export interface IDotnetCoreAcquisitionWorker {
export interface IDotnetCoreAcquisitionWorker
{
uninstallAll(): void;
acquireRuntime(version: string): Promise<IDotnetAcquireResult>;
acquireRuntime(version: string, invoker : IAcquisitionInvoker): Promise<IDotnetAcquireResult>;
acquireSDK(version: string): Promise<IDotnetAcquireResult>;
acquireSDK(version: string, invoker : IAcquisitionInvoker): Promise<IDotnetAcquireResult>;
acquireGlobalSDK(installerResolver: GlobalInstallerResolver): Promise<IDotnetAcquireResult>;
}

Просмотреть файл

@ -76,7 +76,7 @@ export class LinuxVersionResolver
constructor(acquisitionContext : IAcquisitionWorkerContext, utilContext : IUtilityContext, private readonly acquireContext : IDotnetAcquireContext,
executor : ICommandExecutor | null = null, distroProvider : IDistroDotnetSDKProvider | null = null)
{
this.commandRunner = executor ?? new CommandExecutor(acquisitionContext.eventStream, utilContext, acquireContext);
this.commandRunner = executor ?? new CommandExecutor(acquisitionContext, utilContext);
this.acquisitionContext = acquisitionContext;
this.utilityContext = utilContext;
this.versionResolver = new VersionResolver(acquisitionContext);

Просмотреть файл

@ -23,6 +23,7 @@ import { DotnetVersionSupportPhase,
} from '../IDotnetListVersionsContext';
import { Debugging } from '../Utils/Debugging';
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
import { DotnetVersionParseEvent, SuppressedAcquisitionError, getInstallKeyFromContext } from '..';
/* tslint:disable:no-any */
export class VersionResolver implements IVersionResolver {
@ -35,7 +36,7 @@ export class VersionResolver implements IVersionResolver {
webWorker?: WebRequestWorker
)
{
this.webWorker = webWorker ?? new WebRequestWorker(context.extensionState, context.eventStream, this.releasesUrl, this.timeoutTime * 1000, this.proxy);
this.webWorker = webWorker ?? new WebRequestWorker(context, this.releasesUrl);
}
/**
@ -65,7 +66,7 @@ export class VersionResolver implements IVersionResolver {
if (!response)
{
const offlineError = new Error('Unable to connect to the index server: Cannot find .NET versions.');
this.eventStream.post(new DotnetOfflineFailure(offlineError, 'any'));
this.context.eventStream.post(new DotnetOfflineFailure(offlineError, getInstallKeyFromContext(this.context.acquisitionContext!)));
reject(offlineError);
}
else
@ -119,26 +120,26 @@ export class VersionResolver implements IVersionResolver {
try
{
const versionResult = this.resolveVersion(version, releasesVersions);
this.eventStream.post(new DotnetVersionResolutionCompleted(version, versionResult));
this.context.eventStream.post(new DotnetVersionResolutionCompleted(version, versionResult));
resolve(versionResult);
}
catch (error)
{
this.eventStream.post(new DotnetVersionResolutionError(error as Error, version));
this.context.eventStream.post(new DotnetVersionResolutionError(error as Error, version));
reject(error);
}
});
}
private resolveVersion(version: string, releases: IDotnetListVersionsResult): string {
Debugging.log(`Resolving the version: ${version}`, this.eventStream);
Debugging.log(`Resolving the version: ${version}`, this.context.eventStream);
this.validateVersionInput(version);
const matchingVersion = releases.filter((availableVersions : IDotnetVersion) => availableVersions.channelVersion === version);
if (!matchingVersion || matchingVersion.length < 1)
{
const err = new DotnetVersionResolutionError(new Error(`The requested and or resolved version is invalid.`), version);
this.eventStream.post(err);
this.context.eventStream.post(err);
throw err.error;
}
@ -156,16 +157,16 @@ export class VersionResolver implements IVersionResolver {
{
parsedVer = null;
}
Debugging.log(`Semver parsing passed: ${version}.`, this.eventStream);
Debugging.log(`Semver parsing passed: ${version}.`, this.context.eventStream);
if (version.split('.').length !== 2 || !parsedVer)
{
Debugging.log(`Resolving the version: ${version} ... it is invalid!`, this.eventStream);
Debugging.log(`Resolving the version: ${version} ... it is invalid!`, this.context.eventStream);
const err = new DotnetVersionResolutionError(new Error(`An invalid version was requested. Version: ${version}`), version);
this.eventStream.post(err);
this.context.eventStream.post(err);
throw err.error;
}
Debugging.log(`The version ${version} was determined to be valid.`, this.eventStream);
Debugging.log(`The version ${version} was determined to be valid.`, this.context.eventStream);
}
private async getReleasesInfo(getRuntimeVersion : boolean): Promise<IDotnetListVersionsResult>
@ -175,8 +176,9 @@ export class VersionResolver implements IVersionResolver {
const response = await this.GetAvailableDotnetVersions(apiContext);
if (!response)
{
const err = new DotnetInvalidReleasesJSONError(new Error(`We could not reach the releases API ${this.releasesUrl} to download dotnet, is your machine offline or is this website down?`));
this.eventStream.post(err);
const err = new DotnetInvalidReleasesJSONError(new Error(`We could not reach the releases API ${this.releasesUrl} to download dotnet, is your machine offline or is this website down?`),
getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(err);
throw err.error;
}
@ -190,7 +192,7 @@ export class VersionResolver implements IVersionResolver {
*/
public getMajor(fullySpecifiedVersion : string) : string
{
// The called function will check that we can do the split, so we dont need to check again.
// The called function will check that we can do the split, so we don't need to check again.
return this.getMajorMinor(fullySpecifiedVersion).split('.')[0];
}
@ -203,8 +205,9 @@ export class VersionResolver implements IVersionResolver {
{
if(fullySpecifiedVersion.split('.').length < 2)
{
const err = new DotnetVersionResolutionError(new Error(`The requested version ${fullySpecifiedVersion} is invalid.`), fullySpecifiedVersion);
throw err.error;
const event = new DotnetVersionResolutionError(new Error(`The requested version ${fullySpecifiedVersion} is invalid.`), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(event);
throw event.error;
}
const majorMinor = `${fullySpecifiedVersion.split('.').at(0)}.${fullySpecifiedVersion.split('.').at(1)}`;
@ -221,8 +224,10 @@ export class VersionResolver implements IVersionResolver {
const band : string | undefined = fullySpecifiedVersion.split('.')?.at(2)?.charAt(0);
if(band === undefined)
{
const err = new DotnetFeatureBandDoesNotExistError(new Error(`${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`));
throw err.error;
const event = new DotnetFeatureBandDoesNotExistError(new Error(`${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`),
getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(event);
throw event.error;
}
return band;
}
@ -247,8 +252,10 @@ export class VersionResolver implements IVersionResolver {
const patch : string | undefined = fullySpecifiedVersion.split('.')?.at(2)?.substring(1);
if(patch === undefined || !this.isNumber(patch))
{
const err = new DotnetFeatureBandDoesNotExistError(new Error(`${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`));
throw err.error;
const event = new DotnetFeatureBandDoesNotExistError(new Error(`${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`),
getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(event);
throw event.error;
}
return patch
}
@ -268,9 +275,11 @@ export class VersionResolver implements IVersionResolver {
{
return true;
}
Debugging.log(`The version has a bad patch number: ${fullySpecifiedVersion}`);
this.context.eventStream.post(new DotnetVersionParseEvent(`The version has a bad patch number: ${fullySpecifiedVersion}`));
}
Debugging.log(`The version has more or less than two periods, or it is too long: ${fullySpecifiedVersion}`);
this.context.eventStream.post(new DotnetVersionParseEvent(`The version has more or less than two periods, or it is too long: ${fullySpecifiedVersion}`));
return false;
}

Просмотреть файл

@ -16,7 +16,7 @@ import { CommandExecutor } from '../Utils/CommandExecutor';
import { IFileUtilities } from '../Utils/IFileUtilities';
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { DotnetCoreAcquisitionWorker } from '..';
import { DotnetCoreAcquisitionWorker, getInstallKeyFromContext } from '..';
/* tslint:disable:only-arrow-functions */
/* tslint:disable:no-empty */
/* tslint:disable:no-any */
@ -52,7 +52,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
this.installerUrl = installerUrl;
this.installingVersion = installingVersion;
this.installerHash = installerHash;
this.commandRunner = executor ?? new CommandExecutor(context.eventStream, utilContext);
this.commandRunner = executor ?? new CommandExecutor(context, utilContext);
this.versionResolver = new VersionResolver(context);
this.file = new FileUtilities();
this.webWorker = new WebRequestWorker(context, installerUrl);
@ -73,8 +73,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
}
const err = new DotnetConflictingGlobalWindowsInstallError(new Error(`An global install is already on the machine: version ${conflictingVersion}, that conflicts with the requested version.
Please uninstall this version first if you would like to continue.
If Visual Studio is installed, you may need to use the VS Setup Window to uninstall the SDK component.`),
DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(this.acquisitionContext.acquisitionContext?.version!, this.acquisitionContext.installingArchitecture, true));
If Visual Studio is installed, you may need to use the VS Setup Window to uninstall the SDK component.`), getInstallKeyFromContext(this.acquisitionContext.acquisitionContext!));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -85,8 +84,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
if(!canContinue)
{
const err = new DotnetConflictingGlobalWindowsInstallError(new Error(`The integrity of the .NET install file is invalid, or there was no integrity to check and you denied the request to continue with those risks.
We cannot verify .NET is safe to download at this time. Please try again later.`),
DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(this.acquisitionContext.acquisitionContext?.version!, this.acquisitionContext.installingArchitecture, true));
We cannot verify .NET is safe to download at this time. Please try again later.`), getInstallKeyFromContext(this.acquisitionContext.acquisitionContext!));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -183,8 +181,7 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
return path.resolve(`/usr/local/share/dotnet/dotnet`);
}
const err = new DotnetUnexpectedInstallerOSError(new Error(`The operating system ${os.platform()} is unsupported.`),
DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(this.acquisitionContext.acquisitionContext?.version!, this.acquisitionContext.installingArchitecture, true));
const err = new DotnetUnexpectedInstallerOSError(new Error(`The operating system ${os.platform()} is unsupported.`), getInstallKeyFromContext(this.acquisitionContext.acquisitionContext!));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -213,8 +210,7 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
{
const error = new Error(`The 'open' command on OSX was not detected. This is likely due to the PATH environment variable on your system being clobbered by another program.
Please correct your PATH variable or make sure the 'open' utility is installed so .NET can properly execute.`);
this.acquisitionContext.eventStream.post(new OSXOpenNotAvailableError(error,
DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(this.acquisitionContext.acquisitionContext?.version!, this.acquisitionContext.installingArchitecture, true)));
this.acquisitionContext.eventStream.post(new OSXOpenNotAvailableError(error, getInstallKeyFromContext(this.acquisitionContext.acquisitionContext!)));
throw error;
}
else if(workingCommand.commandRoot === 'command')

Просмотреть файл

@ -101,6 +101,22 @@ export abstract class DotnetAcquisitionError extends IEvent {
super();
}
public getProperties(telemetry = false): { [key: string]: string } | undefined {
return {ErrorName : this.error.name,
ErrorMessage : this.error.message,
StackTrace : this.error.stack ? TelemetryUtilities.HashAllPaths(this.error.stack) : '',
InstallKey : this.installKey};
}
}
export abstract class DotnetNonAcquisitionError extends IEvent {
public readonly type = EventType.DotnetAcquisitionError;
public isError = true;
constructor(public readonly error: Error) {
super();
}
public getProperties(telemetry = false): { [key: string]: string } | undefined {
return {ErrorName : this.error.name,
ErrorMessage : this.error.message,
@ -142,6 +158,22 @@ export class DotnetPreinstallDetectionError extends DotnetAcquisitionError {
public readonly eventName = 'DotnetPreinstallDetectionError';
}
export class DotnetNotInstallRelatedCommandFailed extends DotnetNonAcquisitionError {
public readonly eventName = 'DotnetNotInstallRelatedCommandFailed';
constructor(error: Error, public readonly command: string) {
super(error);
}
public getProperties(telemetry = false): { [key: string]: string } | undefined {
return {
ErrorMessage : this.error.message,
CommandName : this.command,
ErrorName : this.error.name,
StackTrace : this.error.stack ? this.error.stack : ''};
}
}
export class DotnetCommandFailed extends DotnetAcquisitionError {
public readonly eventName = 'DotnetCommandFailed';
@ -155,7 +187,7 @@ export class DotnetCommandFailed extends DotnetAcquisitionError {
ErrorName : this.error.name,
StackTrace : this.error.stack ? this.error.stack : ''};
}
}
}
export class DotnetInvalidReleasesJSONError extends DotnetAcquisitionError {
public readonly eventName = 'DotnetInvalidReleasesJSONError';
@ -489,6 +521,10 @@ export class CommandExecutionUnderSudoEvent extends DotnetCustomMessageEvent {
public readonly eventName = 'CommandExecutionUnderSudoEvent';
}
export class DotnetVersionParseEvent extends DotnetCustomMessageEvent {
public readonly eventName = 'DotnetVersionParseEvent';
}
export abstract class DotnetFileEvent extends DotnetAcquisitionMessage
{
constructor(public readonly eventMessage: string, public readonly time: string, public readonly file: string) { super(); }

Просмотреть файл

@ -33,7 +33,7 @@ export interface IEventStreamContext {
}
export function registerEventStream(context: IEventStreamContext, extensionContext : IVSCodeExtensionContext,
utilityContext : IUtilityContext): [EventStream, vscode.OutputChannel, LoggingObserver, IEventStreamObserver[]]
utilityContext : IUtilityContext): [EventStream, vscode.OutputChannel, LoggingObserver, IEventStreamObserver[], TelemetryObserver | null]
{
const outputChannel = vscode.window.createOutputChannel(context.displayChannelName);
if (!fs.existsSync(context.logPath))
@ -56,12 +56,13 @@ export function registerEventStream(context: IEventStreamContext, extensionConte
eventStream.subscribe(event => observer.post(event));
}
let telemetryObserver : TelemetryObserver | null = null;
if (context.enableTelemetry) {
const telemetryObserver = new TelemetryObserver(context.packageJson, context.enableTelemetry, eventStream, extensionContext, utilityContext, context.telemetryReporter);
eventStream.subscribe(event => telemetryObserver.post(event));
telemetryObserver = new TelemetryObserver(context.packageJson, context.enableTelemetry, extensionContext, utilityContext, context.telemetryReporter);
eventStream.subscribe(event => telemetryObserver!.post(event));
}
return [eventStream, outputChannel, loggingObserver, eventStreamObservers];
return [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver];
}
export function enableExtensionTelemetry(extensionConfiguration: IExtensionConfiguration, enableTelemetryKey: string): boolean {

Просмотреть файл

@ -12,6 +12,7 @@ import { IEventStream } from './EventStream';
import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { TelemetryUtilities } from './TelemetryUtilities';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
export interface ITelemetryReporter {
sendTelemetryEvent(eventName: string, properties?: { [key: string]: string }, measures?: { [key: string]: number }): void;
@ -22,12 +23,11 @@ export interface ITelemetryReporter {
export class TelemetryObserver implements IEventStreamObserver {
private readonly telemetryReporter: ITelemetryReporter;
private isExtensionTelemetryEnabled = false;
private eventStream : IEventStream;
private extensionContext : IVSCodeExtensionContext;
private utilityContext : IUtilityContext;
private acquisitionContext : IAcquisitionWorkerContext | null = null;
constructor(packageJson: IPackageJson, isExtensionTelemetryEnabled : boolean, eventStream : IEventStream,
extensionContext : IVSCodeExtensionContext, utilContext : IUtilityContext, telemetryReporter?: ITelemetryReporter) {
constructor(packageJson: IPackageJson, isExtensionTelemetryEnabled : boolean, private readonly extensionContext : IVSCodeExtensionContext,
private readonly utilityContext : IUtilityContext, telemetryReporter?: ITelemetryReporter)
{
if (telemetryReporter === undefined)
{
const extensionVersion = packageJson.version;
@ -41,17 +41,19 @@ export class TelemetryObserver implements IEventStreamObserver {
}
this.isExtensionTelemetryEnabled = isExtensionTelemetryEnabled;
this.eventStream = eventStream;
this.extensionContext = extensionContext;
this.utilityContext = utilContext;
vscode.env.onDidChangeTelemetryEnabled((newIsTelemetryEnabledSetting: boolean) =>
{
this.isExtensionTelemetryEnabled = newIsTelemetryEnabledSetting;
TelemetryUtilities.setDotnetSDKTelemetryToMatch(this.isExtensionTelemetryEnabled, this.extensionContext, this.eventStream, this.utilityContext);
TelemetryUtilities.setDotnetSDKTelemetryToMatch(this.isExtensionTelemetryEnabled, this.extensionContext, this.acquisitionContext!, this.utilityContext);
});
}
public setAcquisitionContext(context : IAcquisitionWorkerContext)
{
this.acquisitionContext = context;
}
/**
*
* @param event The event posted to the event stream that we subscribed to.

Просмотреть файл

@ -11,6 +11,7 @@ import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
import { IEventStream } from './EventStream';
import { DotnetTelemetrySettingEvent } from './EventStreamEvents';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
export class TelemetryUtilities
{
@ -47,13 +48,13 @@ export class TelemetryUtilities
return hashedPathsString;
}
static async setDotnetSDKTelemetryToMatch(isExtensionTelemetryEnabled : boolean, extensionContext : IVSCodeExtensionContext, eventStream : IEventStream, utilityContext : IUtilityContext)
static async setDotnetSDKTelemetryToMatch(isExtensionTelemetryEnabled : boolean, extensionContext : IVSCodeExtensionContext, acquisitionContext : IAcquisitionWorkerContext, utilityContext : IUtilityContext)
{
if(!TelemetryUtilities.isTelemetryEnabled(isExtensionTelemetryEnabled, utilityContext))
{
TelemetryUtilities.logTelemetryChange(`Before disabling .NET SDK telemetry:`, isExtensionTelemetryEnabled, eventStream, utilityContext);
TelemetryUtilities.logTelemetryChange(`Before disabling .NET SDK telemetry:`, isExtensionTelemetryEnabled, acquisitionContext.eventStream, utilityContext);
await new CommandExecutor(eventStream, utilityContext).setEnvironmentVariable(
await new CommandExecutor(acquisitionContext, utilityContext).setEnvironmentVariable(
'DOTNET_CLI_TELEMETRY_OPTOUT',
'true',
extensionContext,
@ -64,7 +65,7 @@ To disable .NET SDK telemetry, set the environment variable DOTNET_CLI_TELEMETRY
`The .NET Install Tool will not collect telemetry. However, the .NET SDK does collect telemetry.
To disable .NET SDK telemetry, set the environment variable DOTNET_CLI_TELEMETRY_OPTOUT to true.`);
TelemetryUtilities.logTelemetryChange(`After disabling .NET SDK telemetry:`, isExtensionTelemetryEnabled, eventStream, utilityContext);
TelemetryUtilities.logTelemetryChange(`After disabling .NET SDK telemetry:`, isExtensionTelemetryEnabled, acquisitionContext.eventStream, utilityContext);
}
else
{
@ -72,7 +73,7 @@ To disable .NET SDK telemetry, set the environment variable DOTNET_CLI_TELEMETRY
`The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry`,
() => {/* No Callback */}, );
TelemetryUtilities.logTelemetryChange(`Unchanged Telemetry Settings.`, isExtensionTelemetryEnabled, eventStream, utilityContext);
TelemetryUtilities.logTelemetryChange(`Unchanged Telemetry Settings.`, isExtensionTelemetryEnabled, acquisitionContext.eventStream, utilityContext);
}
}

Просмотреть файл

@ -24,12 +24,12 @@ import {
} from '../EventStream/EventStreamEvents';
import {exec} from '@vscode/sudo-prompt';
import { ICommandExecutor } from './ICommandExecutor';
import { IEventStream } from '../EventStream/EventStream';
import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
import { IUtilityContext } from './IUtilityContext';
import { IWindowDisplayWorker } from '../EventStream/IWindowDisplayWorker';
import { CommandExecutorCommand } from './ICommandExecutor';
import { IDotnetAcquireContext } from '../IDotnetAcquireContext';
import { getInstallKeyFromContext } from '../IDotnetAcquireContext';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
/* tslint:disable:no-any */
@ -37,9 +37,9 @@ export class CommandExecutor extends ICommandExecutor
{
private pathTroubleshootingOption = 'Troubleshoot';
constructor(eventStream : IEventStream, utilContext : IUtilityContext, acquireContext? : IDotnetAcquireContext)
constructor(context : IAcquisitionWorkerContext, utilContext : IUtilityContext)
{
super(eventStream, utilContext, acquireContext);
super(context, utilContext);
}
/**
@ -63,7 +63,7 @@ export class CommandExecutor extends ICommandExecutor
private async ExecSudoAsync(command : CommandExecutorCommand, terminalFailure = true) : Promise<string>
{
const fullCommandString = CommandExecutor.prettifyCommandExecutorCommand(command, false);
this.eventStream.post(new CommandExecutionUnderSudoEvent(`The command ${fullCommandString} is being ran under sudo.`));
this.context.eventStream.post(new CommandExecutionUnderSudoEvent(`The command ${fullCommandString} is being ran under sudo.`));
if(this.isRunningUnderWSL())
{
@ -73,8 +73,9 @@ export class CommandExecutor extends ICommandExecutor
// We had a working implementation that opens a vscode box and gets the user password, but that will require more security analysis.
const err = new DotnetWSLSecurityError(new Error(`Automatic .NET SDK Installation is not yet supported in WSL due to VS Code & WSL limitations.
Please install the .NET SDK manually by following https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu. Then, add it to the path by following https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md#manually-installing-net`));
this.eventStream.post(err);
Please install the .NET SDK manually by following https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu. Then, add it to the path by following https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md#manually-installing-net`,
), getInstallKeyFromContext(this.context.acquisitionContext!));
this.context.eventStream.post(err);
throw err.error;
}
@ -82,27 +83,27 @@ Please install the .NET SDK manually by following https://learn.microsoft.com/en
return new Promise<string>((resolve, reject) =>
{
// The '.' character is not allowed for sudo-prompt so we use 'NET'
const options = { name: `${this.acquisitionContext?.requestingExtensionId} On behalf of NET Install Tool` };
const options = { name: `${this.context.acquisitionContext?.requestingExtensionId} On behalf of NET Install Tool` };
exec((fullCommandString), options, (error?: any, stdout?: any, stderr?: any) =>
{
let commandResultString = '';
if (stdout)
{
this.eventStream.post(new CommandExecutionStdOut(`The command ${fullCommandString} encountered stdout, continuing
this.context.eventStream.post(new CommandExecutionStdOut(`The command ${fullCommandString} encountered stdout, continuing
${stdout}`));
commandResultString += stdout;
}
if (stderr)
{
this.eventStream.post(new CommandExecutionStdError(`The command ${fullCommandString} encountered stderr, continuing
this.context.eventStream.post(new CommandExecutionStdError(`The command ${fullCommandString} encountered stderr, continuing
${stderr}`));
commandResultString += stderr;
}
if (error)
{
this.eventStream.post(new CommandExecutionUserCompletedDialogueEvent(`The command ${fullCommandString} failed to run under sudo.`));
this.context.eventStream.post(new CommandExecutionUserCompletedDialogueEvent(`The command ${fullCommandString} failed to run under sudo.`));
if(terminalFailure)
{
reject(error);
@ -114,7 +115,7 @@ ${stderr}`));
}
else
{
this.eventStream.post(new CommandExecutionUserCompletedDialogueEvent(`The command ${fullCommandString} successfully ran under sudo.`));
this.context.eventStream.post(new CommandExecutionUserCompletedDialogueEvent(`The command ${fullCommandString} successfully ran under sudo.`));
resolve(this.returnStatus ? '0' : commandResultString);
}
});
@ -134,7 +135,7 @@ ${stderr}`));
/**
*
* @param workingDirectory The directory to execute in. Only works for non sudo commands.
* @param terminalFailure Whether to throw up an error when executing under sudo or supress it and return stderr
* @param terminalFailure Whether to throw up an error when executing under sudo or suppress it and return stderr
* @returns the result(s) of each command. Can throw generically if the command fails.
*/
public async execute(command : CommandExecutorCommand, options : any | null = null, terminalFailure = true) : Promise<string>
@ -151,14 +152,14 @@ ${stderr}`));
}
else
{
this.eventStream.post(new CommandExecutionEvent(`Executing command ${fullCommandStringForTelemetryOnly}
this.context.eventStream.post(new CommandExecutionEvent(`Executing command ${fullCommandStringForTelemetryOnly}
with options ${options}.`));
const commandResult = proc.spawnSync(command.commandRoot, command.commandParts, options);
if(this.returnStatus)
{
if(commandResult.status !== null)
{
this.eventStream.post(new CommandExecutionStatusEvent(`The command ${fullCommandStringForTelemetryOnly} exited
this.context.eventStream.post(new CommandExecutionStatusEvent(`The command ${fullCommandStringForTelemetryOnly} exited
with status: ${commandResult.status.toString()}.`));
return commandResult.status.toString() ?? '';
}
@ -167,13 +168,13 @@ with status: ${commandResult.status.toString()}.`));
// A signal is generally given if a status is not given, and they are 'equivalent' enough
if(commandResult.signal !== null)
{
this.eventStream.post(new CommandExecutionSignalSentEvent(`The command ${fullCommandStringForTelemetryOnly} exited
this.context.eventStream.post(new CommandExecutionSignalSentEvent(`The command ${fullCommandStringForTelemetryOnly} exited
with signal: ${commandResult.signal.toString()}.`));
return commandResult.signal.toString() ?? '';
}
else
{
this.eventStream.post(new CommandExecutionNoStatusCodeWarning(`The command ${fullCommandStringForTelemetryOnly} with
this.context.eventStream.post(new CommandExecutionNoStatusCodeWarning(`The command ${fullCommandStringForTelemetryOnly} with
result: ${commandResult.toString()} had no status or signal.`));
return '000751'; // Error code 000751 : The command did not report an exit code upon completion. This is never expected
}
@ -189,12 +190,12 @@ result: ${commandResult.toString()} had no status or signal.`));
{
if(commandResult.stdout)
{
this.eventStream.post(new CommandExecutionStdOut(`The command ${fullCommandStringForTelemetryOnly} encountered stdout:
this.context.eventStream.post(new CommandExecutionStdOut(`The command ${fullCommandStringForTelemetryOnly} encountered stdout:
${commandResult.stdout}`));
}
if(commandResult.stderr)
{
this.eventStream.post(new CommandExecutionStdError(`The command ${fullCommandStringForTelemetryOnly} encountered stderr:
this.context.eventStream.post(new CommandExecutionStdError(`The command ${fullCommandStringForTelemetryOnly} encountered stderr:
${commandResult.stderr}`));
}
return commandResult.stdout?.toString() + commandResult.stderr?.toString() ?? '';
@ -224,18 +225,18 @@ ${commandResult.stderr}`));
if(cmdFoundOutput === '0')
{
workingCommand = command;
this.eventStream.post(new DotnetAlternativeCommandFoundEvent(`The command ${command.commandRoot} was found.`));
this.context.eventStream.post(new DotnetAlternativeCommandFoundEvent(`The command ${command.commandRoot} was found.`));
break;
}
else
{
this.eventStream.post(new DotnetCommandNotFoundEvent(`The command ${command.commandRoot} was NOT found, no error was thrown.`));
this.context.eventStream.post(new DotnetCommandNotFoundEvent(`The command ${command.commandRoot} was NOT found, no error was thrown.`));
}
}
catch(err)
{
// Do nothing. The error should be raised higher up.
this.eventStream.post(new DotnetCommandNotFoundEvent(`The command ${command.commandRoot} was NOT found, and we caught any errors.`));
this.context.eventStream.post(new DotnetCommandNotFoundEvent(`The command ${command.commandRoot} was NOT found, and we caught any errors.`));
}
};

Просмотреть файл

@ -11,6 +11,9 @@ import {
import { ExistingPathKeys, IExistingPath } from '../IExtensionContext';
import { IIssueContext } from './IIssueContext';
import { formatIssueUrl } from './IssueReporter';
import { DotnetNotInstallRelatedCommandFailed } from '../EventStream/EventStreamEvents';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
import { getInstallKeyFromContext } from '../IDotnetAcquireContext';
export enum AcquireErrorConfiguration {
DisplayAllErrorPopups = 0,
@ -45,66 +48,96 @@ Note that our websites may be blocked in China or experience significant timeout
let showMessage = true;
export async function callWithErrorHandling<T>(callback: () => T, context: IIssueContext, requestingExtensionId?: string): Promise<T | undefined> {
try {
export async function callWithErrorHandling<T>(callback: () => T, context: IIssueContext, requestingExtensionId?: string, acquireContext? : IAcquisitionWorkerContext): Promise<T | undefined> {
const isAcquisitionError = acquireContext ? true : false;
try
{
const result = await callback();
context.eventStream.post(new DotnetCommandSucceeded(context.commandName));
return result;
} catch (caughtError) {
}
catch (caughtError)
{
const error = caughtError as Error;
context.eventStream.post(new DotnetCommandFailed(error, context.commandName));
if (context.errorConfiguration === AcquireErrorConfiguration.DisplayAllErrorPopups) {
context.eventStream.post(isAcquisitionError ?
new DotnetCommandFailed(error, context.commandName, getInstallKeyFromContext(acquireContext?.acquisitionContext!)) :
new DotnetNotInstallRelatedCommandFailed(error, context.commandName)
);
if (context.errorConfiguration === AcquireErrorConfiguration.DisplayAllErrorPopups)
{
if ((error.message as string).includes(timeoutConstants.timeoutMessage))
{
context.displayWorker.showErrorMessage(`${errorConstants.errorMessage}${ context.version ? ` (${context.version})` : '' }: ${ error.message }`,
async (response: string | undefined) => {
if (response === timeoutConstants.moreInfoOption) {
if (response === timeoutConstants.moreInfoOption)
{
open(context.timeoutInfoUrl);
}
}, timeoutConstants.moreInfoOption);
} else if (error.constructor.name !== 'UserCancelledError' && showMessage) {
}
else if (error.constructor.name !== 'UserCancelledError' && showMessage)
{
let errorOptions = [errorConstants.reportOption, errorConstants.hideOption, errorConstants.moreInfoOption];
if (requestingExtensionId) {
if (requestingExtensionId)
{
errorOptions = errorOptions.concat(errorConstants.configureManuallyOption);
}
context.displayWorker.showErrorMessage(`${errorConstants.errorMessage}${ context.version ? ` (${context.version})` : '' }: ${ error.message }`,
async (response: string | undefined) => {
if (response === errorConstants.moreInfoOption) {
async (response: string | undefined) =>
{
if (response === errorConstants.moreInfoOption)
{
open(context.moreInfoUrl);
} else if (response === errorConstants.hideOption) {
}
else if (response === errorConstants.hideOption)
{
showMessage = false;
} else if (response === errorConstants.reportOption) {
}
else if (response === errorConstants.reportOption)
{
const [url, issueBody] = formatIssueUrl(error, context);
context.displayWorker.copyToUserClipboard(issueBody);
open(url);
} else if (response === errorConstants.configureManuallyOption && requestingExtensionId) {
}
else if (response === errorConstants.configureManuallyOption && requestingExtensionId)
{
await configureManualInstall(context, requestingExtensionId);
}
}, ...errorOptions);
}
}
return undefined;
} finally {
}
finally
{
context.logger.dispose();
}
}
async function configureManualInstall(context: IIssueContext, requestingExtensionId: string): Promise<void> {
const manualPath = await context.displayWorker.displayPathConfigPopUp();
if (manualPath && fs.existsSync(manualPath)) {
try {
if (manualPath && fs.existsSync(manualPath))
{
try
{
let configVal: IExistingPath[] = [{ [ExistingPathKeys.extensionIdKey]: requestingExtensionId, [ExistingPathKeys.pathKey] : manualPath}];
const existingConfigVal = context.extensionConfigWorker.getPathConfigurationValue();
if (existingConfigVal) {
if (existingConfigVal)
{
configVal = configVal.concat(existingConfigVal);
}
await context.extensionConfigWorker.setPathConfigurationValue(configVal);
context.displayWorker.showInformationMessage(`Set .NET path to ${manualPath}. Please reload VSCode to apply settings.`, () => { /* No callback needed */});
} catch (e) {
}
catch (e)
{
context.displayWorker.showWarningMessage(`Failed to configure the path: ${(e as Error).toString()}`, () => { /* No callback needed */ });
}
} else {
}
else
{
context.displayWorker.showWarningMessage('Manually configured path was not valid.', () => { /* No callback needed */ });
}
}

Просмотреть файл

@ -6,6 +6,7 @@
/* tslint:disable:no-any */
import { IDotnetAcquireContext } from '..';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
import { IEventStream } from '../EventStream/EventStream';
import { IUtilityContext } from './IUtilityContext';
@ -26,16 +27,12 @@ export type CommandExecutorCommand =
export abstract class ICommandExecutor
{
constructor(eventStream : IEventStream, utilContext : IUtilityContext, acquireContext? : IDotnetAcquireContext)
constructor(protected readonly context : IAcquisitionWorkerContext, utilContext : IUtilityContext)
{
this.eventStream = eventStream;
this.utilityContext = utilContext;
this.acquisitionContext = acquireContext;
}
protected eventStream : IEventStream;
protected utilityContext : IUtilityContext;
protected acquisitionContext? : IDotnetAcquireContext;
/**
* @remarks Set this to true if you don't want to capture stdout and stderr, and just want to return the status / exit code.

Просмотреть файл

@ -34,6 +34,7 @@ import { IVSCodeExtensionContext } from '../../IVSCodeExtensionContext';
import { IUtilityContext } from '../../Utils/IUtilityContext';
import { IVSCodeEnvironment } from '../../Utils/IVSCodeEnvironment';
import { IDotnetAcquireContext } from '../..';
import { IDotnetCoreAcquisitionWorker } from '../../Acquisition/IDotnetCoreAcquisitionWorker';
const testDefaultTimeoutTimeMs = 60000;
/* tslint:disable:no-any */
@ -107,9 +108,8 @@ export class ErrorAcquisitionInvoker extends IAcquisitionInvoker {
export const versionPairs = [['1.0', '1.0.16'], ['1.1', '1.1.13'], ['2.0', '2.0.9'], ['2.1', '2.1.14'], ['2.2', '2.2.8']];
export class FileWebRequestWorker extends WebRequestWorker {
constructor(extensionState: IExtensionState, eventStream: IEventStream, uri: string, extensionStateKey: string,
private readonly mockFilePath: string) {
super(extensionState, eventStream, uri, testDefaultTimeoutTimeMs);
constructor(ctx : IAcquisitionWorkerContext, uri: string, private readonly mockFilePath: string) {
super(ctx, uri);
}
protected async makeWebRequest(): Promise<string | undefined> {
@ -119,8 +119,8 @@ export class FileWebRequestWorker extends WebRequestWorker {
}
export class FailingWebRequestWorker extends WebRequestWorker {
constructor(extensionState: IExtensionState, eventStream: IEventStream, uri: string) {
super(extensionState, eventStream, '', testDefaultTimeoutTimeMs); // Empty string as uri to cause failure. Uri is required to match the interface even though it's unused.
constructor(ctx : IAcquisitionWorkerContext, uri: string) {
super(ctx, '', testDefaultTimeoutTimeMs); // Empty string as uri to cause failure. Uri is required to match the interface even though it's unused.
}
public async getCachedData(): Promise<string | undefined> {
@ -132,10 +132,10 @@ export class MockTrackingWebRequestWorker extends WebRequestWorker {
private requestCount = 0;
public response = 'Mock Web Request Result';
constructor(extensionState: IExtensionState, eventStream: IEventStream, url: string,
constructor(ctx : IAcquisitionWorkerContext, url: string,
protected readonly succeed = true, webTimeToLive = testDefaultTimeoutTimeMs, cacheTimeToLive = testDefaultTimeoutTimeMs)
{
super(extensionState, eventStream, url, webTimeToLive, '', cacheTimeToLive);
super(ctx, url, cacheTimeToLive);
}
public getRequestCount() {
@ -158,8 +158,8 @@ export class MockWebRequestWorker extends MockTrackingWebRequestWorker {
public readonly errorMessage = 'Web Request Failed';
public response = 'Mock Web Request Result';
constructor(extensionState: IExtensionState, eventStream: IEventStream, url: string) {
super(extensionState, eventStream, url);
constructor(ctx : IAcquisitionWorkerContext, url: string) {
super(ctx, url);
}
protected async makeWebRequest(): Promise<string | undefined> {
@ -185,10 +185,10 @@ export class MockIndexWebRequestWorker extends WebRequestWorker {
``
];
constructor(extensionState: IExtensionState, eventStream: IEventStream, url: string,
constructor(ctx : IAcquisitionWorkerContext, url: string,
protected readonly succeed = true, webTimeToLive = testDefaultTimeoutTimeMs, cacheTimeToLive = testDefaultTimeoutTimeMs)
{
super(extensionState, eventStream, url, webTimeToLive, '', cacheTimeToLive);
super(ctx, url, cacheTimeToLive);
}
public async getCachedData(retriesCount = 2): Promise<string | undefined>
@ -225,18 +225,18 @@ export class MockVSCodeEnvironment extends IVSCodeEnvironment
export class MockVersionResolver extends VersionResolver {
private readonly filePath = path.join(__dirname, '../../..', 'src', 'test', 'mocks', 'mock-releases.json');
constructor(extensionState: IExtensionState, eventStream: IEventStream) {
super(extensionState, eventStream, testDefaultTimeoutTimeMs);
this.webWorker = new FileWebRequestWorker(extensionState, eventStream, '', 'releases', this.filePath);
constructor(ctx : IAcquisitionWorkerContext) {
super(ctx);
this.webWorker = new FileWebRequestWorker(ctx, 'releases', this.filePath);
}
}
export class MockInstallScriptWorker extends InstallScriptAcquisitionWorker {
constructor(extensionState: IExtensionState, eventStream: IEventStream, acquireContext : IDotnetAcquireContext, failing: boolean, private fallback = false) {
super(extensionState, eventStream, testDefaultTimeoutTimeMs, acquireContext);
constructor(ctx : IAcquisitionWorkerContext, failing: boolean, private fallback = false) {
super(ctx);
this.webWorker = failing ?
new FailingWebRequestWorker(extensionState, eventStream, '') :
new MockWebRequestWorker(extensionState, eventStream, '');
new FailingWebRequestWorker(ctx, '') :
new MockWebRequestWorker(ctx, '');
}
protected getFallbackScriptPath(): string {
@ -251,8 +251,8 @@ export class MockInstallScriptWorker extends InstallScriptAcquisitionWorker {
export class MockApostropheScriptAcquisitionWorker extends MockInstallScriptWorker
{
protected readonly scriptFilePath: string;
constructor(extensionState: IExtensionState, eventStream: IEventStream, installFolder: string, acquireContext : IDotnetAcquireContext) {
super(extensionState, eventStream, acquireContext, false);
constructor(ctx : IAcquisitionWorkerContext, installFolder: string) {
super(ctx, false);
const scriptFileEnding = 'win32';
const scriptFileName = 'dotnet-install';
this.scriptFilePath = path.join(installFolder, 'install scripts', `${scriptFileName}.${scriptFileEnding}`);
@ -263,9 +263,9 @@ export class MockApostropheScriptAcquisitionWorker extends MockInstallScriptWork
export class MockAcquisitionInvoker extends AcquisitionInvoker
{
protected readonly scriptWorker: MockApostropheScriptAcquisitionWorker
constructor(extensionState: IExtensionState, eventStream: IEventStream, timeoutTime : number, installFolder : string, acquireContext : IDotnetAcquireContext) {
super(extensionState, eventStream, timeoutTime, getMockUtilityContext());
this.scriptWorker = new MockApostropheScriptAcquisitionWorker(extensionState, eventStream, installFolder, acquireContext);
constructor(ctx : IAcquisitionWorkerContext, installFolder : string) {
super(ctx, getMockUtilityContext());
this.scriptWorker = new MockApostropheScriptAcquisitionWorker(ctx, installFolder);
}
}
@ -283,10 +283,10 @@ export class MockCommandExecutor extends ICommandExecutor
public otherCommandsToMock : string[] = [];
public otherCommandsReturnValues : string[] = [];
constructor(eventStream : IEventStream, utilContext : IUtilityContext)
constructor(acquisitionContext : IAcquisitionWorkerContext, utilContext : IUtilityContext)
{
super(eventStream, utilContext);
this.trueExecutor = new CommandExecutor(eventStream, utilContext);
super(acquisitionContext, utilContext);
this.trueExecutor = new CommandExecutor(acquisitionContext, utilContext);
}
public async execute(command: CommandExecutorCommand, options : object | null = null, terminalFailure? : boolean): Promise<string>
@ -447,9 +447,9 @@ export class MockDistroProvider extends IDistroDotnetSDKProvider
export class FailingInstallScriptWorker extends InstallScriptAcquisitionWorker {
constructor(extensionState: IExtensionState, eventStream: IEventStream, acquireContext : IDotnetAcquireContext) {
super(extensionState, eventStream, testDefaultTimeoutTimeMs, acquireContext);
this.webWorker = new MockWebRequestWorker(extensionState, eventStream, '');
constructor(ctx : IAcquisitionWorkerContext) {
super(ctx);
this.webWorker = new MockWebRequestWorker(ctx, '');
}
public getDotnetInstallScriptPath() : Promise<string> {

Просмотреть файл

@ -8,10 +8,7 @@ import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';
import { DotnetCoreAcquisitionWorker } from '../../Acquisition/DotnetCoreAcquisitionWorker';
import { RuntimeInstallationDirectoryProvider } from '../../Acquisition/RuntimeInstallationDirectoryProvider';
import { SdkInstallationDirectoryProvider } from '../../Acquisition/SdkInstallationDirectoryProvider';
import {
DotnetAcquisitionAlreadyInstalled,
DotnetAcquisitionCompleted,
DotnetAcquisitionStarted,
DotnetAcquisitionStatusResolved,
@ -23,17 +20,15 @@ import {
} from '../../EventStream/EventStreamEvents';
import { EventType } from '../../EventStream/EventType';
import {
ErrorAcquisitionInvoker,
MockAcquisitionInvoker,
MockDotnetCoreAcquisitionWorker,
MockEventStream,
MockExtensionContext,
MockInstallationValidator,
MockVSCodeExtensionContext,
NoInstallAcquisitionInvoker,
RejectingAcquisitionInvoker,
} from '../mocks/MockObjects';
import { getMockUtilityContext } from './TestUtility';
import { getMockAcquisitionContext, getMockAcquisitionWorker } from './TestUtility';
import { IAcquisitionInvoker } from '../../Acquisition/IAcquisitionInvoker';
const assert = chai.assert;
chai.use(chaiAsPromised);
const expectedTimeoutTime = 6000;
@ -43,39 +38,12 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
const installedVersionsKey = 'installed';
const dotnetFolderName = `.dotnet O'Hare O'Donald`;
function getTestAcquisitionWorker(runtimeInstall: boolean, arch? : string | null,
customEventStream? : MockEventStream, customContext? : MockExtensionContext): [MockDotnetCoreAcquisitionWorker, MockEventStream, MockExtensionContext]
{
const context = customContext ?? new MockExtensionContext();
const eventStream = customEventStream ?? new MockEventStream();
const acquisitionWorker = new MockDotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new NoInstallAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutSeconds: 10,
installDirectoryProvider: runtimeInstall ? new RuntimeInstallationDirectoryProvider('') : new SdkInstallationDirectoryProvider(''),
installingArchitecture: arch,
isExtensionTelemetryInitiallyEnabled: true,
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
return [acquisitionWorker, eventStream, context];
}
function getTestApostropheAcquisitionWorker(runtimeInstall: boolean, installApostropheFolder : string): [DotnetCoreAcquisitionWorker, MockEventStream, MockExtensionContext] {
function setupWorker(isRuntimeWorker : boolean): [MockDotnetCoreAcquisitionWorker, MockEventStream, MockExtensionContext, IAcquisitionInvoker] {
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new MockAcquisitionInvoker(context, eventStream, 10, installApostropheFolder),
installationValidator: new MockInstallationValidator(eventStream),
timeoutSeconds: 10,
installDirectoryProvider: runtimeInstall ? new RuntimeInstallationDirectoryProvider('') : new SdkInstallationDirectoryProvider(''),
isExtensionTelemetryInitiallyEnabled: true,
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
return [acquisitionWorker, eventStream, context];
const acquisitionWorker = getMockAcquisitionWorker(isRuntimeWorker, undefined, eventStream, context);
const invoker = new NoInstallAcquisitionInvoker(eventStream);
return [acquisitionWorker, eventStream, context, invoker];
}
function getExpectedPath(version: string, isRuntimeInstall: boolean): string {
@ -124,34 +92,34 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
process.env._VSCODE_DOTNET_INSTALL_FOLDER = dotnetFolderName;
});
async function AssertInstallRuntime(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string)
async function AssertInstallRuntime(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string, invoker : IAcquisitionInvoker)
{
const installKey = acquisitionWorker.getInstallKey(version);
const result = await acquisitionWorker.acquireRuntime(version);
const result = await acquisitionWorker.acquireRuntime(version, invoker);
await assertAcquisitionSucceeded(installKey, result.dotnetPath, eventStream, context);
}
async function AssertInstallSDK(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string)
async function AssertInstallSDK(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string, invoker : IAcquisitionInvoker)
{
const installKey = acquisitionWorker.getInstallKey(version);
const result = await acquisitionWorker.acquireSDK(version);
const result = await acquisitionWorker.acquireSDK(version, invoker);
await assertAcquisitionSucceeded(installKey, result.dotnetPath, eventStream, context, false);
}
test('Acquire Runtime Version', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(true);
const version = '1.0';
await AssertInstallRuntime(acquisitionWorker, context, eventStream, version);
await AssertInstallRuntime(acquisitionWorker, context, eventStream, version, invoker);
});
test('Acquire SDK Version', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(false);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(false);
const version = '5.0';
await AssertInstallSDK(acquisitionWorker, context, eventStream, version);
await AssertInstallSDK(acquisitionWorker, context, eventStream, version, invoker);
});
test('Acquire SDK Status', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(false);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(false);
const version = '5.0';
const installKey = acquisitionWorker.getInstallKey(version);
let result = await acquisitionWorker.acquireStatus(version, false);
@ -159,7 +127,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
const undefinedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusUndefined);
assert.exists(undefinedEvent, 'Undefined event exists');
await acquisitionWorker.acquireSDK(version);
await acquisitionWorker.acquireSDK(version, invoker);
result = await acquisitionWorker.acquireStatus(version, false);
await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, context, false);
const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved);
@ -167,7 +135,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Acquire Runtime Status', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(false);
const version = '5.0';
const installKey = acquisitionWorker.getInstallKey(version);
let result = await acquisitionWorker.acquireStatus(version, true);
@ -175,7 +143,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
const undefinedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusUndefined);
assert.exists(undefinedEvent);
await acquisitionWorker.acquireSDK(version);
await acquisitionWorker.acquireSDK(version, invoker);
result = await acquisitionWorker.acquireStatus(version, true);
await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, context, true);
const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved);
@ -184,11 +152,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
test('Acquire Runtime Version Multiple Times', async () => {
const numAcquisitions = 3;
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(true);
for (let i = 0; i < numAcquisitions; i++) {
const version = '1.0';
const pathResult = await acquisitionWorker.acquireRuntime(version);
const pathResult = await acquisitionWorker.acquireRuntime(version, invoker);
const installKey = acquisitionWorker.getInstallKey(version);
await assertAcquisitionSucceeded(installKey, pathResult.dotnetPath, eventStream, context);
}
@ -199,11 +167,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Acquire Multiple Versions and UninstallAll', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(true);
const versions = ['1.0', '1.1', '2.0', '2.1', '2.2'];
for (const version of versions) {
const installKey = acquisitionWorker.getInstallKey(version);
const res = await acquisitionWorker.acquireRuntime(version);
const res = await acquisitionWorker.acquireRuntime(version, invoker);
await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, context);
}
await acquisitionWorker.uninstallAll();
@ -214,11 +182,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Acquire Runtime and UninstallAll', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(true);
const version = '1.0';
const installKey = acquisitionWorker.getInstallKey(version);
const res = await acquisitionWorker.acquireRuntime(version);
const res = await acquisitionWorker.acquireRuntime(version, invoker);
await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, context);
await acquisitionWorker.uninstallAll();
@ -229,16 +197,16 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Graveyard Removes Failed Uninstalls', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
const [acquisitionWorker, eventStream, context, invoker] = setupWorker(true);
const version = '1.0';
const installKey = acquisitionWorker.getInstallKey(version);
const res = await acquisitionWorker.acquireRuntime(version);
const res = await acquisitionWorker.acquireRuntime(version, invoker);
await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, context);
acquisitionWorker.AddToGraveyard(installKey, 'Not applicable');
const versionToKeep = '5.0';
const versionToKeepKey = acquisitionWorker.getInstallKey(versionToKeep);
await acquisitionWorker.acquireRuntime(versionToKeep);
await acquisitionWorker.acquireRuntime(versionToKeep, invoker);
assert.exists(eventStream.events.find(event => event instanceof DotnetInstallGraveyardEvent), 'The graveyard tried to uninstall .NET');
assert.isEmpty(context.get<string[]>(installingVersionsKey, []), 'We did not hang/ get interrupted during the install.');
@ -252,19 +220,19 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
const sdkV5 = '5.0.100';
const sdkV6 = '6.0.100';
const [runtimeWorker, events, context] = getTestAcquisitionWorker(true, null);
const [runtimeWorker, events, context, runtimeInvoker] = setupWorker(true);
// Install 5.0, 6.0 runtime without an architecture
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5);
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV6);
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5, runtimeInvoker);
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV6, runtimeInvoker);
// Install similar SDKs without an architecture.
const [sdkWorker, sdkEvents, sdkContext] = getTestAcquisitionWorker(false, null);
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV5);
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV6);
const [sdkWorker, sdkEvents, sdkContext, sdkInvoker] = setupWorker(false);
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV5, sdkInvoker);
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV6, sdkInvoker);
// Install 5.0 runtime with an architecture. Share the same event stream and context.
runtimeWorker.installingArchitecture = os.arch();
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5);
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5, runtimeInvoker);
// 5.0 legacy runtime should be replaced, but 6.0 runtime should remain, and all SDK items should remain.
let remainingInstalls = context.get<string[]>(installedVersionsKey, []).concat(sdkContext.get<string[]>(installedVersionsKey, []));
@ -273,11 +241,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
// Install a legacy runtime again to make sure its not removed when installing a new SDK with the same version
runtimeWorker.installingArchitecture = null;
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5);
await AssertInstallRuntime(runtimeWorker, context, events, runtimeV5, runtimeInvoker);
// Install non-legacy SDK
sdkWorker.installingArchitecture = os.arch();
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV5);
await AssertInstallSDK(sdkWorker, sdkContext, sdkEvents, sdkV5, runtimeInvoker);
// 6.0 sdk legacy should remain, as well as 5.0 and 6.0 runtime. 5.0 SDK should be removed.
remainingInstalls = context.get<string[]>(installedVersionsKey, []).concat(sdkContext.get<string[]>(installedVersionsKey, []));
@ -286,9 +254,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Repeated Acquisition', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(true);
for (let i = 0; i < 3; i++) {
await acquisitionWorker.acquireRuntime('1.0');
const [acquisitionWorker, eventStream, _, invoker] = setupWorker(true);
for (let i = 0; i < 3; i++)
{
await acquisitionWorker.acquireRuntime('1.0', invoker);
}
// We should only actually Acquire once
const events = eventStream.events.filter(event => event instanceof DotnetAcquisitionStarted);
@ -296,26 +266,18 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
});
test('Error is Redirected on Acquisition Failure', async () => {
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new RejectingAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutSeconds: 10,
installDirectoryProvider: new RuntimeInstallationDirectoryProvider(''),
isExtensionTelemetryInitiallyEnabled: true,
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
const [acquisitionWorker, eventStream, _] = setupWorker(true);
const acquisitionInvoker = new RejectingAcquisitionInvoker(eventStream);
return assert.isRejected(acquisitionWorker.acquireRuntime('1.0'), '.NET Acquisition Failed: Installation failed: Rejecting message');
return assert.isRejected(acquisitionWorker.acquireRuntime('1.0', acquisitionInvoker), '.NET Acquisition Failed: Installation failed: Rejecting message');
});
test('Repeated SDK Acquisition', async () => {
const [acquisitionWorker, eventStream, context] = getTestAcquisitionWorker(false);
for (let i = 0; i < 3; i++) {
await acquisitionWorker.acquireSDK('5.0');
const [acquisitionWorker, eventStream, _, invoker] = setupWorker(true);
for (let i = 0; i < 3; i++)
{
await acquisitionWorker.acquireSDK('5.0', invoker);
}
// We should only actually Acquire once
const events = eventStream.events.filter(event => event instanceof DotnetAcquisitionStarted);
@ -325,10 +287,13 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () {
test('Get Expected Path With Apostrophe In Install path', async () => {
if(os.platform() === 'win32'){
const installApostropheFolder = `test' for' apostrophe`;
const [acquisitionWorker, _, context] = getTestApostropheAcquisitionWorker(true, installApostropheFolder);
const acquisitionContext = getMockAcquisitionContext(false);
const acquisitionWorker = getMockAcquisitionWorker(true);
const acquisitionInvoker = new MockAcquisitionInvoker(acquisitionContext, installApostropheFolder);
const version = '1.0';
const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, os.arch());
const result = await acquisitionWorker.acquireRuntime(version);
const result = await acquisitionWorker.acquireRuntime(version, acquisitionInvoker);
const expectedPath = getExpectedPath(installKey, true);
assert.equal(result.dotnetPath, expectedPath);
deleteFolderRecursive(path.join(process.cwd(), installApostropheFolder));

Просмотреть файл

@ -8,6 +8,7 @@ import * as os from 'os';
import { FileWebRequestWorker, MockEventStream, MockExtensionContext } from '../mocks/MockObjects';
import { GlobalInstallerResolver } from '../../Acquisition/GlobalInstallerResolver';
import path = require('path');
import { getMockAcquisitionContext } from './TestUtility';
const assert = chai.assert;
const mockVersion = '7.0.306';
@ -18,21 +19,22 @@ const majorMinorOnly = '7.0';
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
const acquisitionContext = getMockAcquisitionContext(true);
const filePath = path.join(__dirname, '../../..', 'src', 'test', 'mocks', 'mock-channel-7-index.json');
const webWorker = new FileWebRequestWorker(context, eventStream, '', '', filePath);
const webWorker = new FileWebRequestWorker(acquisitionContext, '', filePath);
const timeoutTime = 10000;
suite('Global Installer Resolver Tests', () =>
{
test('It finds the newest patch version given a feature band', async () => {
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(context, eventStream, featureBandVersion, timeoutTime, undefined);
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, featureBandVersion);
provider.customWebRequestWorker = webWorker;
assert.equal(await provider.getFullySpecifiedVersion(), newestFeatureBandedVersion);
});
test('It finds the correct installer download url for the os', async () => {
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(context, eventStream, mockVersion, timeoutTime, undefined);
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, mockVersion);
provider.customWebRequestWorker = webWorker;
assert.equal(await provider.getFullySpecifiedVersion(), mockVersion);
@ -50,21 +52,21 @@ suite('Global Installer Resolver Tests', () =>
});
test('It parses the major format', async () => {
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(context, eventStream, majorMinorOnly, timeoutTime, undefined);
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, majorMinorOnly);
provider.customWebRequestWorker = webWorker;
assert.equal(await provider.getFullySpecifiedVersion(), mockVersion);
});
test('It parses the major.minor format', async () => {
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(context, eventStream, majorOnly, timeoutTime, undefined);
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, majorOnly);
provider.customWebRequestWorker = webWorker;
assert.equal(await provider.getFullySpecifiedVersion(), mockVersion);
});
test('It rejects correctly with undiscoverable feature band', async () => {
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(context, eventStream, '7.0.500', timeoutTime, undefined);
const provider : GlobalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, '7.0.500');
provider.customWebRequestWorker = webWorker;
assert.isRejected(provider.getFullySpecifiedVersion());

Просмотреть файл

@ -14,7 +14,8 @@ const assert = chai.assert;
const standardTimeoutTime = 100000;
const mockVersion = '7.0.103';
const mockExecutor = new MockCommandExecutor(new MockEventStream(), getMockUtilityContext());
const acquisitionContext = getMockAcquisitionContext(false);
const mockExecutor = new MockCommandExecutor(acquisitionContext, getMockUtilityContext());
const pair : DistroVersionPair = { distro : 'Ubuntu', version : '22.04' };
const provider : GenericDistroSDKProvider = new GenericDistroSDKProvider(pair, getMockAcquisitionContext(false), getMockUtilityContext(), mockExecutor);
const shouldRun = os.platform() === 'linux';

Просмотреть файл

@ -17,7 +17,8 @@ const assert = chai.assert;
suite('Linux Version Resolver Tests', () =>
{
const mockVersion = '7.0.103';
const mockExecutor = new MockCommandExecutor(new MockEventStream(), getMockUtilityContext());
const acquisitionContext = getMockAcquisitionContext(false);
const mockExecutor = new MockCommandExecutor(acquisitionContext, getMockUtilityContext());
const pair : DistroVersionPair = { distro : 'Ubuntu', version : '22.04' };
const redHatPair: DistroVersionPair = { distro : 'Red Hat Enterprise Linux', version : '7.3' };
const shouldRun = os.platform() === 'linux';

Просмотреть файл

@ -14,7 +14,8 @@ const assert = chai.assert;
const standardTimeoutTime = 100000;
const mockVersion = '7.0.103';
const mockExecutor = new MockCommandExecutor(new MockEventStream(), getMockUtilityContext());
const acquisitionContext = getMockAcquisitionContext(false);
const mockExecutor = new MockCommandExecutor(acquisitionContext, getMockUtilityContext());
const pair : DistroVersionPair = { distro : 'Red Hat Enterprise Linux', version : '9.0' };
const provider : RedHatDistroSDKProvider = new RedHatDistroSDKProvider(pair, getMockAcquisitionContext(false), getMockUtilityContext(), mockExecutor);
const shouldRun = os.platform() === 'linux';

Просмотреть файл

@ -9,26 +9,37 @@ import { IAcquisitionWorkerContext } from '../../Acquisition/IAcquisitionWorkerC
import { RuntimeInstallationDirectoryProvider } from '../../Acquisition/RuntimeInstallationDirectoryProvider';
import { SdkInstallationDirectoryProvider } from '../../Acquisition/SdkInstallationDirectoryProvider';
import { IUtilityContext } from '../../Utils/IUtilityContext';
import { MockEventStream, MockExtensionContext, MockInstallationValidator, MockVSCodeEnvironment, NoInstallAcquisitionInvoker } from '../mocks/MockObjects';
import { MockDotnetCoreAcquisitionWorker, MockEventStream, MockExtensionContext, MockInstallationValidator, MockVSCodeEnvironment, MockVSCodeExtensionContext, NoInstallAcquisitionInvoker } from '../mocks/MockObjects';
import { MockWindowDisplayWorker } from '../mocks/MockWindowDisplayWorker';
import { IEventStream } from '../../EventStream/EventStream';
const standardTimeoutTime = 100000;
export function getMockAcquisitionContext(runtimeInstall: boolean, timeoutTime : number = standardTimeoutTime): IAcquisitionWorkerContext{
const extensionContext = new MockExtensionContext();
const eventStream = new MockEventStream();
const workerContext : IAcquisitionWorkerContext = {
export function getMockAcquisitionContext(runtimeInstall: boolean, timeoutTime : number = standardTimeoutTime, customEventStream? : IEventStream,
customContext? : MockExtensionContext, arch? : string): IAcquisitionWorkerContext
{
const extensionContext = customContext ?? new MockExtensionContext();
const myEventStream = customEventStream ?? new MockEventStream();
const workerContext : IAcquisitionWorkerContext =
{
storagePath: '',
extensionState: extensionContext,
eventStream,
acquisitionInvoker: new NoInstallAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
eventStream: myEventStream,
installationValidator: new MockInstallationValidator(myEventStream),
timeoutSeconds: timeoutTime,
installingArchitecture: arch,
installDirectoryProvider: runtimeInstall ? new RuntimeInstallationDirectoryProvider('') : new SdkInstallationDirectoryProvider(''),
isExtensionTelemetryInitiallyEnabled: true
};
return workerContext;
}
export function getMockAcquisitionWorker(runtimeInstall: boolean, arch? : string, customEventStream? : MockEventStream, customContext? : MockExtensionContext) : MockDotnetCoreAcquisitionWorker
{
const acquisitionWorker = new MockDotnetCoreAcquisitionWorker(getMockAcquisitionContext(runtimeInstall, undefined, customEventStream, customContext, arch),
getMockUtilityContext(), new MockVSCodeExtensionContext());
return acquisitionWorker;
}
export function getMockUtilityContext() : IUtilityContext
{
const utilityContext : IUtilityContext = {

Просмотреть файл

@ -6,6 +6,7 @@ import * as chai from 'chai';
import { MockEventStream, MockExtensionContext, MockVersionResolver, versionPairs } from '../mocks/MockObjects';
import { IDotnetListVersionsResult } from '../../IDotnetListVersionsContext';
import { VersionResolver } from '../../Acquisition/VersionResolver';
import { getMockAcquisitionContext } from './TestUtility';
const assert = chai.assert;
const fullySpecifiedVersion = '7.0.201';
const twoDigitPatchVersion = '7.0.221';
@ -24,7 +25,7 @@ suite('VersionResolver Unit Tests', () => {
const context = new MockExtensionContext();
// MockVersionResolver is a VersionResolver that uses a fake releases.json
// (prevents us from making web requests in unit tests)
const resolver: MockVersionResolver = new MockVersionResolver(context, eventStream);
const resolver: MockVersionResolver = new MockVersionResolver(getMockAcquisitionContext(true));
test('Get Available Versions', async () => {
const result : IDotnetListVersionsResult = await resolver.GetAvailableDotnetVersions(undefined);
@ -87,24 +88,24 @@ suite('VersionResolver Unit Tests', () => {
test('Detects Unspecified Patch Version', async () => {
assert.equal(resolver.isNonSpecificFeatureBandedVersion(fullySpecifiedVersion), false, 'It detects versions with patches');
assert.equal(resolver.isNonSpecificFeatureBandedVersion(featureBandVersion), true, 'It detects versions with xx');
assert.equal(resolver.isNonSpecificFeatureBandedVersion(twoDigitMajorVersion), false, 'It doesnt error for non xx containing version');
assert.equal(resolver.isNonSpecificFeatureBandedVersion(twoDigitMajorVersion), false, 'It does not error for non xx containing version');
});
test('Detects if Fully Specified Version', async () => {
assert.equal(resolver.isFullySpecifiedVersion(fullySpecifiedVersion), true, 'It passes basic fully specified version');
assert.equal(resolver.isFullySpecifiedVersion(uniqueMajorMinorVersion), true);
assert.equal(resolver.isFullySpecifiedVersion(twoDigitMajorVersion), true, 'It works for 2+ digit major versions');
assert.equal(resolver.isFullySpecifiedVersion(majorOnly), false, 'It detects major only versions arent fully specified');
assert.equal(resolver.isFullySpecifiedVersion(majorOnly), false, 'It detects major only versions are not fully specified');
assert.equal(resolver.isFullySpecifiedVersion(featureBandVersion), false, 'It counts feature band only with xxx as not fully specified');
assert.equal(resolver.isFullySpecifiedVersion(majorMinorOnly), false, 'It detects major.minor as not fully specified');
});
test('Detects if Only Major or Minor Given', async () => {
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(fullySpecifiedVersion), false, 'It doesnt think a fully specified version is major.minor only');
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(fullySpecifiedVersion), false, 'It does not think a fully specified version is major.minor only');
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(uniqueMajorMinorVersion), false);
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(twoDigitMajorVersion), false);
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(majorOnly), true, 'It detects major only versions as major only versions');
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(featureBandVersion), false, 'It doesnt think xx versions are major minor versions');
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(featureBandVersion), false, 'It does not think xx versions are major minor versions');
assert.equal(resolver.isNonSpecificMajorOrMajorMinorVersion(majorMinorOnly), true, 'It can determine if the version is only major.minor');
});

Просмотреть файл

@ -8,26 +8,22 @@ import * as chaiAsPromised from 'chai-as-promised';
import * as path from 'path';
import { DotnetCoreAcquisitionWorker } from '../../Acquisition/DotnetCoreAcquisitionWorker';
import { IInstallScriptAcquisitionWorker } from '../../Acquisition/IInstallScriptAcquisitionWorker';
import { RuntimeInstallationDirectoryProvider } from '../../Acquisition/RuntimeInstallationDirectoryProvider';
import {
DotnetFallbackInstallScriptUsed,
DotnetInstallScriptAcquisitionError,
} from '../../EventStream/EventStreamEvents';
import {
ErrorAcquisitionInvoker,
MockEventStream,
MockExtensionContext,
MockInstallationValidator,
MockInstallScriptWorker,
MockTrackingWebRequestWorker,
MockVSCodeExtensionContext,
NoInstallAcquisitionInvoker,
} from '../mocks/MockObjects';
import {
Debugging
} from '../../Utils/Debugging';
import { MockWindowDisplayWorker } from '../mocks/MockWindowDisplayWorker';
import { getMockUtilityContext } from './TestUtility';
import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility';
const assert = chai.assert;
chai.use(chaiAsPromised);
@ -37,41 +33,26 @@ const maxTimeoutTime = 10000;
const staticWebsiteUrl = 'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/2.1/releases.json';
suite('WebRequestWorker Unit Tests', () => {
function getTestContext(): [MockEventStream, MockExtensionContext] {
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
return [eventStream, context];
}
test('Acquire Version Network Failure', async () => {
const [eventStream, context] = getTestContext();
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new ErrorAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutSeconds: 10,
installDirectoryProvider: new RuntimeInstallationDirectoryProvider(''),
isExtensionTelemetryInitiallyEnabled: true
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
return assert.isRejected(acquisitionWorker.acquireRuntime('1.0'), Error, '.NET Acquisition Failed');
const eventStream = new MockEventStream();
const acquisitionWorker = new DotnetCoreAcquisitionWorker(getMockAcquisitionContext(true, undefined, eventStream), getMockUtilityContext(), new MockVSCodeExtensionContext());
const invoker = new NoInstallAcquisitionInvoker(eventStream);
return assert.isRejected(acquisitionWorker.acquireRuntime('1.0', invoker), Error, '.NET Acquisition Failed');
});
test('Install Script Request Failure', async () => {
const [eventStream, context] = getTestContext();
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(context, eventStream, true);
const eventStream = new MockEventStream();
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(getMockAcquisitionContext(true), true);
await assert.isRejected(installScriptWorker.getDotnetInstallScriptPath(), Error, 'Failed to Acquire Dotnet Install Script');
assert.exists(eventStream.events.find(event => event instanceof DotnetInstallScriptAcquisitionError));
});
test('Install Script Request Failure With Fallback Install Script', async () => {
Debugging.log('Get Test Context.');
const [eventStream, context] = getTestContext();
const eventStream = new MockEventStream();
Debugging.log('Instantiate Install Script Worker.');
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(context, eventStream, true, true);
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(getMockAcquisitionContext(true, undefined, eventStream), true, true);
Debugging.log('Request the install script path.');
const scriptPath = await installScriptWorker.getDotnetInstallScriptPath();
@ -85,15 +66,14 @@ suite('WebRequestWorker Unit Tests', () => {
});
test('Install Script File Manipulation Failure', async () => {
const [eventStream, context] = getTestContext();
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(context, eventStream, true);
const eventStream = new MockEventStream();
const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(getMockAcquisitionContext(true, undefined, eventStream), true);
await assert.isRejected(installScriptWorker.getDotnetInstallScriptPath(), Error, 'Failed to Acquire Dotnet Install Script')
assert.exists(eventStream.events.find(event => event instanceof DotnetInstallScriptAcquisitionError));
});
test('Web Requests Cached on Repeated calls', async () => {
const [eventStream, context] = getTestContext();
const webWorker = new MockTrackingWebRequestWorker(context, eventStream, staticWebsiteUrl);
const webWorker = new MockTrackingWebRequestWorker(getMockAcquisitionContext(true), staticWebsiteUrl);
const uncachedResult = await webWorker.getCachedData();
// The data should now be cached.
@ -107,9 +87,8 @@ suite('WebRequestWorker Unit Tests', () => {
}).timeout(maxTimeoutTime);
test('Web Requests Cached Does Not Live Forever', async () => {
const [eventStream, context] = getTestContext();
const cacheTimeoutTime = 1;
const webWorker = new MockTrackingWebRequestWorker(context, eventStream, 'https://microsoft.com', true, maxTimeoutTime*5, cacheTimeoutTime);
const webWorker = new MockTrackingWebRequestWorker(getMockAcquisitionContext(true), 'https://microsoft.com', true, cacheTimeoutTime);
const uncachedResult = await webWorker.getCachedData();
await new Promise(resolve => setTimeout(resolve, cacheTimeoutTime));
const cachedResult = await webWorker.getCachedData();

Просмотреть файл

@ -9,39 +9,19 @@ import * as fs from 'fs';
import * as path from 'path';
import { MockCommandExecutor, MockEventStream, MockExtensionContext, MockFileUtilities, MockInstallationValidator, NoInstallAcquisitionInvoker } from '../mocks/MockObjects';
import { WinMacGlobalInstaller } from '../../Acquisition/WinMacGlobalInstaller';
import { RuntimeInstallationDirectoryProvider } from '../../Acquisition/RuntimeInstallationDirectoryProvider';
import { SdkInstallationDirectoryProvider } from '../../Acquisition/SdkInstallationDirectoryProvider';
import { IAcquisitionWorkerContext } from '../../Acquisition/IAcquisitionWorkerContext';
import { FileUtilities } from '../../Utils/FileUtilities';
import { MockWindowDisplayWorker } from '../mocks/MockWindowDisplayWorker';
import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility';
const assert = chai.assert;
const standardTimeoutTime = 100000;
suite('Windows & Mac Global Installer Tests', () =>
{
function mockContext(runtimeInstall: boolean): IAcquisitionWorkerContext {
const extensionContext = new MockExtensionContext();
const eventStream = new MockEventStream();
const workerContext : IAcquisitionWorkerContext = {
storagePath: '',
extensionState: extensionContext,
eventStream,
acquisitionInvoker: new NoInstallAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutSeconds: standardTimeoutTime,
installDirectoryProvider: runtimeInstall ? new RuntimeInstallationDirectoryProvider('') : new SdkInstallationDirectoryProvider(''),
isExtensionTelemetryInitiallyEnabled: true,
};
return workerContext;
}
const mockVersion = '7.0.306';
const mockUrl = 'https://download.visualstudio.microsoft.com/download/pr/4c0aaf08-3fa1-4fa0-8435-73b85eee4b32/e8264b3530b03b74b04ecfcf1666fe93/dotnet-sdk-7.0.306-win-x64.exe';
const mockHash = '';
const mockExecutor = new MockCommandExecutor(new MockEventStream(), getMockUtilityContext());
const mockExecutor = new MockCommandExecutor(getMockAcquisitionContext(false), getMockUtilityContext());
const mockFileUtils = new MockFileUtilities();
const installer : WinMacGlobalInstaller = new WinMacGlobalInstaller(mockContext(false), getMockUtilityContext(), mockVersion, mockUrl, mockHash, mockExecutor);
const installer : WinMacGlobalInstaller = new WinMacGlobalInstaller(getMockAcquisitionContext(false), getMockUtilityContext(), mockVersion, mockUrl, mockHash, mockExecutor);
installer.file = mockFileUtils;
test('It reads SDK registry entries correctly on windows', async () =>
@ -147,7 +127,7 @@ suite('Windows & Mac Global Installer Tests', () =>
assert.isTrue(fs.existsSync(mockExecutor.attemptedCommand.split(' ')[0]), 'It ran a command to an executable that exists');
if(new FileUtilities().isElevated())
{
assert.include(mockExecutor.attemptedCommand, ' /quiet /install /norestart', 'It ran under the hood if it had privelleges already');
assert.include(mockExecutor.attemptedCommand, ' /quiet /install /norestart', 'It ran under the hood if it had privileges already');
}
}
@ -169,12 +149,12 @@ suite('Windows & Mac Global Installer Tests', () =>
const installersDir = WinMacGlobalInstaller.getDownloadedInstallFilesFolder();
assert.equal(installerDownloadFolder, installersDir, 'The expected installer folder is used');
assert.isTrue(fs.existsSync(installerDownloadFolder), 'install folder is created when we dont clean it up');
assert.isTrue(fs.existsSync(installerDownloadFolder), 'install folder is created when we do not clean it up');
installer.cleanupInstallFiles = true;
await installer.installSDK();
// The installer files should be removed. Note this doesnt really check the default as we changed it manually
// The installer files should be removed. Note this doesn't really check the default as we changed it manually
if(new FileUtilities().isElevated())
{

Просмотреть файл

@ -1,11 +1,15 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"cSpell.words": [
"APPDATA",
"HKCU"
]
}

Просмотреть файл

@ -10,7 +10,7 @@ import * as path from 'path';
import * as vscode from 'vscode';
import {
AcquireErrorConfiguration,
AcquisitionInvoker,
AcquisitionInvoker as LocalAcquisitionInvoker,
callWithErrorHandling,
DotnetAcquisitionRequested,
DotnetAcquisitionStatusRequested,
@ -44,6 +44,7 @@ import {
import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId';
import { GlobalInstallerResolver } from 'vscode-dotnet-runtime-library/dist/Acquisition/GlobalInstallerResolver';
import { IAcquisitionWorkerContext } from 'vscode-dotnet-runtime-library/dist/Acquisition/IAcquisitionWorkerContext';
// tslint:disable no-var-requires
const packageJson = require('../package.json');
@ -94,7 +95,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
showLogCommand: `${commandPrefix}.${commandKeys.showAcquisitionLog}`,
packageJson,
} as IEventStreamContext;
const [eventStream, outputChannel, loggingObserver, eventStreamObservers] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext);
const [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext);
const extensionConfigWorker = new ExtensionConfigurationWorker(extensionConfiguration, undefined);
const issueContext = (errorConfiguration: ErrorConfiguration | undefined, commandName: string, version?: string) => {
@ -111,6 +112,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
} as IIssueContext;
};
const timeoutValue = extensionConfiguration.get<number>(configKeys.installTimeoutValue);
const resolvedTimeoutSeconds = timeoutValue === undefined ? defaultTimeoutValue : timeoutValue;
const proxyUrl = extensionConfiguration.get<string>(configKeys.proxyUrl);
@ -126,25 +128,27 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
}
}
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
const acquisitionContext : IAcquisitionWorkerContext = {
storagePath,
extensionState: context.globalState,
eventStream,
acquisitionInvoker: new AcquisitionInvoker(context.globalState, eventStream, resolvedTimeoutSeconds, utilContext),
installationValidator: new InstallationValidator(eventStream),
timeoutValue: resolvedTimeoutSeconds,
timeoutSeconds: resolvedTimeoutSeconds,
installDirectoryProvider: new SdkInstallationDirectoryProvider(storagePath),
acquisitionContext : null,
acquisitionContext : null, // todo: why is this null
isExtensionTelemetryInitiallyEnabled : isExtensionTelemetryEnabled,
}, utilContext, vsCodeExtensionContext);
};
const versionResolver = new VersionResolver(context.globalState, eventStream, resolvedTimeoutSeconds);
telemetryObserver?.setAcquisitionContext(acquisitionContext);
const acquisitionWorker = new DotnetCoreAcquisitionWorker(acquisitionContext, utilContext, vsCodeExtensionContext);
const versionResolver = new VersionResolver(acquisitionContext);
const getAvailableVersions = async (commandContext: IDotnetListVersionsContext | undefined, customWebWorker: WebRequestWorker | undefined) : Promise<IDotnetListVersionsResult | undefined> =>
{
const versionsResult = await callWithErrorHandling(async () => {
const customVersionResolver = new VersionResolver(context.globalState, eventStream, resolvedTimeoutSeconds, proxyUrl, customWebWorker);
const customVersionResolver = new VersionResolver(acquisitionContext);
return customVersionResolver.GetAvailableDotnetVersions(commandContext);
}, issueContext(commandContext?.errorConfiguration, 'listVersions'));
@ -177,10 +181,10 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
throw Error(`No version was defined to install.`);
}
const globalInstallerResolver = new GlobalInstallerResolver(context.globalState, eventStream, commandContext.version, resolvedTimeoutSeconds, proxyUrl);
const globalInstallerResolver = new GlobalInstallerResolver(acquisitionContext, commandContext.version);
const dotnetPath = await acquisitionWorker.acquireGlobalSDK(globalInstallerResolver);
new CommandExecutor(eventStream, utilContext).setPathEnvVar(dotnetPath.dotnetPath, troubleshootingUrl, displayWorker, vsCodeExtensionContext, true);
new CommandExecutor(acquisitionContext, utilContext).setPathEnvVar(dotnetPath.dotnetPath, troubleshootingUrl, displayWorker, vsCodeExtensionContext, true);
Debugging.log(`Returning path: ${dotnetPath}.`, eventStream);
return dotnetPath;
}
@ -189,13 +193,14 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
Debugging.log(`Acquisition Request was remarked as local.`, eventStream);
const resolvedVersion = await versionResolver.getFullSDKVersion(commandContext.version);
const dotnetPath = await acquisitionWorker.acquireSDK(resolvedVersion);
const acquisitionInvoker = new LocalAcquisitionInvoker(acquisitionContext, utilContext);
const dotnetPath = await acquisitionWorker.acquireSDK(resolvedVersion, acquisitionInvoker);
const pathEnvVar = path.dirname(dotnetPath.dotnetPath);
new CommandExecutor(eventStream, utilContext).setPathEnvVar(pathEnvVar, troubleshootingUrl, displayWorker, vsCodeExtensionContext, false);
new CommandExecutor(acquisitionContext, utilContext).setPathEnvVar(pathEnvVar, troubleshootingUrl, displayWorker, vsCodeExtensionContext, false);
return dotnetPath;
}
}, issueContext(commandContext.errorConfiguration, 'acquireSDK'));
}, issueContext(commandContext.errorConfiguration, 'acquireSDK'), commandContext.requestingExtensionId, acquisitionContext);
Debugging.log(`Returning Path Result ${pathResult}.`, eventStream);
@ -212,7 +217,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
return pathResult;
});
const dotnetlistVersionsRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.listVersions}`,
const dotnetListVersionsRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.listVersions}`,
async (commandContext: IDotnetListVersionsContext | undefined, customWebWorker: WebRequestWorker | undefined) =>
{
return getAvailableVersions(commandContext, customWebWorker);
@ -253,7 +258,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE
context.subscriptions.push(
dotnetAcquireRegistration,
dotnetAcquireStatusRegistration,
dotnetlistVersionsRegistration,
dotnetListVersionsRegistration,
dotnetRecommendedVersionRegistration,
dotnetUninstallAllRegistration,
showOutputChannelRegistration,

Просмотреть файл

@ -20,22 +20,19 @@ import {
IDotnetAcquireResult,
IDotnetListVersionsContext,
IDotnetListVersionsResult,
FileUtilities,
GlobalInstallerResolver,
MockEnvironmentVariableCollection,
MockEventStream,
MockExtensionConfiguration,
MockExtensionContext,
MockInstallationValidator,
MockTelemetryReporter,
MockWebRequestWorker,
MockWindowDisplayWorker,
NoInstallAcquisitionInvoker,
SdkInstallationDirectoryProvider,
WinMacGlobalInstaller,
MockIndexWebRequestWorker,
MockVSCodeExtensionContext,
getMockUtilityContext
getMockAcquisitionContext,
getMockAcquisitionWorker
} from 'vscode-dotnet-runtime-library';
import * as extension from '../../extension';
import { uninstallSDKExtension } from '../../ExtensionUninstall';
@ -49,6 +46,7 @@ chai.use(chaiAsPromised);
/* tslint:disable:no-unsafe-finally */
const currentSDKVersion = '6.0';
const mockAcquisitionContext = getMockAcquisitionContext(false);
suite('DotnetCoreAcquisitionExtension End to End', function ()
{
@ -111,7 +109,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
test('List Sdks & Runtimes', async () => {
const mockWebContext = new MockExtensionContext();
const eventStream = new MockEventStream();
const webWorker = new MockWebRequestWorker(mockWebContext, eventStream, '');
const webWorker = new MockWebRequestWorker(mockAcquisitionContext, '');
webWorker.response = JSON.parse(mockReleasesData);
// The API can find the available SDKs and list their versions.
@ -134,7 +132,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
test('Get Recommended SDK Version', async () => {
const mockWebContext = new MockExtensionContext();
const eventStream = new MockEventStream();
const webWorker = new MockWebRequestWorker(mockWebContext, eventStream, '');
const webWorker = new MockWebRequestWorker(mockAcquisitionContext, '');
webWorker.response = JSON.parse(mockReleasesData);
const result = await vscode.commands.executeCommand<IDotnetVersion>('dotnet-sdk.recommendedVersion', null, webWorker);
@ -147,16 +145,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
const installDirectoryProvider = new SdkInstallationDirectoryProvider(storagePath);
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new NoInstallAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutValue: 10,
installDirectoryProvider,
isExtensionTelemetryInitiallyEnabled: true,
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
const acquisitionWorker = getMockAcquisitionWorker(false, undefined, eventStream);
const version = currentSDKVersion;
const earlierVersion = '3.1';
@ -175,7 +164,8 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
fs.writeFileSync(dotnetExePath, '');
// Assert preinstalled SDKs are detected
const result = await acquisitionWorker.acquireSDK(version);
const acquisitionInvoker = new NoInstallAcquisitionInvoker(eventStream);
const result = await acquisitionWorker.acquireSDK(version, acquisitionInvoker);
assert.equal(path.dirname(result.dotnetPath), dotnetDir);
const preinstallEvents = eventStream.events
.filter(event => event instanceof DotnetPreinstallDetected)
@ -194,19 +184,8 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
test('Install Status Command with Preinstalled SDK', async () => {
// Set up acquisition worker
const context = new MockExtensionContext();
const eventStream = new MockEventStream();
const installDirectoryProvider = new SdkInstallationDirectoryProvider(storagePath);
const acquisitionWorker = new DotnetCoreAcquisitionWorker({
storagePath: '',
extensionState: context,
eventStream,
acquisitionInvoker: new NoInstallAcquisitionInvoker(eventStream),
installationValidator: new MockInstallationValidator(eventStream),
timeoutValue: 10,
installDirectoryProvider,
isExtensionTelemetryInitiallyEnabled: true,
}, getMockUtilityContext(), new MockVSCodeExtensionContext());
const acquisitionWorker = getMockAcquisitionWorker(false);
const version = currentSDKVersion;
const currentVersionInstallKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, os.arch());
@ -252,9 +231,6 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
}).timeout(standardTimeoutTime);
test('Global Install Version Parsing Handles Different Version Formats Correctly and Gives Expected Installer URL', async () => {
const mockExtensionContext = new MockExtensionContext();
const eventStream = new MockEventStream();
const majorOnlyVersion = '6';
const majorMinorVersion = '6.0';
const featureBandOnlyVersion = '6.0.3xx'; // this should be a full version thats lower than the newest version available.
@ -264,21 +240,21 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
const newestVersion = '6.0.408';
const url = 'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/6.0/releases.json'
const webWorker = new MockIndexWebRequestWorker(mockExtensionContext, eventStream, url);
const webWorker = new MockIndexWebRequestWorker(mockAcquisitionContext, url);
webWorker.knownUrls.push(url);
// Note that ZIPS in the data below come before EXEs to make sure the file extension check works.
const mockJsonFile = path.join(__dirname, '../../..', 'src', 'test', 'mocks', 'mock-releases.json');
webWorker.matchingUrlResponses.push(fs.readFileSync(mockJsonFile, 'utf8'));
let resolver : GlobalInstallerResolver = new GlobalInstallerResolver(mockExtensionContext, eventStream, majorOnlyVersion, standardTimeoutTime, undefined);
let resolver : GlobalInstallerResolver = new GlobalInstallerResolver(mockAcquisitionContext, majorOnlyVersion);
resolver.customWebRequestWorker = webWorker;
assert.strictEqual(await resolver.getFullySpecifiedVersion(), newestVersion);
resolver = new GlobalInstallerResolver(mockExtensionContext, eventStream, majorMinorVersion, standardTimeoutTime, undefined);
resolver = new GlobalInstallerResolver(mockAcquisitionContext, majorMinorVersion);
resolver.customWebRequestWorker = webWorker;
assert.strictEqual(await resolver.getFullySpecifiedVersion(), newestVersion);
resolver = new GlobalInstallerResolver(mockExtensionContext, eventStream, featureBandOnlyVersion, standardTimeoutTime, undefined);
resolver = new GlobalInstallerResolver(mockAcquisitionContext, featureBandOnlyVersion);
resolver.customWebRequestWorker = webWorker;
assert.strictEqual(await resolver.getFullySpecifiedVersion(), newestBandedVersion);
@ -297,7 +273,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function()
}
}
resolver = new GlobalInstallerResolver(mockExtensionContext, eventStream, fullVersion, standardTimeoutTime, undefined);
resolver = new GlobalInstallerResolver(mockAcquisitionContext, fullVersion);
resolver.customWebRequestWorker = webWorker;
assert.strictEqual(await resolver.getFullySpecifiedVersion(), fullVersion);
}).timeout(standardTimeoutTime);