modelService.test.ts 20.3 KB
Newer Older
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
A
Alex Dima 已提交
5

6
import * as assert from 'assert';
A
Alex Dima 已提交
7
import { CharCode } from 'vs/base/common/charCode';
8
import * as platform from 'vs/base/common/platform';
A
Alex Dima 已提交
9
import { URI } from 'vs/base/common/uri';
10 11
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
12
import { Selection } from 'vs/editor/common/core/selection';
13
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder';
14
import { DefaultEndOfLine, ITextModel } from 'vs/editor/common/model';
15
import { createTextBuffer } from 'vs/editor/common/model/textModel';
16
import { ModelSemanticColoring, ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
A
Alex Dima 已提交
17
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
18
import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
19
import { NullLogService } from 'vs/platform/log/common/log';
20
import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService';
A
Alex Dima 已提交
21 22
import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
23
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
24 25 26 27 28 29 30 31 32
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DocumentSemanticTokensProvider, DocumentSemanticTokensProviderRegistry, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Barrier, timeout } from 'vs/base/common/async';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
33
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/testTextResourcePropertiesService';
34
import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService';
35
import { getDocumentSemanticTokens, isSemanticTokens } from 'vs/editor/common/services/getSemanticTokens';
36 37

const GENERATE_TESTS = false;
38 39

suite('ModelService', () => {
40
	let disposables: DisposableStore;
41 42 43
	let modelService: ModelServiceImpl;

	setup(() => {
44
		disposables = new DisposableStore();
45 46 47 48
		const configService = new TestConfigurationService();
		configService.setUserConfiguration('files', { 'eol': '\n' });
		configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));

49
		const dialogService = new TestDialogService();
50 51 52 53 54 55 56 57 58
		modelService = disposables.add(new ModelServiceImpl(
			configService,
			new TestTextResourcePropertiesService(configService),
			new TestThemeService(),
			new NullLogService(),
			new UndoRedoService(dialogService, new TestNotificationService()),
			disposables.add(new ModeServiceImpl()),
			new TestLanguageConfigurationService()
		));
59 60 61
	});

	teardown(() => {
62
		disposables.dispose();
63 64 65
	});

	test('EOL setting respected depending on root', () => {
A
Alex Dima 已提交
66
		const model1 = modelService.createModel('farboo', null);
S
Sandeep Somavarapu 已提交
67 68
		const model2 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\myroot\\myfile.txt' : '/myroot/myfile.txt'));
		const model3 = modelService.createModel('farboo', null, URI.file(platform.isWindows ? 'c:\\other\\myfile.txt' : '/other/myfile.txt'));
69

A
Alexandru Dima 已提交
70 71 72
		assert.strictEqual(model1.getOptions().defaultEOL, DefaultEndOfLine.LF);
		assert.strictEqual(model2.getOptions().defaultEOL, DefaultEndOfLine.CRLF);
		assert.strictEqual(model3.getOptions().defaultEOL, DefaultEndOfLine.LF);
73
	});
74

75 76
	test('_computeEdits no change', function () {

77
		const model = disposables.add(createTextModel(
78 79 80 81 82 83
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n')
84
		));
85 86 87 88 89 90 91 92 93

		const textBuffer = createTextBuffer(
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n'),
			DefaultEndOfLine.LF
94
		).textBuffer;
95 96 97

		const actual = ModelServiceImpl._computeEdits(model, textBuffer);

A
Alexandru Dima 已提交
98
		assert.deepStrictEqual(actual, []);
99 100
	});

