Skip to content

Commit 75fa007

Browse files
committed
Tests for shortestCommonSupersequence (claude)
1 parent 46d7561 commit 75fa007

File tree

1 file changed

+380
-0
lines changed
  • packages/interactivity-router/src/assets/test/scs

1 file changed

+380
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,380 @@
1+
/**
2+
* Internal dependencies
3+
*/
4+
import { shortestCommonSupersequence } from '../../scs';
5+
6+
describe( 'shortestCommonSupersequence', () => {
7+
// BASIC FUNCTIONALITY TESTS
8+
describe( 'Basic functionality', () => {
9+
it( 'should handle empty arrays', () => {
10+
// Both arrays empty
11+
expect( shortestCommonSupersequence( [], [] ) ).toEqual( [] );
12+
13+
// X empty, Y with values
14+
const Y = [ 1, 2, 3 ];
15+
expect( shortestCommonSupersequence( [], Y ) ).toEqual( Y );
16+
17+
// Y empty, X with values
18+
const X = [ 1, 2, 3 ];
19+
expect( shortestCommonSupersequence( X, [] ) ).toEqual( X );
20+
} );
21+
22+
it( 'should handle identical arrays', () => {
23+
// Same elements, same order
24+
const X = [ 1, 2, 3 ];
25+
const Y = [ 1, 2, 3 ];
26+
expect( shortestCommonSupersequence( X, Y ) ).toEqual( [
27+
1, 2, 3,
28+
] );
29+
30+
// Should be the same reference from X when elements are identical
31+
const result = shortestCommonSupersequence( X, Y );
32+
expect( result[ 0 ] ).toBe( X[ 0 ] );
33+
expect( result[ 1 ] ).toBe( X[ 1 ] );
34+
expect( result[ 2 ] ).toBe( X[ 2 ] );
35+
} );
36+
37+
it( 'should handle simple cases with partial overlap', () => {
38+
// Overlap at the beginning
39+
const result1 = shortestCommonSupersequence(
40+
[ 1, 2, 3 ],
41+
[ 1, 2, 4 ]
42+
);
43+
// Verify it's a valid supersequence
44+
expect( isSubsequence( [ 1, 2, 3 ], result1 ) ).toBe( true );
45+
expect( isSubsequence( [ 1, 2, 4 ], result1 ) ).toBe( true );
46+
// Verify optimal length
47+
expect( result1.length ).toBe( 4 );
48+
49+
// Overlap at the end
50+
const result2 = shortestCommonSupersequence(
51+
[ 1, 2, 3 ],
52+
[ 0, 2, 3 ]
53+
);
54+
// Verify it's a valid supersequence
55+
expect( isSubsequence( [ 1, 2, 3 ], result2 ) ).toBe( true );
56+
expect( isSubsequence( [ 0, 2, 3 ], result2 ) ).toBe( true );
57+
// Verify optimal length
58+
expect( result2.length ).toBe( 4 );
59+
60+
// Middle overlap
61+
const result3 = shortestCommonSupersequence(
62+
[ 1, 2, 3 ],
63+
[ 0, 2, 4 ]
64+
);
65+
// Verify it's a valid supersequence
66+
expect( isSubsequence( [ 1, 2, 3 ], result3 ) ).toBe( true );
67+
expect( isSubsequence( [ 0, 2, 4 ], result3 ) ).toBe( true );
68+
// Verify optimal length
69+
expect( result3.length ).toBe( 5 );
70+
} );
71+
72+
it( 'should handle arrays with no overlap', () => {
73+
const X = [ 1, 2, 3 ];
74+
const Y = [ 4, 5, 6 ];
75+
const result = shortestCommonSupersequence( X, Y );
76+
77+
// Result should contain all elements from both X and Y
78+
expect( result ).toContain( 1 );
79+
expect( result ).toContain( 2 );
80+
expect( result ).toContain( 3 );
81+
expect( result ).toContain( 4 );
82+
expect( result ).toContain( 5 );
83+
expect( result ).toContain( 6 );
84+
85+
// Result length should be X.length + Y.length
86+
expect( result.length ).toBe( X.length + Y.length );
87+
} );
88+
89+
it( 'should verify the supersequence length is optimal', () => {
90+
// Example where the shortest supersequence is shorter than just concatenating
91+
const X = [ 1, 2, 3 ];
92+
const Y = [ 2, 3, 4 ];
93+
const result = shortestCommonSupersequence( X, Y );
94+
95+
// Result should be [1,2,3,4] with length 4, shorter than X.length + Y.length = 6
96+
expect( result.length ).toBe( 4 );
97+
expect( result ).toEqual( [ 1, 2, 3, 4 ] );
98+
} );
99+
} );
100+
101+
// EDGE CASES
102+
describe( 'Edge cases', () => {
103+
it( 'should handle subsequence cases', () => {
104+
// X is a subsequence of Y
105+
const X1 = [ 1, 3 ];
106+
const Y1 = [ 1, 2, 3, 4 ];
107+
expect( shortestCommonSupersequence( X1, Y1 ) ).toEqual( Y1 );
108+
109+
// Y is a subsequence of X
110+
const X2 = [ 1, 2, 3, 4 ];
111+
const Y2 = [ 1, 3 ];
112+
expect( shortestCommonSupersequence( X2, Y2 ) ).toEqual( X2 );
113+
} );
114+
115+
it( 'should handle arrays with duplicate elements', () => {
116+
// Duplicates in X
117+
const resultX = shortestCommonSupersequence(
118+
[ 1, 2, 2, 3 ],
119+
[ 1, 3, 4 ]
120+
);
121+
expect( isSubsequence( [ 1, 2, 2, 3 ], resultX ) ).toBe( true );
122+
expect( isSubsequence( [ 1, 3, 4 ], resultX ) ).toBe( true );
123+
expect( resultX.length ).toBe( 5 );
124+
125+
// Duplicates in Y
126+
const resultY = shortestCommonSupersequence(
127+
[ 1, 3, 4 ],
128+
[ 1, 2, 2, 3 ]
129+
);
130+
expect( isSubsequence( [ 1, 3, 4 ], resultY ) ).toBe( true );
131+
expect( isSubsequence( [ 1, 2, 2, 3 ], resultY ) ).toBe( true );
132+
expect( resultY.length ).toBe( 5 );
133+
134+
// Duplicates in both X and Y
135+
const resultXY = shortestCommonSupersequence(
136+
[ 1, 2, 2, 3 ],
137+
[ 1, 2, 3, 3 ]
138+
);
139+
expect( isSubsequence( [ 1, 2, 2, 3 ], resultXY ) ).toBe( true );
140+
expect( isSubsequence( [ 1, 2, 3, 3 ], resultXY ) ).toBe( true );
141+
expect( resultXY.length ).toBe( 5 );
142+
143+
// Multiple duplicate occurrences
144+
const result = shortestCommonSupersequence(
145+
[ 1, 2, 1, 2 ],
146+
[ 2, 1, 2, 1 ]
147+
);
148+
149+
// Result should preserve order and contain all elements
150+
expect( result.length ).toBeLessThanOrEqual( 8 ); // Maximum length would be 8
151+
152+
// Verify it's a valid supersequence by checking subsequences
153+
expect( isSubsequence( [ 1, 2, 1, 2 ], result ) ).toBe( true );
154+
expect( isSubsequence( [ 2, 1, 2, 1 ], result ) ).toBe( true );
155+
} );
156+
157+
it( 'should handle completely duplicate array', () => {
158+
expect(
159+
shortestCommonSupersequence( [ 1, 1, 1 ], [ 1, 1 ] )
160+
).toEqual( [ 1, 1, 1 ] );
161+
} );
162+
163+
it( 'should handle asymmetric cases with different array lengths', () => {
164+
// X much longer than Y
165+
const X1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
166+
const Y1 = [ 2, 5 ];
167+
const result1 = shortestCommonSupersequence( X1, Y1 );
168+
169+
// Result should be same as X since Y is a subsequence
170+
expect( result1 ).toEqual( X1 );
171+
172+
// Y much longer than X
173+
const X2 = [ 2, 5 ];
174+
const Y2 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
175+
const result2 = shortestCommonSupersequence( X2, Y2 );
176+
177+
// Result should be same as Y since X is a subsequence
178+
expect( result2 ).toEqual( Y2 );
179+
} );
180+
} );
181+
182+
// ORDERING TESTS
183+
describe( 'Order preservation', () => {
184+
it( 'should preserve order of elements from input arrays', () => {
185+
const X = [ 1, 3, 5 ];
186+
const Y = [ 5, 3, 1 ];
187+
const result = shortestCommonSupersequence( X, Y );
188+
189+
// Test that it's a valid supersequence of both arrays
190+
expect( isSubsequence( X, result ) ).toBe( true );
191+
expect( isSubsequence( Y, result ) ).toBe( true );
192+
193+
// The algorithm should produce a result shorter than X.length + Y.length
194+
expect( result.length ).toBeLessThanOrEqual( X.length + Y.length );
195+
196+
// Test reverse case
197+
const result2 = shortestCommonSupersequence( Y, X );
198+
expect( isSubsequence( X, result2 ) ).toBe( true );
199+
expect( isSubsequence( Y, result2 ) ).toBe( true );
200+
} );
201+
202+
it( 'should handle zigzag patterns correctly', () => {
203+
const X = [ 1, 3, 5, 7, 9 ];
204+
const Y = [ 2, 4, 6, 8, 10 ];
205+
const result = shortestCommonSupersequence( X, Y );
206+
207+
// Verify it's a valid supersequence
208+
expect( isSubsequence( X, result ) ).toBe( true );
209+
expect( isSubsequence( Y, result ) ).toBe( true );
210+
211+
// Verify optimal length (should be X.length + Y.length because no common elements)
212+
expect( result.length ).toBe( 10 );
213+
214+
// Verify all elements are present
215+
X.concat( Y ).forEach( ( elem ) => {
216+
expect( result ).toContain( elem );
217+
} );
218+
} );
219+
} );
220+
221+
// ADVANCED CASES
222+
describe( 'Advanced cases', () => {
223+
it( 'should handle complex interleavings', () => {
224+
const X = [ 1, 2, 3, 1, 2, 3 ];
225+
const Y = [ 3, 2, 1, 3, 2, 1 ];
226+
const result = shortestCommonSupersequence( X, Y );
227+
228+
// Result should be a valid supersequence
229+
expect( isSubsequence( X, result ) ).toBe( true );
230+
expect( isSubsequence( Y, result ) ).toBe( true );
231+
232+
// Result should be shorter than X.length + Y.length
233+
expect( result.length ).toBeLessThan( X.length + Y.length );
234+
} );
235+
236+
it( 'should handle almost identical arrays with one difference', () => {
237+
const X = [ 1, 2, 3, 4, 5 ];
238+
const Y = [ 1, 2, 3, 5, 4 ];
239+
const result = shortestCommonSupersequence( X, Y );
240+
241+
// Result should contain 6 elements (optimal length)
242+
expect( result.length ).toBe( 6 );
243+
244+
// The first 3 elements should be 1,2,3
245+
expect( result.slice( 0, 3 ) ).toEqual( [ 1, 2, 3 ] );
246+
} );
247+
248+
it( 'should handle multiple matching possibilities optimally', () => {
249+
const X = [ 1, 1, 2, 2 ];
250+
const Y = [ 1, 2, 1, 2 ];
251+
const result = shortestCommonSupersequence( X, Y );
252+
253+
// Result should be a valid supersequence
254+
expect( isSubsequence( X, result ) ).toBe( true );
255+
expect( isSubsequence( Y, result ) ).toBe( true );
256+
257+
// Length should be less than or equal to 6 (optimal)
258+
expect( result.length ).toBeLessThanOrEqual( 6 );
259+
} );
260+
} );
261+
262+
// CUSTOM EQUALITY FUNCTION TESTS
263+
describe( 'Custom equality function', () => {
264+
it( 'should use custom equality function for comparing elements', () => {
265+
const X = [
266+
{ id: 1, name: 'a' },
267+
{ id: 2, name: 'b' },
268+
];
269+
const Y = [
270+
{ id: 1, name: 'c' },
271+
{ id: 2, name: 'd' },
272+
];
273+
274+
// Custom equality function that only compares 'id' property
275+
const isEqual = ( a, b ) => a.id === b.id;
276+
277+
const result = shortestCommonSupersequence( X, Y, isEqual );
278+
279+
// Should treat objects with same ids as equal
280+
expect( result.length ).toBe( 2 );
281+
expect( result[ 0 ].id ).toBe( 1 );
282+
expect( result[ 1 ].id ).toBe( 2 );
283+
284+
// Should use references from X
285+
expect( result[ 0 ] ).toBe( X[ 0 ] );
286+
expect( result[ 1 ] ).toBe( X[ 1 ] );
287+
} );
288+
289+
it( 'should handle case-insensitive comparison', () => {
290+
const X = [ 'A', 'B', 'C' ];
291+
const Y = [ 'a', 'b', 'D' ];
292+
293+
// Case-insensitive comparison
294+
const isEqual = ( a, b ) => a.toLowerCase() === b.toLowerCase();
295+
296+
const result = shortestCommonSupersequence( X, Y, isEqual );
297+
298+
// Check references are from X when elements are considered equal
299+
expect( result[ 0 ] ).toBe( X[ 0 ] ); // 'A' instead of 'a'
300+
expect( result[ 1 ] ).toBe( X[ 1 ] ); // 'B' instead of 'b'
301+
302+
// Verify it contains C and D (which don't match)
303+
expect( result.includes( 'C' ) ).toBe( true );
304+
expect( result.includes( 'D' ) ).toBe( true );
305+
306+
// Check length is correct (4 instead of 6)
307+
expect( result.length ).toBe( 4 );
308+
} );
309+
} );
310+
311+
// NULL/UNDEFINED HANDLING
312+
describe( 'Null and undefined handling', () => {
313+
it( 'should handle arrays with null values', () => {
314+
const X = [ 1, null, 3 ];
315+
const Y = [ null, 2, 3 ];
316+
const result = shortestCommonSupersequence( X, Y );
317+
318+
// Should treat null values correctly
319+
expect( result ).toEqual( [ 1, null, 2, 3 ] );
320+
} );
321+
322+
it( 'should handle arrays with undefined', () => {
323+
const X = [ 1, undefined, 3 ];
324+
const Y = [ 2, undefined, 4 ];
325+
const result = shortestCommonSupersequence( X, Y );
326+
327+
// Should handle undefined values correctly
328+
expect( result ).toContain( 1 );
329+
expect( result ).toContain( undefined );
330+
expect( result ).toContain( 2 );
331+
expect( result ).toContain( 3 );
332+
expect( result ).toContain( 4 );
333+
334+
// Length should be shorter than concatenating the arrays
335+
expect( result.length ).toBeLessThan( X.length + Y.length );
336+
} );
337+
} );
338+
339+
// MUTATION TESTS
340+
describe( 'Mutation checks', () => {
341+
it( 'should not modify the input arrays', () => {
342+
const X = [ 1, 2, 3 ];
343+
const Y = [ 2, 3, 4 ];
344+
345+
// Create copies for comparison
346+
const originalX = [ ...X ];
347+
const originalY = [ ...Y ];
348+
349+
shortestCommonSupersequence( X, Y );
350+
351+
// Input arrays should remain unchanged
352+
expect( X ).toEqual( originalX );
353+
expect( Y ).toEqual( originalY );
354+
} );
355+
} );
356+
} );
357+
358+
// Helper functions for testing
359+
function isSubsequence( arr, superseq ) {
360+
let i = 0;
361+
let j = 0;
362+
363+
while ( i < arr.length && j < superseq.length ) {
364+
if ( arr[ i ] === superseq[ j ] ) {
365+
i++;
366+
}
367+
j++;
368+
}
369+
370+
return i === arr.length;
371+
}
372+
373+
function findLastIndex( arr, predicate ) {
374+
for ( let i = arr.length - 1; i >= 0; i-- ) {
375+
if ( predicate( arr[ i ] ) ) {
376+
return i;
377+
}
378+
}
379+
return -1;
380+
}

0 commit comments

Comments
 (0)