Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as sinon from 'sinon';
7
import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
8
import { SyncDescriptor } from '../../common/descriptors.js';
9
import { ServiceIdentifier, ServicesAccessor } from '../../common/instantiation.js';
10
import { InstantiationService, Trace } from '../../common/instantiationService.js';
11
import { ServiceCollection } from '../../common/serviceCollection.js';
12
13
interface IServiceMock<T> {
14
id: ServiceIdentifier<T>;
15
service: any;
16
}
17
18
const isSinonSpyLike = (fn: Function): fn is sinon.SinonSpy => fn && 'callCount' in fn;
19
20
export class TestInstantiationService extends InstantiationService implements IDisposable, ServicesAccessor {
21
22
private _servciesMap: Map<ServiceIdentifier<any>, any>;
23
24
constructor(private _serviceCollection: ServiceCollection = new ServiceCollection(), strict: boolean = false, parent?: TestInstantiationService, private _properDispose?: boolean) {
25
super(_serviceCollection, strict, parent);
26
27
this._servciesMap = new Map<ServiceIdentifier<any>, any>();
28
}
29
30
public get<T>(service: ServiceIdentifier<T>): T {
31
return super._getOrCreateServiceInstance(service, Trace.traceCreation(false, TestInstantiationService));
32
}
33
34
public getIfExists<T>(service: ServiceIdentifier<T>): T | undefined {
35
try {
36
return super._getOrCreateServiceInstance(service, Trace.traceCreation(false, TestInstantiationService));
37
} catch (e) {
38
return undefined;
39
}
40
}
41
42
public set<T>(service: ServiceIdentifier<T>, instance: T): T {
43
return <T>this._serviceCollection.set(service, instance);
44
}
45
46
public mock<T>(service: ServiceIdentifier<T>): T | sinon.SinonMock {
47
return <T>this._create(service, { mock: true });
48
}
49
50
public stub<T>(service: ServiceIdentifier<T>, ctor: Function): T;
51
public stub<T>(service: ServiceIdentifier<T>, obj: Partial<T>): T;
52
public stub<T, V>(service: ServiceIdentifier<T>, ctor: Function, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
53
public stub<T, V>(service: ServiceIdentifier<T>, obj: Partial<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
54
public stub<T, V>(service: ServiceIdentifier<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
55
public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
56
const service = typeof arg2 !== 'string' ? arg2 : undefined;
57
const serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service };
58
const property = typeof arg2 === 'string' ? arg2 : arg3;
59
const value = typeof arg2 === 'string' ? arg3 : arg4;
60
61
const stubObject = <any>this._create(serviceMock, { stub: true }, service && !property);
62
if (property) {
63
if (stubObject[property]) {
64
if (stubObject[property].hasOwnProperty('restore')) {
65
stubObject[property].restore();
66
}
67
if (typeof value === 'function') {
68
const spy = isSinonSpyLike(value) ? value : sinon.spy(value);
69
stubObject[property] = spy;
70
return spy;
71
} else {
72
const stub = value ? sinon.stub().returns(value) : sinon.stub();
73
stubObject[property] = stub;
74
return stub;
75
}
76
} else {
77
stubObject[property] = value;
78
}
79
}
80
return stubObject;
81
}
82
83
public stubPromise<T>(service?: ServiceIdentifier<T>, fnProperty?: string, value?: any): T | sinon.SinonStub;
84
public stubPromise<T, V>(service?: ServiceIdentifier<T>, ctor?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
85
public stubPromise<T, V>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
86
public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
87
arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3;
88
arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4;
89
return this.stub(arg1, arg2, arg3, arg4);
90
}
91
92
public spy<T>(service: ServiceIdentifier<T>, fnProperty: string): sinon.SinonSpy {
93
const spy = sinon.spy();
94
this.stub(service, fnProperty, spy);
95
return spy;
96
}
97
98
private _create<T>(serviceMock: IServiceMock<T>, options: SinonOptions, reset?: boolean): any;
99
private _create<T>(ctor: any, options: SinonOptions): any;
100
private _create(arg1: any, options: SinonOptions, reset: boolean = false): any {
101
if (this.isServiceMock(arg1)) {
102
const service = this._getOrCreateService(arg1, options, reset);
103
this._serviceCollection.set(arg1.id, service);
104
return service;
105
}
106
return options.mock ? sinon.mock(arg1) : this._createStub(arg1);
107
}
108
109
private _getOrCreateService<T>(serviceMock: IServiceMock<T>, opts: SinonOptions, reset?: boolean): any {
110
const service: any = this._serviceCollection.get(serviceMock.id);
111
if (!reset && service) {
112
if (opts.mock && service['sinonOptions'] && !!service['sinonOptions'].mock) {
113
return service;
114
}
115
if (opts.stub && service['sinonOptions'] && !!service['sinonOptions'].stub) {
116
return service;
117
}
118
}
119
return this._createService(serviceMock, opts);
120
}
121
122
private _createService(serviceMock: IServiceMock<any>, opts: SinonOptions): any {
123
serviceMock.service = serviceMock.service ? serviceMock.service : this._servciesMap.get(serviceMock.id);
124
const service = opts.mock ? sinon.mock(serviceMock.service) : this._createStub(serviceMock.service);
125
service['sinonOptions'] = opts;
126
return service;
127
}
128
129
private _createStub(arg: any): any {
130
return typeof arg === 'object' ? arg : sinon.createStubInstance(arg);
131
}
132
133
private isServiceMock(arg1: any): boolean {
134
return typeof arg1 === 'object' && arg1.hasOwnProperty('id');
135
}
136
137
override createChild(services: ServiceCollection): TestInstantiationService {
138
return new TestInstantiationService(services, false, this);
139
}
140
141
override dispose() {
142
sinon.restore();
143
if (this._properDispose) {
144
super.dispose();
145
}
146
}
147
}
148
149
interface SinonOptions {
150
mock?: boolean;
151
stub?: boolean;
152
}
153
154
export type ServiceIdCtorPair<T> = [id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)];
155
156
export function createServices(disposables: DisposableStore, services: ServiceIdCtorPair<any>[]): TestInstantiationService {
157
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
158
const serviceCollection = new ServiceCollection();
159
160
const define = <T>(id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)) => {
161
if (!serviceCollection.has(id)) {
162
if (typeof ctorOrInstance === 'function') {
163
serviceCollection.set(id, new SyncDescriptor(ctorOrInstance as any));
164
} else {
165
serviceCollection.set(id, ctorOrInstance);
166
}
167
}
168
serviceIdentifiers.push(id);
169
};
170
171
for (const [id, ctor] of services) {
172
define(id, ctor);
173
}
174
175
const instantiationService = disposables.add(new TestInstantiationService(serviceCollection, true));
176
disposables.add(toDisposable(() => {
177
for (const id of serviceIdentifiers) {
178
const instanceOrDescriptor = serviceCollection.get(id);
179
if (typeof instanceOrDescriptor.dispose === 'function') {
180
instanceOrDescriptor.dispose();
181
}
182
}
183
}));
184
return instantiationService;
185
}
186
187