101 102
	test('_computeEdits first line changed', function () {

103
		const model = disposables.add(createTextModel(
104 105 106 107 108 109
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n')
110
		));
111

112
		const textBuffer = createTextBuffer(
113 114 115 116 117 118 119
			[
				'This is line One', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n'),
			DefaultEndOfLine.LF
120
		).textBuffer;
121

122
		const actual = ModelServiceImpl._computeEdits(model, textBuffer);
123

A
Alexandru Dima 已提交
124
		assert.deepStrictEqual(actual, [
A
Alex Dima 已提交
125
			EditOperation.replaceMove(new Range(1, 1, 2, 1), 'This is line One\n')
126 127 128 129 130
		]);
	});

	test('_computeEdits EOL changed', function () {

131
		const model = disposables.add(createTextModel(
132 133 134 135 136 137
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n')
138
		));
139

140
		const textBuffer = createTextBuffer(
141 142 143 144 145 146 147
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\r\n'),
			DefaultEndOfLine.LF
148
		).textBuffer;
149

150
		const actual = ModelServiceImpl._computeEdits(model, textBuffer);
151

A
Alexandru Dima 已提交
152
		assert.deepStrictEqual(actual, []);
153 154 155 156
	});

	test('_computeEdits EOL and other change 1', function () {

157
		const model = disposables.add(createTextModel(
158 159 160 161 162 163
			[
				'This is line one', //16
				'and this is line number two', //27
				'it is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\n')
164
		));
165

166
		const textBuffer = createTextBuffer(
167 168 169 170 171 172 173
			[
				'This is line One', //16
				'and this is line number two', //27
				'It is followed by #3', //20
				'and finished with the fourth.', //29
			].join('\r\n'),
			DefaultEndOfLine.LF
174
		).textBuffer;
175

176
		const actual = ModelServiceImpl._computeEdits(model, textBuffer);
177

A
Alexandru Dima 已提交
178
		assert.deepStrictEqual(actual, [
A
Alex Dima 已提交
179
			EditOperation.replaceMove(
180 181 182 183 184 185 186 187
				new Range(1, 1, 4, 1),
				[
					'This is line One',
					'and this is line number two',
					'It is followed by #3',
					''
				].join('\r\n')
			)
188 189 190 191 192
		]);
	});

	test('_computeEdits EOL and other change 2', function () {

193
		const model = disposables.add(createTextModel(
194 195 196 197 198
			[
				'package main',	// 1
				'func foo() {',	// 2
				'}'				// 3
			].join('\n')
199
		));
200

201
		const textBuffer = createTextBuffer(
202 203 204 205 206 207 208
			[
				'package main',	// 1
				'func foo() {',	// 2
				'}',			// 3
				''
			].join('\r\n'),
			DefaultEndOfLine.LF
209
		).textBuffer;
210

211
		const actual = ModelServiceImpl._computeEdits(model, textBuffer);
212

A
Alexandru Dima 已提交
213
		assert.deepStrictEqual(actual, [
A
Alex Dima 已提交
214
			EditOperation.replaceMove(new Range(3, 2, 3, 2), '\r\n')
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
		]);
	});

	test('generated1', () => {
		const file1 = ['pram', 'okctibad', 'pjuwtemued', 'knnnm', 'u', ''];
		const file2 = ['tcnr', 'rxwlicro', 'vnzy', '', '', 'pjzcogzur', 'ptmxyp', 'dfyshia', 'pee', 'ygg'];
		assertComputeEdits(file1, file2);
	});

	test('generated2', () => {
		const file1 = ['', 'itls', 'hrilyhesv', ''];
		const file2 = ['vdl', '', 'tchgz', 'bhx', 'nyl'];
		assertComputeEdits(file1, file2);
	});

	test('generated3', () => {
		const file1 = ['ubrbrcv', 'wv', 'xodspybszt', 's', 'wednjxm', 'fklajt', 'fyfc', 'lvejgge', 'rtpjlodmmk', 'arivtgmjdm'];
		const file2 = ['s', 'qj', 'tu', 'ur', 'qerhjjhyvx', 't'];
		assertComputeEdits(file1, file2);
	});

	test('generated4', () => {
		const file1 = ['ig', 'kh', 'hxegci', 'smvker', 'pkdmjjdqnv', 'vgkkqqx', '', 'jrzeb'];
		const file2 = ['yk', ''];
		assertComputeEdits(file1, file2);
	});

	test('does insertions in the middle of the document', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3'
		];
		const file2 = [
			'line 1',
			'line 2',
			'line 5',
			'line 3'
		];
		assertComputeEdits(file1, file2);
	});

	test('does insertions at the end of the document', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3'
		];
		const file2 = [
			'line 1',
			'line 2',
			'line 3',
			'line 4'
		];
		assertComputeEdits(file1, file2);
	});

	test('does insertions at the beginning of the document', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3'
		];
		const file2 = [
			'line 0',
			'line 1',
			'line 2',
			'line 3'
		];
		assertComputeEdits(file1, file2);
	});

	test('does replacements', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3'
		];
		const file2 = [
			'line 1',
			'line 7',
			'line 3'
		];
		assertComputeEdits(file1, file2);
	});

	test('does deletions', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3'
		];
		const file2 = [
			'line 1',
			'line 3'
		];
		assertComputeEdits(file1, file2);
	});

	test('does insert, replace, and delete', () => {
		const file1 = [
			'line 1',
			'line 2',
			'line 3',
			'line 4',
			'line 5',
		];
		const file2 = [
			'line 0', // insert line 0
			'line 1',
			'replace line 2', // replace line 2
			'line 3',
			// delete line 4
			'line 5',
		];
		assertComputeEdits(file1, file2);
	});
