searchModel.test.ts 12.9 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 12
import { PPromise } from 'vs/base/common/winjs.base';
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

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
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;
	}
};


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

46 47
	let instantiationService: TestInstantiationService;
	let restoreStubs;
E
Erich Gamma 已提交
48

49 50 51
	const testSearchStats: IUncachedSearchStats = {
		fromCache: false,
		resultCount: 4,
C
Christof Marti 已提交
52 53
		traversal: 'node',
		errors: [],
C
chrmarti 已提交
54 55 56 57 58 59
		fileWalkStartTime: 0,
		fileWalkResultTime: 1,
		directoriesWalked: 2,
		filesWalked: 3
	};

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

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

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

	test('Search Model: Search adds to results', function () {
J
Johannes Rieken 已提交
80 81
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
		instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results }));
82

83 84
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
85

J
Johannes Rieken 已提交
86
		let actual = testObject.searchResult.matches();
87 88 89 90

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

J
Johannes Rieken 已提交
91
		let actuaMatches = actual[0].matches();
92 93 94 95 96 97
		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 已提交
98
		actuaMatches = actual[1].matches();
99 100 101 102 103 104
		assert.equal(1, actuaMatches.length);
		assert.equal('preview 2', actuaMatches[0].text());
		assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range()));
	});

	test('Search Model: Search adds to results during progress', function (done) {
J
Johannes Rieken 已提交
105 106 107
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
		let promise = new DeferredPPromise<ISearchComplete, ISearchProgressItem>();
		instantiationService.stub(ISearchService, 'search', promise);
108

J
Johannes Rieken 已提交
109
		let testObject = instantiationService.createInstance(SearchModel);
110
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
111 112 113

		promise.progress(results[0]);
		promise.progress(results[1]);
J
Johannes Rieken 已提交
114
		promise.complete({ results: [], stats: testSearchStats });
115 116

		result.done(() => {
J
Johannes Rieken 已提交
117
			let actual = testObject.searchResult.matches();
118 119 120 121

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

J
Johannes Rieken 已提交
122
			let actuaMatches = actual[0].matches();
123 124 125 126 127 128
			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 已提交
129
			actuaMatches = actual[1].matches();
130 131 132 133 134 135 136 137 138
			assert.equal(1, actuaMatches.length);
			assert.equal('preview 2', actuaMatches[0].text());
			assert.ok(new Range(2, 1, 2, 2).equalsRange(actuaMatches[0].range()));

			done();
		});
	});

	test('Search Model: Search reports telemetry on search completed', function () {
J
Johannes Rieken 已提交
139 140 141
		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'))];
		instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results }));
142

J
Johannes Rieken 已提交
143
		let testObject = instantiationService.createInstance(SearchModel);
144
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
145 146

		assert.ok(target.calledOnce);
147 148
		const data = target.args[0];
		data[1].duration = -1;
B
Benjamin Pasero 已提交
149
		assert.deepEqual(['searchResultsShown', { count: 3, fileCount: 2, options: {}, duration: -1, useRipgrep: undefined }], data);
150 151
	});

J
Joao Moreno 已提交
152
	test('Search Model: Search reports timed telemetry on search when progress is not called', function (done) {
J
Johannes Rieken 已提交
153
		let target2 = sinon.spy();
154
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
155
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
156
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
157

J
Johannes Rieken 已提交
158
		instantiationService.stub(ISearchService, 'search', PPromise.as({ results: [] }));
159

J
Johannes Rieken 已提交
160
		let testObject = instantiationService.createInstance(SearchModel);
161
		const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
J
Joao Moreno 已提交
162 163 164 165 166

		setTimeout(() => {
			result.done(() => {
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
167

J
Joao Moreno 已提交
168 169 170
				done();
			});
		}, 0);
171 172 173
	});

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

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

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

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

J
Joao Moreno 已提交
188 189 190 191 192
		setTimeout(() => {
			result.done(() => {
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.equal(1, target2.callCount);
193

J
Joao Moreno 已提交
194 195 196
				done();
			});
		}, 0);
