diff --git a/CHANGELOG.md b/CHANGELOG.md index 848b63965041..2d07bacc6c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - `[docs]` Clarify usage of `--projects` CLI option ([#6872](https://github.com/facebook/jest/pull/6872)) - `[docs]` Correct `failure-change` notification mode ([#6878](https://github.com/facebook/jest/pull/6878)) - `[scripts]` Don’t remove node_modules from subdirectories of presets in e2e tests ([#6948](https://github.com/facebook/jest/pull/6948)) +- `[diff-sequences]` Double-check number of differences in tests ([#6953](https://github.com/facebook/jest/pull/6953)) ## 23.5.0 diff --git a/packages/diff-sequences/src/__tests__/index.test.js b/packages/diff-sequences/src/__tests__/index.test.js index 9db44b86793c..ef5c894415a7 100644 --- a/packages/diff-sequences/src/__tests__/index.test.js +++ b/packages/diff-sequences/src/__tests__/index.test.js @@ -149,34 +149,84 @@ const assertCommonItems = ( } }; +// Given lengths of sequences and input function to compare items at indexes, +// return number of differences according to baseline greedy forward algorithm. +const countDifferences = ( + aLength: number, + bLength: number, + isCommon, +): number => { + const dMax = aLength + bLength; + const aIndexes = [-1]; // initialize for aLast + 1 in loop when d = 0 + + for (let d = 0; d <= dMax; d += 1) { + let aIndexPrev1 = 0; // that is, not yet set + + for (let iF = 0, kF = -d; iF <= d; iF += 1, kF += 2) { + const aFirst = + iF === 0 || (iF !== d && aIndexPrev1 < aIndexes[iF]) + ? aIndexes[iF] // vertical to insert from b + : aIndexPrev1 + 1; // horizontal to delete from a + + // To get last point of path segment, move along diagonal of common items. + let aLast = aFirst; + let bLast = aFirst - kF; + while ( + aLast + 1 < aLength && + bLast + 1 < bLength && + isCommon(aLast + 1, bLast + 1) + ) { + aLast += 1; + bLast += 1; + } + + aIndexPrev1 = aIndexes[iF]; + aIndexes[iF] = aLast; + + if (aLast === aLength - 1 && bLast === bLength - 1) { + return d; + } + } + } + throw new Error(`countDifferences did not return a number`); +}; + // Return array of items in a longest common subsequence of array-like objects. const findCommonItems = ( a: Array | string, b: Array | string, ): Array => { + const aLength = a.length; + const bLength = b.length; + const isCommon = (aIndex: number, bIndex: number) => { + assertMin('input aIndex', aIndex, 0); + assertEnd('input aIndex', aIndex, aLength); + assertMin('input bIndex', bIndex, 0); + assertEnd('input bIndex', bIndex, bLength); + return a[aIndex] === b[bIndex]; + }; + const array = []; diff( - a.length, - b.length, - (aIndex: number, bIndex: number) => { - assertMin('input aIndex', aIndex, 0); - assertEnd('input aIndex', aIndex, a.length); - assertMin('input bIndex', bIndex, 0); - assertEnd('input bIndex', bIndex, b.length); - return a[aIndex] === b[bIndex]; - }, + aLength, + bLength, + isCommon, (nCommon: number, aCommon: number, bCommon: number) => { assertMin('output nCommon', nCommon, 1); assertMin('output aCommon', aCommon, 0); - assertMax('output aCommon + nCommon', aCommon + nCommon, a.length); + assertMax('output aCommon + nCommon', aCommon + nCommon, aLength); assertMin('output bCommon', bCommon, 0); - assertMax('output bCommon + nCommon', bCommon + nCommon, b.length); + assertMax('output bCommon + nCommon', bCommon + nCommon, bLength); assertCommonItems(a, b, nCommon, aCommon, bCommon); for (; nCommon !== 0; nCommon -= 1, aCommon += 1) { array.push(a[aCommon]); } }, ); + + const nDifferences = countDifferences(aLength, bLength, isCommon); + expect(aLength + bLength - 2 * array.length).toBe(nDifferences); + return array; };