332

333 334 335 336 337 338 339
	test('maintains undo for same resource and same content', () => {
		const resource = URI.parse('file://test.txt');

		// create a model
		const model1 = modelService.createModel('text', null, resource);
		// make an edit
		model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
A
Alexandru Dima 已提交
340
		assert.strictEqual(model1.getValue(), 'text1');
341 342 343 344 345 346 347
		// dispose it
		modelService.destroyModel(resource);

		// create a new model with the same content
		const model2 = modelService.createModel('text1', null, resource);
		// undo
		model2.undo();
A
Alexandru Dima 已提交
348
		assert.strictEqual(model2.getValue(), 'text');
349 350
		// dispose it
		modelService.destroyModel(resource);
351 352 353 354 355 356 357 358 359
	});

	test('maintains version id and alternative version id for same resource and same content', () => {
		const resource = URI.parse('file://test.txt');

		// create a model
		const model1 = modelService.createModel('text', null, resource);
		// make an edit
		model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
A
Alexandru Dima 已提交
360
		assert.strictEqual(model1.getValue(), 'text1');
361 362 363 364 365 366 367
		const versionId = model1.getVersionId();
		const alternativeVersionId = model1.getAlternativeVersionId();
		// dispose it
		modelService.destroyModel(resource);

		// create a new model with the same content
		const model2 = modelService.createModel('text1', null, resource);
A
Alexandru Dima 已提交
368 369
		assert.strictEqual(model2.getVersionId(), versionId);
		assert.strictEqual(model2.getAlternativeVersionId(), alternativeVersionId);
370 371
		// dispose it
		modelService.destroyModel(resource);
372
	});
A
Alex Dima 已提交
373 374 375 376 377 378 379 380

	test('does not maintain undo for same resource and different content', () => {
		const resource = URI.parse('file://test.txt');

		// create a model
		const model1 = modelService.createModel('text', null, resource);
		// make an edit
		model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
A
Alexandru Dima 已提交
381
		assert.strictEqual(model1.getValue(), 'text1');
A
Alex Dima 已提交
382 383 384 385 386 387 388
		// dispose it
		modelService.destroyModel(resource);

		// create a new model with the same content
		const model2 = modelService.createModel('text2', null, resource);
		// undo
		model2.undo();
A
Alexandru Dima 已提交
389
		assert.strictEqual(model2.getValue(), 'text2');
390 391
		// dispose it
		modelService.destroyModel(resource);
A
Alex Dima 已提交
392 393 394 395 396 397 398
	});

	test('setValue should clear undo stack', () => {
		const resource = URI.parse('file://test.txt');

		const model = modelService.createModel('text', null, resource);
		model.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
A
Alexandru Dima 已提交
399
		assert.strictEqual(model.getValue(), 'text1');
A
Alex Dima 已提交
400 401 402

		model.setValue('text2');
		model.undo();
A
Alexandru Dima 已提交
403
		assert.strictEqual(model.getValue(), 'text2');
404 405
		// dispose it
		modelService.destroyModel(resource);
A
Alex Dima 已提交
406
	});
