Skip to content

Commit e9eeb80

Browse files
authored
✅ tests to ensure resource is passed into module installer (#855)
Fixes #690
1 parent 59f428c commit e9eeb80

File tree

5 files changed

+111
-9
lines changed

5 files changed

+111
-9
lines changed

src/client/common/installer/installer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { inject, injectable, named } from 'inversify';
22
import * as os from 'os';
33
import * as path from 'path';
4-
import { ConfigurationTarget, QuickPickItem, Uri, window, workspace } from 'vscode';
4+
import { ConfigurationTarget, Uri, window, workspace } from 'vscode';
55
import * as vscode from 'vscode';
66
import { IFormatterHelper } from '../../formatters/types';
77
import { IServiceContainer } from '../../ioc/types';
@@ -13,7 +13,7 @@ import { IPlatformService } from '../platform/types';
1313
import { IProcessService, IPythonExecutionFactory } from '../process/types';
1414
import { ITerminalServiceFactory } from '../terminal/types';
1515
import { IInstaller, ILogger, InstallerResponse, IOutputChannel, ModuleNamePurpose, Product } from '../types';
16-
import { IInstallationChannelManager, IModuleInstaller } from './types';
16+
import { IInstallationChannelManager } from './types';
1717

1818
export { Product } from '../types';
1919

@@ -144,7 +144,7 @@ export class Installer implements IInstaller {
144144

145145
const moduleName = this.translateProductToModuleName(product, ModuleNamePurpose.install);
146146
const logger = this.serviceContainer.get<ILogger>(ILogger);
147-
await installer.installModule(moduleName)
147+
await installer.installModule(moduleName, resource)
148148
.catch(logger.logError.bind(logger, `Error in installing the module '${moduleName}'`));
149149

150150
return this.isInstalled(product)

src/client/common/installer/productInstaller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { inject, injectable, named } from 'inversify';
22
import * as os from 'os';
33
import * as path from 'path';
4-
import { OutputChannel, QuickPickItem, Uri, window } from 'vscode';
4+
import { OutputChannel, Uri } from 'vscode';
55
import * as vscode from 'vscode';
66
import { IFormatterHelper } from '../../formatters/types';
77
import { IServiceContainer } from '../../ioc/types';
@@ -14,7 +14,7 @@ import { IProcessService, IPythonExecutionFactory } from '../process/types';
1414
import { ITerminalServiceFactory } from '../terminal/types';
1515
import { IConfigurationService, IInstaller, ILogger, InstallerResponse, IOutputChannel, ModuleNamePurpose, Product } from '../types';
1616
import { ProductNames } from './productNames';
17-
import { IInstallationChannelManager, IModuleInstaller } from './types';
17+
import { IInstallationChannelManager } from './types';
1818

1919
export { Product } from '../types';
2020

@@ -53,7 +53,7 @@ abstract class BaseInstaller {
5353

5454
const moduleName = translateProductToModule(product, ModuleNamePurpose.install);
5555
const logger = this.serviceContainer.get<ILogger>(ILogger);
56-
await installer.installModule(moduleName)
56+
await installer.installModule(moduleName, resource)
5757
.catch(logger.logError.bind(logger, `Error in installing the module '${moduleName}'`));
5858

5959
return this.isInstalled(product)

src/client/common/installer/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Product } from '../types';
77
export const IModuleInstaller = Symbol('IModuleInstaller');
88
export interface IModuleInstaller {
99
readonly displayName: string;
10-
installModule(name: string): Promise<void>;
10+
installModule(name: string, resource?: Uri): Promise<void>;
1111
isSupported(resource?: Uri): Promise<boolean>;
1212
}
1313

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { expect, use } from 'chai';
5+
import * as chaiAsPromised from 'chai-as-promised';
6+
import * as TypeMoq from 'typemoq';
7+
import { Disposable, OutputChannel, Uri } from 'vscode';
8+
import { EnumEx } from '../../../client/common/enumUtils';
9+
import { Installer } from '../../../client/common/installer/installer';
10+
import { ProductInstaller } from '../../../client/common/installer/productInstaller';
11+
import { IInstallationChannelManager, IModuleInstaller } from '../../../client/common/installer/types';
12+
import { IDisposableRegistry, ILogger, InstallerResponse, ModuleNamePurpose, Product } from '../../../client/common/types';
13+
import { IServiceContainer } from '../../../client/ioc/types';
14+
15+
use(chaiAsPromised);
16+
17+
// tslint:disable-next-line:max-func-body-length
18+
suite('Module Installerx', () => {
19+
[undefined, Uri.file('resource')].forEach(resource => {
20+
EnumEx.getNamesAndValues<Product>(Product).forEach(product => {
21+
let disposables: Disposable[] = [];
22+
let installer: Installer;
23+
let installationChannel: TypeMoq.IMock<IInstallationChannelManager>;
24+
let moduleInstaller: TypeMoq.IMock<IModuleInstaller>;
25+
let serviceContainer: TypeMoq.IMock<IServiceContainer>;
26+
let productInstallerFactory: ProductInstaller;
27+
setup(() => {
28+
serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
29+
const outputChannel = TypeMoq.Mock.ofType<OutputChannel>();
30+
31+
installer = new Installer(serviceContainer.object, outputChannel.object);
32+
33+
disposables = [];
34+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IDisposableRegistry), TypeMoq.It.isAny())).returns(() => disposables);
35+
36+
installationChannel = TypeMoq.Mock.ofType<IInstallationChannelManager>();
37+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(IInstallationChannelManager), TypeMoq.It.isAny())).returns(() => installationChannel.object);
38+
39+
moduleInstaller = TypeMoq.Mock.ofType<IModuleInstaller>();
40+
// tslint:disable-next-line:no-any
41+
moduleInstaller.setup((x: any) => x.then).returns(() => undefined);
42+
installationChannel.setup(i => i.getInstallationChannel(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(moduleInstaller.object));
43+
installationChannel.setup(i => i.getInstallationChannel(TypeMoq.It.isAny())).returns(() => Promise.resolve(moduleInstaller.object));
44+
45+
productInstallerFactory = new ProductInstaller(serviceContainer.object, outputChannel.object);
46+
});
47+
teardown(() => {
48+
disposables.forEach(disposable => {
49+
if (disposable) {
50+
disposable.dispose();
51+
}
52+
});
53+
});
54+
55+
switch (product.value) {
56+
case Product.isort:
57+
case Product.ctags: {
58+
return;
59+
}
60+
case Product.unittest: {
61+
test(`Ensure resource info is passed into the module installer ${product.name} (${resource ? 'With a resource' : 'without a resource'})`, async () => {
62+
const response = await installer.install(product.value, resource);
63+
expect(response).to.be.equal(InstallerResponse.Installed);
64+
});
65+
test(`Ensure resource info is passed into the module installer (created using ProductInstaller) ${product.name} (${resource ? 'With a resource' : 'without a resource'})`, async () => {
66+
const response = await installer.install(product.value, resource);
67+
expect(response).to.be.equal(InstallerResponse.Installed);
68+
});
69+
}
70+
default: {
71+
test(`Ensure resource info is passed into the module installer ${product.name} (${resource ? 'With a resource' : 'without a resource'})`, async () => {
72+
const moduleName = installer.translateProductToModuleName(product.value, ModuleNamePurpose.install);
73+
const logger = TypeMoq.Mock.ofType<ILogger>();
74+
logger.setup(l => l.logError(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => new Error('UnitTesting'));
75+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ILogger), TypeMoq.It.isAny())).returns(() => logger.object);
76+
77+
moduleInstaller.setup(m => m.installModule(TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource))).returns(() => Promise.reject(new Error('UnitTesting')));
78+
79+
try {
80+
await installer.install(product.value, resource);
81+
} catch (ex) {
82+
moduleInstaller.verify(m => m.installModule(TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource)), TypeMoq.Times.once());
83+
}
84+
});
85+
test(`Ensure resource info is passed into the module installer (created using ProductInstaller) ${product.name} (${resource ? 'With a resource' : 'without a resource'})`, async () => {
86+
const moduleName = installer.translateProductToModuleName(product.value, ModuleNamePurpose.install);
87+
const logger = TypeMoq.Mock.ofType<ILogger>();
88+
logger.setup(l => l.logError(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => new Error('UnitTesting'));
89+
serviceContainer.setup(c => c.get(TypeMoq.It.isValue(ILogger), TypeMoq.It.isAny())).returns(() => logger.object);
90+
91+
moduleInstaller.setup(m => m.installModule(TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource))).returns(() => Promise.reject(new Error('UnitTesting')));
92+
93+
try {
94+
await productInstallerFactory.install(product.value, resource);
95+
} catch (ex) {
96+
moduleInstaller.verify(m => m.installModule(TypeMoq.It.isValue(moduleName), TypeMoq.It.isValue(resource)), TypeMoq.Times.once());
97+
}
98+
});
99+
}
100+
}
101+
});
102+
});
103+
});

src/test/mocks/moduleInstaller.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { EventEmitter } from 'events';
22
import { Uri } from 'vscode';
3-
import { createDeferred, Deferred } from '../../client/common/helpers';
43
import { IModuleInstaller } from '../../client/common/installer/types';
54

65
export class MockModuleInstaller extends EventEmitter implements IModuleInstaller {
76
constructor(public readonly displayName: string, private supported: boolean) {
87
super();
98
}
10-
public async installModule(name: string): Promise<void> {
9+
public async installModule(name: string, resource?: Uri): Promise<void> {
1110
this.emit('installModule', name);
1211
}
1312
public async isSupported(resource?: Uri): Promise<boolean> {

0 commit comments

Comments
 (0)