searchModel.test.ts 11.8 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';
9
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
10
import { DeferredPPromise } from 'vs/base/test/common/utils';
11
import { PPromise } from 'vs/base/common/winjs.base';
12
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
J
Johannes Rieken 已提交
13
import URI from 'vs/base/common/uri';
14
import { IFileMatch, IFolderQuery, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats } from 'vs/platform/search/common/search';
15 16
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
17
import { Range } from 'vs/editor/common/core/range';
18
import { IModelService } from 'vs/editor/common/services/modelService';
19 20 21
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
22
import { timeout } from 'vs/base/common/async';
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 51 52
	const testSearchStats: IUncachedSearchStats = {
		fromCache: false,
		resultCount: 4,
C
Christof Marti 已提交
53 54
		traversal: 'node',
		errors: [],
C
chrmarti 已提交
55 56 57 58 59 60
		fileWalkStartTime: 0,
		fileWalkResultTime: 1,
		directoriesWalked: 2,
		filesWalked: 3
	};

61 62 63 64
	const folderQueries: IFolderQuery[] = [
		{ folder: URI.parse('file://c:/') }
	];

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

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

80 81 82 83 84 85 86 87 88 89
	function ppromiseWithProgress(results: IFileMatch[]): () => PPromise<any, IFileMatch> {
		return () => new PPromise((resolve, reject, progress) => {
			process.nextTick(() => {
				results.forEach(progress);
				resolve(null);
			});
		});
	}

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

93
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
94
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
95

J
Johannes Rieken 已提交
96
		let actual = testObject.searchResult.matches();
97 98 99 100

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

J
Johannes Rieken 已提交
101
		let actuaMatches = actual[0].matches();
102 103 104 105 106 107
		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 已提交
108
		actuaMatches = actual[1].matches();
109 110 111 112 113
		assert.equal(1, actuaMatches.length);
		assert.equal('preview 2', actuaMatches[0].text());
		assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range()));
	});

114
	test('Search Model: Search reports telemetry on search completed', async () => {
J
Johannes Rieken 已提交
115 116
		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'))];
117
		instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results));
118

J
Johannes Rieken 已提交
119
		let testObject = instantiationService.createInstance(SearchModel);
120
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
121

122
		assert.ok(target.calledThrice);
123 124
		const data = target.args[0];
		data[1].duration = -1;
125
		assert.deepEqual(['searchResultsFirstRender', { duration: -1 }], data);
126 127
	});

B
Benjamin Pasero 已提交
128 129 130 131 132
	// 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);
133

B
Benjamin Pasero 已提交
134
	// 	instantiationService.stub(ISearchService, 'search', ppromiseWithProgress([]));
135

B
Benjamin Pasero 已提交
136 137
	// 	let testObject = instantiationService.createInstance(SearchModel);
	// 	const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
J
Joao Moreno 已提交
138

B
Benjamin Pasero 已提交
139 140 141 142 143
	// 	return timeout(1).then(() => {
	// 		return result.then(() => {
	// 			assert.ok(target1.calledWith('searchResultsFirstRender'));
	// 			assert.ok(target1.calledWith('searchResultsFinished'));
	// 		});
144

B
Benjamin Pasero 已提交
145 146
	// 	});
	// });
147

148
	test('Search Model: Search reports timed telemetry on search when progress is called', () => {
J
Johannes Rieken 已提交
149
		let target2 = sinon.spy();
150
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
151
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
152
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
153

J
Johannes Rieken 已提交
154 155
		let promise = new DeferredPPromise<ISearchComplete, ISearchProgressItem>();
		instantiationService.stub(ISearchService, 'search', promise);
156

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

		promise.progress(aRawMatch('file://c:/1', aLineMatch('some preview')));
J
Johannes Rieken 已提交
161
		promise.complete({ results: [], stats: testSearchStats });
162

163
		return timeout(1).then(() => {
164
			return result.then(() => {
J
Joao Moreno 已提交
165 166 167 168
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.equal(1, target2.callCount);
			});
169
		});
170 171
	});

172
	test('Search Model: Search reports timed telemetry on search when error is called', () => {
J
Johannes Rieken 已提交
173
		let target2 = sinon.spy();
174
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
175
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
176
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
177

J
Johannes Rieken 已提交
178 179
		let promise = new DeferredPPromise<ISearchComplete, ISearchProgressItem>();
		instantiationService.stub(ISearchService, 'search', promise);
180

J
Johannes Rieken 已提交
181
		let testObject = instantiationService.createInstance(SearchModel);
182
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
183 184 185

		promise.error('error');

186
		return timeout(1).then(() => {
187
			return result.then(() => { }, () => {
J
Joao Moreno 已提交
188 189 190 191
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
			});
192
		});
193 194
	});

195
	test('Search Model: Search reports timed telemetry on search when error is cancelled error', () => {
J
Johannes Rieken 已提交
196
		let target2 = sinon.spy();
197
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
198
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
199
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
200

J
Johannes Rieken 已提交
201 202
		let promise = new DeferredPPromise<ISearchComplete, ISearchProgressItem>();
		instantiationService.stub(ISearchService, 'search', promise);
203

J
Johannes Rieken 已提交
204
		let testObject = instantiationService.createInstance(SearchModel);
205
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
206 207 208

		promise.cancel();

209
		return timeout(1).then(() => {
210
			return result.then(() => { }, () => {
J
Joao Moreno 已提交
211 212 213 214
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
			});
215
		});
216 217
	});

218
	test('Search Model: Search results are cleared during search', async () => {
J
Johannes Rieken 已提交
219
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
220
		instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results));
J
Johannes Rieken 已提交
221
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
222
		await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
223 224
		assert.ok(!testObject.searchResult.isEmpty());

J
Johannes Rieken 已提交
225
		instantiationService.stub(ISearchService, 'search', new DeferredPPromise<ISearchComplete, ISearchProgressItem>());
226

227
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
228 229 230
		assert.ok(testObject.searchResult.isEmpty());
	});

231
	test('Search Model: Previous search is cancelled when new search is called', async () => {
J
Johannes Rieken 已提交
232 233 234
		let target = sinon.spy();
		instantiationService.stub(ISearchService, 'search', new DeferredPPromise((c, e, p) => { }, target));
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
235

236
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
J
Johannes Rieken 已提交
237
		instantiationService.stub(ISearchService, 'search', new DeferredPPromise<ISearchComplete, ISearchProgressItem>());
238
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
239 240 241 242

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

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

J
Johannes Rieken 已提交
247
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
248
		await testObject.search({ contentPattern: { pattern: 're' }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
249 250 251 252
		testObject.replaceString = 'hello';
		let match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

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

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

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

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

271 272 273 274 275 276 277 278
	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 };
	}

279
	function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub {
J
Johannes Rieken 已提交
280
		const stub = sinon.stub(arg1, arg2, arg3);
281 282 283 284
		restoreStubs.push(stub);
		return stub;
	}

285 286 287 288 289
	function stubModelService(instantiationService: TestInstantiationService): IModelService {
		instantiationService.stub(IConfigurationService, new TestConfigurationService());
		return instantiationService.createInstance(ModelServiceImpl);
	}

290
});