407 408
});

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
suite('ModelSemanticColoring', () => {

	const disposables = new DisposableStore();
	const ORIGINAL_FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY;
	let modelService: IModelService;
	let modeService: IModeService;

	setup(() => {
		ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = 0;

		const configService = new TestConfigurationService({ editor: { semanticHighlighting: true } });
		const themeService = new TestThemeService();
		themeService.setTheme(new TestColorTheme({}, ColorScheme.DARK, true));
		modelService = disposables.add(new ModelServiceImpl(
			configService,
			new TestTextResourcePropertiesService(configService),
			themeService,
			new NullLogService(),
427
			new UndoRedoService(new TestDialogService(), new TestNotificationService()),
428
			disposables.add(new ModeServiceImpl()),
429
			new TestLanguageConfigurationService()
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
		));
		modeService = disposables.add(new ModeServiceImpl(false));
	});

	teardown(() => {
		disposables.clear();
		ModelSemanticColoring.FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY = ORIGINAL_FETCH_DOCUMENT_SEMANTIC_TOKENS_DELAY;
	});

	test('DocumentSemanticTokens should be fetched when the result is empty if there are pending changes', async () => {

		disposables.add(ModesRegistry.registerLanguage({ id: 'testMode' }));

		const inFirstCall = new Barrier();
		const delayFirstResult = new Barrier();
		const secondResultProvided = new Barrier();
		let callCount = 0;

		disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode', new class implements DocumentSemanticTokensProvider {
			getLegend(): SemanticTokensLegend {
				return { tokenTypes: ['class'], tokenModifiers: [] };
			}
			async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
				callCount++;
				if (callCount === 1) {
					assert.ok('called once');
					inFirstCall.open();
					await delayFirstResult.wait();
					await timeout(0); // wait for the simple scheduler to fire to check that we do actually get rescheduled
					return null;
				}
				if (callCount === 2) {
					assert.ok('called twice');
					secondResultProvided.open();
					return null;
				}
				assert.fail('Unexpected call');
			}
			releaseDocumentSemanticTokens(resultId: string | undefined): void {
			}
		}));

		const textModel = disposables.add(modelService.createModel('Hello world', modeService.create('testMode')));

		// wait for the provider to be called
		await inFirstCall.wait();

		// the provider is now in the provide call
		// change the text buffer while the provider is running
		textModel.applyEdits([{ range: new Range(1, 1, 1, 1), text: 'x' }]);

		// let the provider finish its first result
		delayFirstResult.open();

		// we need to check that the provider is called again, even if it returns null
		await secondResultProvided.wait();

		// assert that it got called twice
		assert.strictEqual(callCount, 2);
	});
R
Rich Chiodo 已提交
490 491 492

	test('DocumentSemanticTokens should be pick the token provider with actual items', async () => {

493
		let callCount = 0;
R
Rich Chiodo 已提交
494 495 496
		disposables.add(ModesRegistry.registerLanguage({ id: 'testMode2' }));
		disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider {
			getLegend(): SemanticTokensLegend {
497
				return { tokenTypes: ['class1'], tokenModifiers: [] };
R
Rich Chiodo 已提交
498 499
			}
			async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
500
				callCount++;
501 502 503 504 505 506
				// For a secondary request return a different value
				if (lastResultId) {
					return {
						data: new Uint32Array([2, 1, 1, 1, 1, 0, 2, 1, 1, 1])
					};
				}
R
Rich Chiodo 已提交
507
				return {
508
					resultId: '1',
R
Rich Chiodo 已提交
509 510 511 512 513 514 515 516
					data: new Uint32Array([0, 1, 1, 1, 1, 0, 2, 1, 1, 1])
				};
			}
			releaseDocumentSemanticTokens(resultId: string | undefined): void {
			}
		}));
		disposables.add(DocumentSemanticTokensProviderRegistry.register('testMode2', new class implements DocumentSemanticTokensProvider {
			getLegend(): SemanticTokensLegend {
517
				return { tokenTypes: ['class2'], tokenModifiers: [] };
R
Rich Chiodo 已提交
518 519
			}
			async provideDocumentSemanticTokens(model: ITextModel, lastResultId: string | null, token: CancellationToken): Promise<SemanticTokens | SemanticTokensEdits | null> {
520
				callCount++;
R
Rich Chiodo 已提交
521 522 523 524 525 526
				return null;
			}
			releaseDocumentSemanticTokens(resultId: string | undefined): void {
			}
		}));

