assert: avoid expensive diff for large values#62798
assert: avoid expensive diff for large values#62798semimikoh wants to merge 2 commits intonodejs:mainfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #62798 +/- ##
==========================================
- Coverage 91.55% 89.61% -1.95%
==========================================
Files 355 706 +351
Lines 149381 219161 +69780
Branches 23364 41997 +18633
==========================================
+ Hits 136765 196391 +59626
- Misses 12354 14677 +2323
- Partials 262 8093 +7831
🚀 New features to boost your workflow:
|
| diffType !== 'full' && | ||
| inspectedSplitActual.length + inspectedSplitExpected.length > kMaxDiffLineCount | ||
| ) { | ||
| message = `\n${colors.green}+${colors.white} ${getTruncatedDiffValue(inspectedSplitActual)}\n` + |
There was a problem hiding this comment.
Hardcoded colors bypasses the partialDeepStrictEqual coloring convention
There was a problem hiding this comment.
Updated the truncated diff path to follow the partialDeepStrictEqual coloring convention.
| @@ -213,6 +222,13 @@ function createErrDiff(actual, expected, operator, customMessage, diffType = 'si | |||
| message = ArrayPrototypeJoin(inspectedSplitActual, '\n'); | |||
| } | |||
| header = ''; | |||
| } else if ( | |||
There was a problem hiding this comment.
This can completely hide the real diff when users actually need it.
There was a problem hiding this comment.
Narrowed the skip path to large root-level mismatches only. Large values with the same root representation still use the normal line diff, and I added a regression test for that case.
|
I think we can further optimize this by short-circuiting diff generation for clearly incompatible types (e.g. Buffer vs Array). Right now, even in trivial mismatches, we still go through inspection and diff generation which is expensive for large values. Would it make sense to add a fast-path before diffing, something like:
This could avoid unnecessary work and improve performance beyond just limiting diff size. |
f41f6cf to
00c4485
Compare
00c4485 to
8f55a99
Compare
|
@BHUVANSH855 As the original reporter of the issue, that's what I also would've expected the behaviour to be. Thinking about it more, I'm actually slightly confused as to why it even tries to diff the content in the first place in With @semimikoh Would it perhaps make more sense to do an "until first mismatch" diff (maybe with a minimum of 5 lines or so) of the two inputs, and then indicating skipped lines for the remainder? That way it could still visualize and colorize the specific differences, highlighting eg. structural differences too, but without running a full diff. IIRC a Myers diff is very fast on long matching sequences, but I'm not sure how it's implemented here and whether it's possible to stop it early for example. |
Thanks for the insights! That makes sense regarding visualization expectations. Building on my earlier thought — instead of completely skipping diff for incompatible types, maybe we could introduce a lightweight fast-path:
This way:
Would this approach align better with the intended behavior? |
This limits the input size used for simple assertion diffs.
When an assertion fails for large inspected values, such as
assert.strictEqual(Buffer.alloc(n), [buffer]),AssertionErrorcurrentlyruns the full line-based Myers diff over the inspected output. For large
Buffers this can make the failure path much slower than linear even though the
values are trivially different.
This change keeps the existing rich diff behavior for smaller values, but skips
the Myers diff for very large inspected outputs in the default
simplediffmode. In that case, the error message includes a truncated representation of
both sides and marks skipped lines instead.
diff: 'full'remains available for callers that explicitly request the fulldiff.
Refs: #62792
Test
test/parallel/test-assert-deep.js16.0, while this tree requires a newer macOS toolchain. Local build fails in
V8 because
std::atomic_refis unavailable.