Path: blob/main/src/vs/base/test/common/diff/diff.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { IDiffChange, LcsDiff, StringDiffSequence } from '../../../common/diff/diff.js';7import { ensureNoDisposablesAreLeakedInTestSuite } from '../utils.js';89function createArray<T>(length: number, value: T): T[] {10const r: T[] = [];11for (let i = 0; i < length; i++) {12r[i] = value;13}14return r;15}1617function maskBasedSubstring(str: string, mask: boolean[]): string {18let r = '';19for (let i = 0; i < str.length; i++) {20if (mask[i]) {21r += str.charAt(i);22}23}24return r;25}2627function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void {28const originalMask = createArray(originalStr.length, true);29const modifiedMask = createArray(modifiedStr.length, true);3031let i, j, change;32for (i = 0; i < changes.length; i++) {33change = changes[i];3435if (change.originalLength) {36for (j = 0; j < change.originalLength; j++) {37originalMask[change.originalStart + j] = false;38}39}4041if (change.modifiedLength) {42for (j = 0; j < change.modifiedLength; j++) {43modifiedMask[change.modifiedStart + j] = false;44}45}46}4748const originalAnswer = maskBasedSubstring(originalStr, originalMask);49const modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask);5051if (onlyLength) {52assert.strictEqual(originalAnswer.length, answerStr.length);53assert.strictEqual(modifiedAnswer.length, answerStr.length);54} else {55assert.strictEqual(originalAnswer, answerStr);56assert.strictEqual(modifiedAnswer, answerStr);57}58}5960function lcsInnerTest(originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void {61const diff = new LcsDiff(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr));62const changes = diff.ComputeDiff(false).changes;63assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength);64}6566function stringPower(str: string, power: number): string {67let r = str;68for (let i = 0; i < power; i++) {69r += r;70}71return r;72}7374function lcsTest(originalStr: string, modifiedStr: string, answerStr: string) {75lcsInnerTest(originalStr, modifiedStr, answerStr);76for (let i = 2; i <= 5; i++) {77lcsInnerTest(stringPower(originalStr, i), stringPower(modifiedStr, i), stringPower(answerStr, i), true);78}79}8081suite('Diff', () => {82ensureNoDisposablesAreLeakedInTestSuite();8384test('LcsDiff - different strings tests', function () {85this.timeout(10000);86lcsTest('heLLo world', 'hello orlando', 'heo orld');87lcsTest('abcde', 'acd', 'acd'); // simple88lcsTest('abcdbce', 'bcede', 'bcde'); // skip89lcsTest('abcdefgabcdefg', 'bcehafg', 'bceafg'); // long90lcsTest('abcde', 'fgh', ''); // no match91lcsTest('abcfabc', 'fabc', 'fabc');92lcsTest('0azby0', '9axbzby9', 'azby');93lcsTest('0abc00000', '9a1b2c399999', 'abc');9495lcsTest('fooBar', 'myfooBar', 'fooBar'); // all insertions96lcsTest('fooBar', 'fooMyBar', 'fooBar'); // all insertions97lcsTest('fooBar', 'fooBar', 'fooBar'); // identical sequences98});99});100101suite('Diff - Ported from VS', () => {102ensureNoDisposablesAreLeakedInTestSuite();103104test('using continue processing predicate to quit early', function () {105const left = 'abcdef';106const right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf';107108// We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic109// doesn't get there first.110let predicateCallCount = 0;111112let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {113assert.strictEqual(predicateCallCount, 0);114115predicateCallCount++;116117assert.strictEqual(leftIndex, 1);118119// cancel processing120return false;121});122let changes = diff.ComputeDiff(true).changes;123124assert.strictEqual(predicateCallCount, 1);125126// Doesn't include 'c', 'd', or 'e', since we quit on the first request127assertAnswer(left, right, changes, 'abf');128129130131// Cancel after the first match ('c')132diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {133assert(longestMatchSoFar <= 1); // We never see a match of length > 1134135// Continue processing as long as there hasn't been a match made.136return longestMatchSoFar < 1;137});138changes = diff.ComputeDiff(true).changes;139140assertAnswer(left, right, changes, 'abcf');141142143144// Cancel after the second match ('d')145diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {146assert(longestMatchSoFar <= 2); // We never see a match of length > 2147148// Continue processing as long as there hasn't been a match made.149return longestMatchSoFar < 2;150});151changes = diff.ComputeDiff(true).changes;152153assertAnswer(left, right, changes, 'abcdf');154155156157// Cancel *one iteration* after the second match ('d')158let hitSecondMatch = false;159diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {160assert(longestMatchSoFar <= 2); // We never see a match of length > 2161162const hitYet = hitSecondMatch;163hitSecondMatch = longestMatchSoFar > 1;164// Continue processing as long as there hasn't been a match made.165return !hitYet;166});167changes = diff.ComputeDiff(true).changes;168169assertAnswer(left, right, changes, 'abcdf');170171172173// Cancel after the third and final match ('e')174diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {175assert(longestMatchSoFar <= 3); // We never see a match of length > 3176177// Continue processing as long as there hasn't been a match made.178return longestMatchSoFar < 3;179});180changes = diff.ComputeDiff(true).changes;181182assertAnswer(left, right, changes, 'abcdef');183});184});185186187