Path: blob/main/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as sinon from 'sinon';6import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';7import { SyncDescriptor } from '../../common/descriptors.js';8import { ServiceIdentifier, ServicesAccessor } from '../../common/instantiation.js';9import { InstantiationService, Trace } from '../../common/instantiationService.js';10import { ServiceCollection } from '../../common/serviceCollection.js';1112interface IServiceMock<T> {13id: ServiceIdentifier<T>;14service: any;15}1617const isSinonSpyLike = (fn: Function): fn is sinon.SinonSpy => fn && 'callCount' in fn;1819export class TestInstantiationService extends InstantiationService implements IDisposable, ServicesAccessor {2021private _servciesMap: Map<ServiceIdentifier<any>, any>;2223constructor(private _serviceCollection: ServiceCollection = new ServiceCollection(), strict: boolean = false, parent?: TestInstantiationService, private _properDispose?: boolean) {24super(_serviceCollection, strict, parent);2526this._servciesMap = new Map<ServiceIdentifier<any>, any>();27}2829public get<T>(service: ServiceIdentifier<T>): T {30return super._getOrCreateServiceInstance(service, Trace.traceCreation(false, TestInstantiationService));31}3233public getIfExists<T>(service: ServiceIdentifier<T>): T | undefined {34try {35return super._getOrCreateServiceInstance(service, Trace.traceCreation(false, TestInstantiationService));36} catch (e) {37return undefined;38}39}4041public set<T>(service: ServiceIdentifier<T>, instance: T): T {42return <T>this._serviceCollection.set(service, instance);43}4445public mock<T>(service: ServiceIdentifier<T>): T | sinon.SinonMock {46return <T>this._create(service, { mock: true });47}4849public stub<T>(service: ServiceIdentifier<T>, ctor: Function): T;50public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>): T;51public stub<T, V>(service: ServiceIdentifier<T>, ctor: Function, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;52public stub<T, V>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;53public stub<T, V>(service: ServiceIdentifier<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;54public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: any): sinon.SinonStub | sinon.SinonSpy {55const service = typeof arg2 !== 'string' ? arg2 : undefined;56const serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service };57const property = typeof arg2 === 'string' ? arg2 : arg3;58const value = typeof arg2 === 'string' ? arg3 : arg4;5960const stubObject = <any>this._create(serviceMock, { stub: true }, service && !property);61if (property) {62if (stubObject[property]) {63if (stubObject[property].hasOwnProperty('restore')) {64stubObject[property].restore();65}66if (typeof value === 'function') {67const spy = isSinonSpyLike(value) ? value : sinon.spy(value);68stubObject[property] = spy;69return spy;70} else {71const stub = value ? sinon.stub().returns(value) : sinon.stub();72stubObject[property] = stub;73return stub;74}75} else {76stubObject[property] = value;77}78}79return stubObject;80}8182public stubPromise<T>(service?: ServiceIdentifier<T>, fnProperty?: string, value?: any): T | sinon.SinonStub;83public stubPromise<T, V>(service?: ServiceIdentifier<T>, ctor?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;84public stubPromise<T, V>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;85public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: any): sinon.SinonStub | sinon.SinonSpy {86arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3;87arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4;88return this.stub(arg1, arg2, arg3, arg4);89}9091public spy<T>(service: ServiceIdentifier<T>, fnProperty: string): sinon.SinonSpy {92const spy = sinon.spy();93this.stub(service, fnProperty, spy);94return spy;95}9697private _create<T>(serviceMock: IServiceMock<T>, options: SinonOptions, reset?: boolean): any;98private _create<T>(ctor: any, options: SinonOptions): any;99private _create(arg1: any, options: SinonOptions, reset: boolean = false): any {100if (this.isServiceMock(arg1)) {101const service = this._getOrCreateService(arg1, options, reset);102this._serviceCollection.set(arg1.id, service);103return service;104}105return options.mock ? sinon.mock(arg1) : this._createStub(arg1);106}107108private _getOrCreateService<T>(serviceMock: IServiceMock<T>, opts: SinonOptions, reset?: boolean): any {109const service: any = this._serviceCollection.get(serviceMock.id);110if (!reset && service) {111if (opts.mock && service['sinonOptions'] && !!service['sinonOptions'].mock) {112return service;113}114if (opts.stub && service['sinonOptions'] && !!service['sinonOptions'].stub) {115return service;116}117}118return this._createService(serviceMock, opts);119}120121private _createService(serviceMock: IServiceMock<any>, opts: SinonOptions): any {122serviceMock.service = serviceMock.service ? serviceMock.service : this._servciesMap.get(serviceMock.id);123const service = opts.mock ? sinon.mock(serviceMock.service) : this._createStub(serviceMock.service);124service['sinonOptions'] = opts;125return service;126}127128private _createStub(arg: any): any {129return typeof arg === 'object' ? arg : sinon.createStubInstance(arg);130}131132private isServiceMock(arg1: any): boolean {133return typeof arg1 === 'object' && arg1.hasOwnProperty('id');134}135136override createChild(services: ServiceCollection): TestInstantiationService {137return new TestInstantiationService(services, false, this);138}139140override dispose() {141sinon.restore();142if (this._properDispose) {143super.dispose();144}145}146}147148interface SinonOptions {149mock?: boolean;150stub?: boolean;151}152153export type ServiceIdCtorPair<T> = [id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)];154155export function createServices(disposables: DisposableStore, services: ServiceIdCtorPair<any>[]): TestInstantiationService {156const serviceIdentifiers: ServiceIdentifier<any>[] = [];157const serviceCollection = new ServiceCollection();158159const define = <T>(id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)) => {160if (!serviceCollection.has(id)) {161if (typeof ctorOrInstance === 'function') {162serviceCollection.set(id, new SyncDescriptor(ctorOrInstance as any));163} else {164serviceCollection.set(id, ctorOrInstance);165}166}167serviceIdentifiers.push(id);168};169170for (const [id, ctor] of services) {171define(id, ctor);172}173174const instantiationService = disposables.add(new TestInstantiationService(serviceCollection, true));175disposables.add(toDisposable(() => {176for (const id of serviceIdentifiers) {177const instanceOrDescriptor = serviceCollection.get(id);178if (typeof instanceOrDescriptor.dispose === 'function') {179instanceOrDescriptor.dispose();180}181}182}));183return instantiationService;184}185186187