searchModel.test.ts 12.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import * as assert from 'assert';
8
import * as sinon from 'sinon';
R
Rob Lourens 已提交
9
import { timeout } from 'vs/base/common/async';
J
Johannes Rieken 已提交
10
import URI from 'vs/base/common/uri';
R
Rob Lourens 已提交
11 12
import { TPromise } from 'vs/base/common/winjs.base';
import { DeferredTPromise } from 'vs/base/test/common/utils';
13
import { Range } from 'vs/editor/common/core/range';
14
import { IModelService } from 'vs/editor/common/services/modelService';
R
Rob Lourens 已提交
15
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
16 17
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
R
Rob Lourens 已提交
18
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
19
import { IFileMatch, IFileSearchStats, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService } from 'vs/platform/search/common/search';
R
Rob Lourens 已提交
20 21 22
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
const nullEvent = new class {

	public id: number;
	public topic: string;
	public name: string;
	public description: string;
	public data: any;

	public startTime: Date;
	public stopTime: Date;

	public stop(): void {
		return;
	}

	public timeTaken(): number {
		return -1;
	}
};


45
suite('SearchModel', () => {
E
Erich Gamma 已提交
46

47
	let instantiationService: TestInstantiationService;
48
	let restoreStubs: sinon.SinonStub[];
E
Erich Gamma 已提交
49

50
	const testSearchStats: IFileSearchStats = {
51
		fromCache: false,
52 53 54 55 56 57 58 59 60 61 62
		resultCount: 1,
		type: 'searchProcess',
		workspaceFolderCount: 1,
		cacheOrSearchEngineStats: {
			traversal: 'node',
			fileWalkTime: 0,
			cmdTime: 0,
			cmdResultCount: 0,
			directoriesWalked: 2,
			filesWalked: 3
		}
C
chrmarti 已提交
63 64
	};

65 66 67 68
	const folderQueries: IFolderQuery[] = [
		{ folder: URI.parse('file://c:/') }
	];

E
Erich Gamma 已提交
69
	setup(() => {
J
Johannes Rieken 已提交
70 71
		restoreStubs = [];
		instantiationService = new TestInstantiationService();
72
		instantiationService.stub(ITelemetryService, NullTelemetryService);
73
		instantiationService.stub(IModelService, stubModelService(instantiationService));
74
		instantiationService.stub(ISearchService, {});
75
		instantiationService.stub(ISearchService, 'search', TPromise.as({ results: [] }));
76 77 78 79 80 81 82 83
	});

	teardown(() => {
		restoreStubs.forEach(element => {
			element.restore();
		});
	});

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete = null): ISearchService {
		return <ISearchService>{
			search(query: ISearchQuery, onProgress: (result: ISearchProgressItem) => void): TPromise<ISearchComplete> {
				return new TPromise(resolve => {
					process.nextTick(() => {
						results.forEach(onProgress);
						resolve(complete);
					});
				});
			}
		};
	}

	function searchServiceWithError(error: Error): ISearchService {
		return <ISearchService>{
			search(query: ISearchQuery, onProgress: (result: ISearchProgressItem) => void): TPromise<ISearchComplete> {
				return new TPromise((resolve, reject) => {
					reject(error);
				});
			}
		};
105 106 107
	}

	test('Search Model: Search adds to results', async () => {
J
Johannes Rieken 已提交
108
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
109
		instantiationService.stub(ISearchService, searchServiceWithResults(results));
110

111
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
112
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
113

J
Johannes Rieken 已提交
114
		let actual = testObject.searchResult.matches();
115 116 117 118

		assert.equal(2, actual.length);
		assert.equal('file://c:/1', actual[0].resource().toString());

J
Johannes Rieken 已提交
119
		let actuaMatches = actual[0].matches();
120 121 122 123 124 125
		assert.equal(2, actuaMatches.length);
		assert.equal('preview 1', actuaMatches[0].text());
		assert.ok(new Range(2, 2, 2, 5).equalsRange(actuaMatches[0].range()));
		assert.equal('preview 1', actuaMatches[1].text());
		assert.ok(new Range(2, 5, 2, 12).equalsRange(actuaMatches[1].range()));

J
Johannes Rieken 已提交
126
		actuaMatches = actual[1].matches();
127 128 129 130 131
		assert.equal(1, actuaMatches.length);
		assert.equal('preview 2', actuaMatches[0].text());
		assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range()));
	});

132
	test('Search Model: Search reports telemetry on search completed', async () => {
J
Johannes Rieken 已提交
133 134
		let target = instantiationService.spy(ITelemetryService, 'publicLog');
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
135
		instantiationService.stub(ISearchService, searchServiceWithResults(results));
136

137
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
138
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
139

140
		assert.ok(target.calledThrice);
141 142
		const data = target.args[0];
		data[1].duration = -1;
143
		assert.deepEqual(['searchResultsFirstRender', { duration: -1 }], data);
144 145
	});