197 198 199
	});

	test('Search Model: Search reports timed telemetry on search when error is called', function (done) {
J
Johannes Rieken 已提交
200
		let target2 = sinon.spy();
201
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
202
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
203
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
204

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

J
Johannes Rieken 已提交
208
		let testObject = instantiationService.createInstance(SearchModel);
209
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
210 211 212

		promise.error('error');

J
Joao Moreno 已提交
213
		setTimeout(() => {
J
Johannes Rieken 已提交
214
			result.done(() => { }, () => {
J
Joao Moreno 已提交
215 216 217
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
218

J
Joao Moreno 已提交
219 220 221
				done();
			});
		}, 0);
222 223 224
	});

	test('Search Model: Search reports timed telemetry on search when error is cancelled error', function (done) {
J
Johannes Rieken 已提交
225
		let target2 = sinon.spy();
226
		stub(nullEvent, 'stop', target2);
J
Johannes Rieken 已提交
227
		let target1 = sinon.stub().returns(nullEvent);
J
Joao Moreno 已提交
228
		instantiationService.stub(ITelemetryService, 'publicLog', target1);
229

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

J
Johannes Rieken 已提交
233
		let testObject = instantiationService.createInstance(SearchModel);
234
		let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
235 236 237

		promise.cancel();

J
Joao Moreno 已提交
238
		setTimeout(() => {
J
Johannes Rieken 已提交
239
			result.done(() => { }, () => {
J
Joao Moreno 已提交
240 241 242 243 244 245
				assert.ok(target1.calledWith('searchResultsFirstRender'));
				assert.ok(target1.calledWith('searchResultsFinished'));
				// assert.ok(target2.calledOnce);
				done();
			});
		}, 0);
246 247 248
	});

	test('Search Model: Search results are cleared during search', function () {
J
Johannes Rieken 已提交
249 250 251
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))];
		instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results }));
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
252
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
253 254
		assert.ok(!testObject.searchResult.isEmpty());

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

257
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
258 259 260 261
		assert.ok(testObject.searchResult.isEmpty());
	});

	test('Search Model: Previous search is cancelled when new search is called', function () {
J
Johannes Rieken 已提交
262 263 264
		let target = sinon.spy();
		instantiationService.stub(ISearchService, 'search', new DeferredPPromise((c, e, p) => { }, target));
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
265

266
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
J
Johannes Rieken 已提交
267
		instantiationService.stub(ISearchService, 'search', new DeferredPPromise<ISearchComplete, ISearchProgressItem>());
268
		testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries });
269 270 271 272

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

S
Sandeep Somavarapu 已提交
273
	test('getReplaceString returns proper replace string for regExpressions', function () {
J
Johannes Rieken 已提交
274 275
		let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]))];
		instantiationService.stub(ISearchService, 'search', PPromise.as({ results: results }));
S
Sandeep Somavarapu 已提交
276

J
Johannes Rieken 已提交
277
		let testObject: SearchModel = instantiationService.createInstance(SearchModel);
278
		testObject.search({ contentPattern: { pattern: 're' }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
279 280 281 282
		testObject.replaceString = 'hello';
		let match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('hello', match.replaceString);

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

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

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

295
		testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: 1, folderQueries });
S
Sandeep Somavarapu 已提交
296 297 298 299 300
		testObject.replaceString = 'hello$1';
		match = testObject.searchResult.matches()[0].matches()[0];
		assert.equal('helloe', match.replaceString);
	});

301 302 303 304 305 306 307 308
	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 };
	}

J
Johannes Rieken 已提交
309 310
	function stub(arg1, arg2, arg3): sinon.SinonStub {
		const stub = sinon.stub(arg1, arg2, arg3);
311 312 313 314
		restoreStubs.push(stub);
		return stub;
	}

315 316 317 318 319
	function stubModelService(instantiationService: TestInstantiationService): IModelService {
		instantiationService.stub(IConfigurationService, new TestConfigurationService());
		return instantiationService.createInstance(ModelServiceImpl);
	}

320
});