Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Revision history for {{$dist->name}}

{{$NEXT}}
- fix _diff to no longer be affected by a caller's ambient $/ (input
record separator); a caller that left $/ set to undef (or any other
non-default value) could previously cause a false failure report for
two JSON documents that are actually the same

0.01 2026-09-29 13:05:04 -0600
- initial version
7 changes: 7 additions & 0 deletions lib/Test/JSON/Diff.pm
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,13 @@ sub _run_to_files ($cmd, $in_path, $out_path, $err_path) {
# returns an empty list if the files are the same, otherwise up to
# $max_lines lines of unified diff, followed by '...' if clipped.
sub _diff ($diff, $context, $max_lines, $expected, $actual, $err_path) {
# reading $diff's output is line based below, so make sure that's true
# regardless of what the caller has done to $/ -- in particular, if $/
# is set to undef (slurp mode), reading an already-at-EOF pipe returns
# an empty string once instead of undef immediately, which is read as a
# single (phantom) line of diff output, producing a false failure.
local $/ = "\n";

my $err = $err_path->openw_raw;
my $pid = open3(my $stdin, my $stdout, '>&' . fileno($err),
$diff, "-U$context", '--label', 'expected', '--label', 'actual', $expected, $actual);
Expand Down
20 changes: 20 additions & 0 deletions t/test_json_diff.t
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,26 @@ subtest 'usage errors' => sub {
qr/max_lines must be a positive integer/, 'bad max_lines';
};

subtest 'insulated from caller $/' => sub {
# a caller that has left $/ in slurp mode (or any non-default value)
# shouldn't affect _diff's own line-based reading of the diff subprocess's
# output -- in particular, with $/ undef, reading an already-at-EOF pipe
# returns an empty string once instead of undef immediately, which used
# to be misread as a single (phantom) line of diff output, producing a
# false failure for two documents that are actually the same.
foreach my $sep ( undef, '', "\x00" ) {
local $/ = $sep;
my $sep_name = defined $sep ? ( length $sep ? "chr(" . ord($sep) . ")" : "''" ) : 'undef';

my ($ret) = run_check( '{"a":1,"b":2}', '{"b":2,"a":1}', "same despite \$/ = $sep_name" );
is $ret, T(), "still detects equal JSON when caller left \$/ = $sep_name";

my ( undef, undef, $diag ) = run_check( '[1,2]', '[2,1]', "different despite \$/ = $sep_name" );
like $diag, qr/^--- expected\n\+\+\+ actual\n\@\@/,
"still produces a real diagnostic for an actual difference when \$/ = $sep_name";
}
};

subtest 'missing tools' => sub {
my $jq = File::Which::which('jq');

Expand Down
Loading