R
Rob Lourens 已提交
146 147 148 149 150
	test('Search Model: Search reports timed telemetry on search when progress is not called', () => {
		let target2 = sinon.spy();
		stub(nullEvent, 'stop', target2);
		let target1 = sinon.stub().returns(nullEvent);
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
151

152
		instantiationService.stub(ISearchService, searchServiceWithResults([]));
J
Joao Moreno 已提交
153

R
Rob Lourens 已提交
154 155
		let testObject = instantiationService.createInstance(SearchModel);
		const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
156

R
Rob Lourens 已提交
157 158 159 160 161 162 163
		return result.then(() => {
			return timeout(1).then(() => {
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
			});
		});
	});
164

R
Rob Lourens 已提交
165
	test('Search Model: Search reports timed telemetry on search when progress is called', () => {
J
Johannes Rieken 已提交
166
		let target2 = sinon.spy();
167
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
168
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
169
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
170

171 172 173
		instantiationService.stub(ISearchService, searchServiceWithResults(
			[aRawMatch('file://c:/1', aLineMatch('some preview'))],
			{ results: [], stats: testSearchStats }));
174

J
Johannes Rieken 已提交
175
		let testObject = instantiationService.createInstance(SearchModel);
176
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
177

R
Rob Lourens 已提交
178 179 180 181
		return result.then(() => {
			return timeout(1).then(() => {
				// timeout because promise handlers may run in a different order. We only care that these
				// are fired at some point.
J
Joao Moreno 已提交
182 183 184 185
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.equal(1, target2.callCount);
			});
186
		});
187 188
	});

189
	test('Search Model: Search reports timed telemetry on search when error is called', () => {
J
Johannes Rieken 已提交
190
		let target2 = sinon.spy();
191
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
192
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
193
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
194

195
		instantiationService.stub(ISearchService, searchServiceWithError(new Error('error')));
196

J
Johannes Rieken 已提交
197
		let testObject = instantiationService.createInstance(SearchModel);
198
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
199

200
		return timeout(1).then(() => {
201
			return result.then(() => { }, () => {
J
Joao Moreno 已提交
202 203 204 205
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
			});
206
		});
207 208
	});

209
	test('Search Model: Search reports timed telemetry on search when error is cancelled error', () => {
J
Johannes Rieken 已提交
210
		let target2 = sinon.spy();
211
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
212
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
213
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
214

215
		let promise = new DeferredTPromise<ISearchComplete>();
J
Johannes Rieken 已提交
216
		instantiationService.stub(ISearchService, 'search', promise);
217

J
Johannes Rieken 已提交
218
		let testObject = instantiationService.createInstance(SearchModel);
219
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
220 221 222

		promise.cancel();

223
		return timeout(1).then(() => {
224
			return result.then(() => { }, () => {
J
Joao Moreno 已提交
225 226 227 228
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
			});
229
		});
230 231
	});

232
	test('Search Model: Search results are cleared during search', async () => {
J
Johannes Rieken 已提交
233
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
234
		instantiationService.stub(ISearchService, searchServiceWithResults(results));
J
Johannes Rieken 已提交
235
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
236
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
237 238
		assert.ok(!testObject.searchResult.isEmpty());

239
		instantiationService.stub(ISearchService, searchServiceWithResults([]));
240

241
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
242 243 244
		assert.ok(testObject.searchResult.isEmpty());
	});

245
	test('Search Model: Previous search is cancelled when new search is called', async () => {
J
Johannes Rieken 已提交
246
		let target = sinon.spy();
247
		instantiationService.stub(ISearchService, 'search', new DeferredTPromise(target));
J
Johannes Rieken 已提交
248
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
249

250
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
251
		instantiationService.stub(ISearchService, searchServiceWithResults([]));
252
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
253 254 255 256

		assert.ok(target.calledOnce);
	});

257
	test('getReplaceString returns proper replace string for regExpressions', async () => {
J
Johannes Rieken 已提交
258
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]))];
259
		instantiationService.stub(ISearchService, searchServiceWithResults(results));
S
Sandeep Somavarapu 已提交
260

J
Johannes Rieken 已提交
261
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
262
		await testObject.search({ contentPattern: { pattern: 're' }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
263 264 265 266
		testObject.replaceString = 'hello';
		let match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

267
		await testObject.search({ contentPattern: { pattern: 're', isRegExp: true }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
268 269 270
		match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

271
		await testObject.search({ contentPattern: { pattern: 're(?:vi)', isRegExp: true }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
272 273 274
		match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

275
		await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
276 277 278
		match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

279
		await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
280 281 282 283 284
		testObject.replaceString = 'hello$1';
		match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('helloe', match.replaceString);
	});

285 286 287 288 289 290 291 292
	function aRawMatch(resource: string, ...lineMatches: ILineMatch[]): IFileMatch {
		return { resource: URI.parse(resource), lineMatches };
	}

	function aLineMatch(preview: string, lineNumber: number = 1, offsetAndLengths: number[][] = [[0, 1]]): ILineMatch {
		return { preview, lineNumber, offsetAndLengths };
	}

293
	function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub {
J
Johannes Rieken 已提交
294
		const stub = sinon.stub(arg1, arg2, arg3);
295 296 297 298
		restoreStubs.push(stub);
		return stub;
	}

299 300 301 302 303
	function stubModelService(instantiationService: TestInstantiationService): IModelService {
		instantiationService.stub(IConfigurationService, new TestConfigurationService());
		return instantiationService.createInstance(ModelServiceImpl);
	}

304
});