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
5281 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, SyncDescriptor0 } from '../../common/descriptors.js';
9
import { GetLeadingNonServiceArgs, 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
private readonly _classStubs: Map<Function, any> = new Map();
24
private readonly _parentTestService: TestInstantiationService | undefined;
25
26
constructor(private _serviceCollection: ServiceCollection = new ServiceCollection(), strict: boolean = false, parent?: InstantiationService, private _properDispose?: boolean) {
27
super(_serviceCollection, strict, parent);
28
29
this._servciesMap = new Map<ServiceIdentifier<any>, any>();
30
if (parent instanceof TestInstantiationService) {
31
this._parentTestService = parent;
32
}
33
}
34
35
public get<T>(service: ServiceIdentifier<T>): T {
36
return super._getOrCreateServiceInstance(service, Trace.traceCreation(false, TestInstantiationService));
37
}
38
39
public set<T>(service: ServiceIdentifier<T>, instance: T): T {
40
return <T>this._serviceCollection.set(service, instance);
41
}
42
43
public mock<T>(service: ServiceIdentifier<T>): T | sinon.SinonMock {
44
return <T>this._create(service, { mock: true });
45
}
46
47
public stubInstance<T>(ctor: new (...args: any[]) => T, instance: Partial<T>): void {
48
this._classStubs.set(ctor, instance);
49
}
50
51
protected _getClassStub(ctor: Function): unknown {
52
return this._classStubs.get(ctor) ?? this._parentTestService?._getClassStub(ctor);
53
}
54
55
public override createInstance<T>(descriptor: SyncDescriptor0<T>): T;
56
public override createInstance<Ctor extends new (...args: any[]) => unknown, R extends InstanceType<Ctor>>(ctor: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
57
public override createInstance(ctorOrDescriptor: any | SyncDescriptor<any>, ...rest: unknown[]): unknown {
58
const stub = this._getClassStub(ctorOrDescriptor as Function);
59
if (stub) {
60
return stub;
61
}
62
return super.createInstance(ctorOrDescriptor, ...rest);
63
}
64
65
public stub<T>(service: ServiceIdentifier<T>, obj: Partial<NoInfer<T>> | Function): T;
66
public stub<T, V>(service: ServiceIdentifier<T>, obj: Partial<NoInfer<T>> | Function, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
67
public stub<T, V>(service: ServiceIdentifier<T>, property: string, value: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
68
public stub<T>(serviceIdentifier: ServiceIdentifier<T>, arg2: any, arg3?: string, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
69
const service = typeof arg2 !== 'string' ? arg2 : undefined;
70
const serviceMock: IServiceMock<any> = { id: serviceIdentifier, service: service };
71
const property = typeof arg2 === 'string' ? arg2 : arg3;
72
const value = typeof arg2 === 'string' ? arg3 : arg4;
73
74
const stubObject = this._create(serviceMock, { stub: true }, service && !property);
75
if (property) {
76
if (stubObject[property]) {
77
if (stubObject[property].hasOwnProperty('restore')) {
78
stubObject[property].restore();
79
}
80
if (typeof value === 'function') {
81
const spy = isSinonSpyLike(value) ? value : sinon.spy(value);
82
stubObject[property] = spy;
83
return spy;
84
} else {
85
const stub = value ? sinon.stub().returns(value) : sinon.stub();
86
stubObject[property] = stub;
87
return stub;
88
}
89
} else {
90
stubObject[property] = value;
91
}
92
}
93
return stubObject;
94
}
95
96
public stubPromise<T>(service?: ServiceIdentifier<T>, fnProperty?: string, value?: any): T | sinon.SinonStub;
97
public stubPromise<T, V>(service?: ServiceIdentifier<T>, ctor?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
98
public stubPromise<T, V>(service?: ServiceIdentifier<T>, obj?: any, fnProperty?: string, value?: V): V extends Function ? sinon.SinonSpy : sinon.SinonStub;
99
public stubPromise(arg1?: any, arg2?: any, arg3?: any, arg4?: any): sinon.SinonStub | sinon.SinonSpy {
100
arg3 = typeof arg2 === 'string' ? Promise.resolve(arg3) : arg3;
101
arg4 = typeof arg2 !== 'string' && typeof arg3 === 'string' ? Promise.resolve(arg4) : arg4;
102
return this.stub(arg1, arg2, arg3, arg4);
103
}
104
105
public spy<T>(service: ServiceIdentifier<T>, fnProperty: string): sinon.SinonSpy {
106
const spy = sinon.spy();
107
this.stub(service, fnProperty, spy);
108
return spy;
109
}
110
111
private _create<T>(serviceMock: IServiceMock<T>, options: SinonOptions, reset?: boolean): any;
112
private _create<T>(ctor: any, options: SinonOptions): any;
113
private _create(arg1: any, options: SinonOptions, reset: boolean = false): any {
114
if (this.isServiceMock(arg1)) {
115
const service = this._getOrCreateService(arg1, options, reset);
116
this._serviceCollection.set(arg1.id, service);
117
return service;
118
}
119
return options.mock ? sinon.mock(arg1) : this._createStub(arg1);
120
}
121
122
private _getOrCreateService<T>(serviceMock: IServiceMock<T>, opts: SinonOptions, reset?: boolean): any {
123
const service: any = this._serviceCollection.get(serviceMock.id);
124
if (!reset && service) {
125
if (opts.mock && service['sinonOptions'] && !!service['sinonOptions'].mock) {
126
return service;
127
}
128
if (opts.stub && service['sinonOptions'] && !!service['sinonOptions'].stub) {
129
return service;
130
}
131
}
132
return this._createService(serviceMock, opts);
133
}
134
135
private _createService(serviceMock: IServiceMock<any>, opts: SinonOptions): any {
136
serviceMock.service = serviceMock.service ? serviceMock.service : this._servciesMap.get(serviceMock.id);
137
const service = opts.mock ? sinon.mock(serviceMock.service) : this._createStub(serviceMock.service);
138
service['sinonOptions'] = opts;
139
return service;
140
}
141
142
private _createStub(arg: any): any {
143
return typeof arg === 'object' ? arg : sinon.createStubInstance(arg);
144
}
145
146
private isServiceMock(arg1: any): boolean {
147
return typeof arg1 === 'object' && arg1.hasOwnProperty('id');
148
}
149
150
override createChild(services: ServiceCollection): TestInstantiationService {
151
return new TestInstantiationService(services, false, this);
152
}
153
154
override dispose() {
155
sinon.restore();
156
if (this._properDispose) {
157
super.dispose();
158
}
159
}
160
}
161
162
interface SinonOptions {
163
mock?: boolean;
164
stub?: boolean;
165
}
166
167
export type ServiceIdCtorPair<T> = [id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)];
168
169
export function createServices(disposables: DisposableStore, services: ServiceIdCtorPair<any>[]): TestInstantiationService {
170
const serviceIdentifiers: ServiceIdentifier<any>[] = [];
171
const serviceCollection = new ServiceCollection();
172
173
const define = <T>(id: ServiceIdentifier<T>, ctorOrInstance: T | (new (...args: any[]) => T)) => {
174
if (!serviceCollection.has(id)) {
175
if (typeof ctorOrInstance === 'function') {
176
serviceCollection.set(id, new SyncDescriptor(ctorOrInstance as new (...args: any[]) => T));
177
} else {
178
serviceCollection.set(id, ctorOrInstance);
179
}
180
}
181
serviceIdentifiers.push(id);
182
};
183
184
for (const [id, ctor] of services) {
185
define(id, ctor);
186
}
187
188
const instantiationService = disposables.add(new TestInstantiationService(serviceCollection, true));
189
disposables.add(toDisposable(() => {
190
for (const id of serviceIdentifiers) {
191
const instanceOrDescriptor = serviceCollection.get(id);
192
if (typeof instanceOrDescriptor.dispose === 'function') {
193
instanceOrDescriptor.dispose();
194
}
195
}
196
}));
197
return instantiationService;
198
}
199
200