Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/common/diff/diff.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { IDiffChange, LcsDiff, StringDiffSequence } from '../../../common/diff/diff.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../utils.js';
9
10
function createArray<T>(length: number, value: T): T[] {
11
const r: T[] = [];
12
for (let i = 0; i < length; i++) {
13
r[i] = value;
14
}
15
return r;
16
}
17
18
function maskBasedSubstring(str: string, mask: boolean[]): string {
19
let r = '';
20
for (let i = 0; i < str.length; i++) {
21
if (mask[i]) {
22
r += str.charAt(i);
23
}
24
}
25
return r;
26
}
27
28
function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void {
29
const originalMask = createArray(originalStr.length, true);
30
const modifiedMask = createArray(modifiedStr.length, true);
31
32
let i, j, change;
33
for (i = 0; i < changes.length; i++) {
34
change = changes[i];
35
36
if (change.originalLength) {
37
for (j = 0; j < change.originalLength; j++) {
38
originalMask[change.originalStart + j] = false;
39
}
40
}
41
42
if (change.modifiedLength) {
43
for (j = 0; j < change.modifiedLength; j++) {
44
modifiedMask[change.modifiedStart + j] = false;
45
}
46
}
47
}
48
49
const originalAnswer = maskBasedSubstring(originalStr, originalMask);
50
const modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask);
51
52
if (onlyLength) {
53
assert.strictEqual(originalAnswer.length, answerStr.length);
54
assert.strictEqual(modifiedAnswer.length, answerStr.length);
55
} else {
56
assert.strictEqual(originalAnswer, answerStr);
57
assert.strictEqual(modifiedAnswer, answerStr);
58
}
59
}
60
61
function lcsInnerTest(originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void {
62
const diff = new LcsDiff(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr));
63
const changes = diff.ComputeDiff(false).changes;
64
assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength);
65
}
66
67
function stringPower(str: string, power: number): string {
68
let r = str;
69
for (let i = 0; i < power; i++) {
70
r += r;
71
}
72
return r;
73
}
74
75
function lcsTest(originalStr: string, modifiedStr: string, answerStr: string) {
76
lcsInnerTest(originalStr, modifiedStr, answerStr);
77
for (let i = 2; i <= 5; i++) {
78
lcsInnerTest(stringPower(originalStr, i), stringPower(modifiedStr, i), stringPower(answerStr, i), true);
79
}
80
}
81
82
suite('Diff', () => {
83
ensureNoDisposablesAreLeakedInTestSuite();
84
85
test('LcsDiff - different strings tests', function () {
86
this.timeout(10000);
87
lcsTest('heLLo world', 'hello orlando', 'heo orld');
88
lcsTest('abcde', 'acd', 'acd'); // simple
89
lcsTest('abcdbce', 'bcede', 'bcde'); // skip
90
lcsTest('abcdefgabcdefg', 'bcehafg', 'bceafg'); // long
91
lcsTest('abcde', 'fgh', ''); // no match
92
lcsTest('abcfabc', 'fabc', 'fabc');
93
lcsTest('0azby0', '9axbzby9', 'azby');
94
lcsTest('0abc00000', '9a1b2c399999', 'abc');
95
96
lcsTest('fooBar', 'myfooBar', 'fooBar'); // all insertions
97
lcsTest('fooBar', 'fooMyBar', 'fooBar'); // all insertions
98
lcsTest('fooBar', 'fooBar', 'fooBar'); // identical sequences
99
});
100
});
101
102
suite('Diff - Ported from VS', () => {
103
ensureNoDisposablesAreLeakedInTestSuite();
104
105
test('using continue processing predicate to quit early', function () {
106
const left = 'abcdef';
107
const right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf';
108
109
// We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic
110
// doesn't get there first.
111
let predicateCallCount = 0;
112
113
let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
114
assert.strictEqual(predicateCallCount, 0);
115
116
predicateCallCount++;
117
118
assert.strictEqual(leftIndex, 1);
119
120
// cancel processing
121
return false;
122
});
123
let changes = diff.ComputeDiff(true).changes;
124
125
assert.strictEqual(predicateCallCount, 1);
126
127
// Doesn't include 'c', 'd', or 'e', since we quit on the first request
128
assertAnswer(left, right, changes, 'abf');
129
130
131
132
// Cancel after the first match ('c')
133
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
134
assert(longestMatchSoFar <= 1); // We never see a match of length > 1
135
136
// Continue processing as long as there hasn't been a match made.
137
return longestMatchSoFar < 1;
138
});
139
changes = diff.ComputeDiff(true).changes;
140
141
assertAnswer(left, right, changes, 'abcf');
142
143
144
145
// Cancel after the second match ('d')
146
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
147
assert(longestMatchSoFar <= 2); // We never see a match of length > 2
148
149
// Continue processing as long as there hasn't been a match made.
150
return longestMatchSoFar < 2;
151
});
152
changes = diff.ComputeDiff(true).changes;
153
154
assertAnswer(left, right, changes, 'abcdf');
155
156
157
158
// Cancel *one iteration* after the second match ('d')
159
let hitSecondMatch = false;
160
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
161
assert(longestMatchSoFar <= 2); // We never see a match of length > 2
162
163
const hitYet = hitSecondMatch;
164
hitSecondMatch = longestMatchSoFar > 1;
165
// Continue processing as long as there hasn't been a match made.
166
return !hitYet;
167
});
168
changes = diff.ComputeDiff(true).changes;
169
170
assertAnswer(left, right, changes, 'abcdf');
171
172
173
174
// Cancel after the third and final match ('e')
175
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
176
assert(longestMatchSoFar <= 3); // We never see a match of length > 3
177
178
// Continue processing as long as there hasn't been a match made.
179
return longestMatchSoFar < 3;
180
});
181
changes = diff.ComputeDiff(true).changes;
182
183
assertAnswer(left, right, changes, 'abcdef');
184
});
185
});
186
187