527 528 529 530 531 532 533 534
		function toArr(arr: Uint32Array): number[] {
			let result: number[] = [];
			for (let i = 0; i < arr.length; i++) {
				result[i] = arr[i];
			}
			return result;
		}

R
Rich Chiodo 已提交
535 536
		const textModel = modelService.createModel('Hello world 2', modeService.create('testMode2'));
		try {
537 538 539 540 541 542 543 544
			let result = await getDocumentSemanticTokens(textModel, null, null, CancellationToken.None);
			assert.ok(result, `We should have tokens (1)`);
			assert.ok(result.tokens, `Tokens are found from multiple providers (1)`);
			assert.ok(isSemanticTokens(result.tokens), `Tokens are full (1)`);
			assert.ok(result.tokens.resultId, `Token result id found from multiple providers (1)`);
			assert.deepStrictEqual(toArr(result.tokens.data), [0, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (1)`);
			assert.deepStrictEqual(callCount, 2, `Called both token providers (1)`);
			assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (1)`);
545 546

			// Make a second request. Make sure we get the secondary value
547 548 549 550 551 552 553 554
			result = await getDocumentSemanticTokens(textModel, result.provider, result.tokens.resultId, CancellationToken.None);
			assert.ok(result, `We should have tokens (2)`);
			assert.ok(result.tokens, `Tokens are found from multiple providers (2)`);
			assert.ok(isSemanticTokens(result.tokens), `Tokens are full (2)`);
			assert.ok(!result.tokens.resultId, `Token result id found from multiple providers (2)`);
			assert.deepStrictEqual(toArr(result.tokens.data), [2, 1, 1, 1, 1, 0, 2, 1, 1, 1], `Token data returned for multiple providers (2)`);
			assert.deepStrictEqual(callCount, 4, `Called both token providers (2)`);
			assert.deepStrictEqual(result.provider.getLegend(), { tokenTypes: ['class1'], tokenModifiers: [] }, `Legend matches the tokens (2)`);
R
Rich Chiodo 已提交
555 556 557 558 559 560 561 562 563 564
		} finally {
			disposables.clear();

			// Wait for scheduler to finish
			await timeout(0);

			// Now dispose the text model
			textModel.dispose();
		}
	});
565 566
});

567
function assertComputeEdits(lines1: string[], lines2: string[]): void {
568
	const model = createTextModel(lines1.join('\n'));
569
	const textBuffer = createTextBuffer(lines2.join('\n'), DefaultEndOfLine.LF).textBuffer;
570 571 572

	// compute required edits
	// let start = Date.now();
573
	const edits = ModelServiceImpl._computeEdits(model, textBuffer);
574 575 576
	// console.log(`took ${Date.now() - start} ms.`);

	// apply edits
A
Alex Dima 已提交
577
	model.pushEditOperations([], edits, null);
578

A
Alexandru Dima 已提交
579
	assert.strictEqual(model.getValue(), lines2.join('\n'));
580
	model.dispose();
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
}

function getRandomInt(min: number, max: number): number {
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getRandomString(minLength: number, maxLength: number): string {
	let length = getRandomInt(minLength, maxLength);
	let t = createStringBuilder(length);
	for (let i = 0; i < length; i++) {
		t.appendASCII(getRandomInt(CharCode.a, CharCode.z));
	}
	return t.build();
}

function generateFile(small: boolean): string[] {
	let lineCount = getRandomInt(1, small ? 3 : 10000);
	let lines: string[] = [];
	for (let i = 0; i < lineCount; i++) {
		lines.push(getRandomString(0, small ? 3 : 10000));
	}
	return lines;
}

if (GENERATE_TESTS) {
	let number = 1;
	while (true) {

		console.log('------TEST: ' + number++);

		const file1 = generateFile(true);
		const file2 = generateFile(true);

		console.log('------TEST GENERATED');

		try {
			assertComputeEdits(file1, file2);
		} catch (err) {
			console.log(err);
			console.log(`
const file1 = ${JSON.stringify(file1).replace(/"/g, '\'')};
const file2 = ${JSON.stringify(file2).replace(/"/g, '\'')};
assertComputeEdits(file1, file2);
`);
			break;
		}
	}
A
Alex Dima 已提交
628
}