Stop printing both strings in full when Should-BeString fails on a big string - #3003
Stop printing both strings in full when Should-BeString fails on a big string#3003nohwnd wants to merge 1 commit into
Conversation
…g string Comparing whole generated files against an expected copy is a real use for Should-BeString, and until now failing one printed both strings in their entirety with a caret under the first differing character. For the 10 000 line file in #2951 that is a hundred thousand lines of output and a character offset nobody can act on. Should -BeExactly in v5 at least truncated. Strings that are small enough to read are left exactly as they were, the full text with a caret is the most precise thing we can show. Bigger ones get a compact view: - More than 10 lines: the line that differs, with two lines of context either side, the expected line marked - and the actual one +. Only the differing lines are expanded, so a trailing space or a stray CR is visible without turning every other line into escape codes. - Longer than 120 characters on one line: an excerpt around the difference with ellipses, the way v5 did it. Splitting on line endings throws away the difference when the difference *is* the line ending, and the first version of this happily printed two identical looking blocks claiming line 1 differed. When every line matches, it now says so and names the endings on each side instead, and points at -NormalizeLineEnding. Fix #2951 🤖
More examples of what this printsRan the branch (5c5d19c) against a wider set of inputs so we can look at the actual output before deciding. Everything below is copy-pasted from a real failure message, nothing is hand written. Small strings, unchanged10 lines or fewer and under 120 characters still print in full with the caret, exactly like on One more line and it switches to the compact view: Realistic filesA JSON document where one value differs: The same document reindented from 2 spaces to 4: A line inserted at line 6 of 20: A line deleted at line 6 of 20: A trailing space on line 7, which is invisible in the source but shows up because only the differing lines are escaped: Line endingsAll 20 lines LF against all 20 CRLF: Only one of the 20 line endings is CRLF: Three lines, so still small, so it keeps the old rendering, which is readable here anyway: Long single lines401 characters, difference in the middle: Difference at index 0, so no leading ellipsis and the caret sits at the start: Expected is a prefix of actual, so the caret points just past the end of the expected excerpt: Escaping is length preserving ( Inside a real run
🤖 |
What the examples show that I am not happy with yetSix things came out of running the wider set. The first three produce output that is wrong or useless, the last three are judgement calls. The 1. The context lines always come from Expected, so they go blank when Expected is shorterExpected has 12 lines, actual has 15: Lines 14 and 15 of the actual are Fix is either to fall back to the actual line when expected ran out, or to stop printing context past the end of the shorter side. 2. A difference that is only a trailing newline prints two blank linesBoth sides render as nothing. This is the same class of problem as the line-endings case that this PR already handles, the split threw away the only difference there is. A file ending with or without a final newline is a common real difference, so it deserves its own sentence the way the line endings got one, something like "The strings are the same, actual is missing the final newline." 3. The differing line itself is not truncatedThe whole point of the PR is not to print a huge string, and the line branch happily prints a 500 character line twice. Real output, with the two long lines shortened by me so this comment stays readable, they are printed in full: A minified JS bundle, a base64 blob or a long connection string in an otherwise short config file hits this. The 120 character window from the single line branch should apply to the differing line here too, with the caret, otherwise we solved the tall case and left the wide case. 4. Mixed line endings do not say which lineCorrect and still not actionable. When it is 1 of 19 the useful part is which one. When it is all of them the counts are enough. Worth naming the first line whose ending differs when the endings are mixed. 5. Only the first differing line is reported, so an insert reads as an editA line inserted at position 6 of 20: Technically true, and it hides that everything below line 6 is shifted by one and otherwise fine. The line counts in the header are the only hint. I do not want a diff library here (see the PR description), but a shift is cheap to detect: if 6. The 10 line and 120 character thresholds are a hard cliff10 lines gives the full string with a caret, 11 lines gives the compact block. Both are readable, but the same test can flip between two completely different message shapes because someone added a line. Alternative is to always use the line view for anything with more than one line and keep the caret view only for single line strings. That is a bigger behavior change and would need the existing message shape tests updated, so I left it, but it is the version I would defend more easily. 🤖 |
|
we will instead embed a diffing lib to avoid going into a diffing lib authoring bussiness. |
Fix #2951
Comparing whole generated files against an expected copy is a real use for
Should-BeString, and failing one printed both strings in their entirety with a caret under the first differing character. For the 10 000 line file in the issue that is a hundred thousand lines of output, and a character offset into it that nobody can act on.Should -BeExactlyin v5 at least truncated.Before, on the issue's example: both strings printed in full.
After:
What changed
Strings small enough to read are left exactly as they were, the full text with a caret is the most precise thing we can show. Only bigger ones get a compact view:
Only the differing lines are passed through
Expand-SpecialCharacters, so a trailing space or a stray CR is visible without turning every other line into escape codes.Line endings
Splitting on line endings throws away the difference when the difference is the line ending. The first version of this printed two identical looking blocks and claimed line 1 differed, which is worse than no diff at all. It now detects that every line matches and names the endings instead:
Not done here
No diff library. One difference in a big string needs "which line, plus context", which is a first-differing-line scan. A real diff engine earns its keep when there are many scattered differences and you need hunks, which is the snapshot testing case, and that deserves its own justification rather than being folded in here.
Should-BeString -Output Hexfrom #2562 is also still open.Tests
Three added: the differing-line-with-context case including an assertion that the whole string is not printed, the line-endings-only case, and the long single line truncation. The existing message-shape test passes untouched, and
tst/functions/assertis at 1146 passed / 7 failed, identical to main's baseline on the same machine.🤖