Subject: | is_string reports incorrect position of first difference when performing comparison in numeric context |
Distribution: Test-LongString (all versions)
Perl version: 5.14.2 for darwin
Operating system: Mac OS X 10.6.8
% uname -a
Darwin zenyatta 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun 7 16:33:36
PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386
Abstract
================
_common_prefix_length sometimes compares arguments in numeric context,
which can cause Test::LongString subroutines such as is_string to report
an incorrect position of the first difference.
_common_prefix_length should always perform a bitwise string XOR, rather
than the ambiguous XOR that sometimes executes in numeric context.
Bug reproduction and analysis
==============================
When $str1 and/or $str2 are passed to _common_prefix_length as numbers
(rather than strings, as expected), $diff is constructed using a bitwise
numeric XOR rather than the desired bitwise string XOR. This has the
effect of presenting confusing error messages, as illustrated here:
#--- BEGIN BUG REPRODUCTION ---
% perl -e "use Test::LongString; is_string(0+'123', '124', '123 is 124');"
not ok 1 - 123 is 124
# Failed test '123 is 124'
# at -e line 1.
# got: "123"
# length: 3
# expected: "124"
# length: 3
# strings begin to differ at char 1 (line 1 column 1)
# Tests were run but no plan was declared and done_testing() was not seen.
#--- END BUG REPRODUCTION ---
Please note the claim that the "strings begin to differ at char 1". It
would be accurate to report that the strings begin to differ at char 3.
Please also note that there is no indication that the arguments are
numbers. In the report, the values indicated by got: and expected: are
both surrounded by double-quotes, which (to me, at least) appears to
imply a string comparison was performed.
Patch
================
A fix for this is to force a bitwise string XOR in _common_prefix_length
per perldoc perlop [1]:
# --- BEGIN PATCH of Test/LongString.pm ---
@@ -62,7 +62,7 @@
sub _common_prefix_length {
my ($str1, $str2) = @_;
- my $diff = $str1 ^ $str2;
+ my $diff = "$str1" ^ "$str2";
my ($pre) = $diff =~ /^(\000*)/;
return length $pre;
}
# --- END PATCH ---
Tests
================
Tests for this fix:
# --- BEGIN TESTS ---
test_out("not ok 1 - '123' is '124'");test_fail(6);
test_diag(qq! got: "123"
# length: 3
# expected: "124"
# length: 3
# strings begin to differ at char 3 (line 1 column 3)!);
is_string("123", "124", "123 is 124");
test_test("two small number strings different");
test_out("not ok 1 - 0+'123' is 0+'124'");
test_fail(6);
test_diag(qq! got: "123"
# length: 3
# expected: "124"
# length: 3
# strings begin to differ at char 3 (line 1 column 3)!);
is_string(0+"123", 0+"124", "123 is 124");
test_test("two small numbers different");
test_out("not ok 1 - 123 is 123xyz");
test_fail(6);
test_diag(qq! got: "123"
# length: 3
# expected: "123xyz"
# length: 6
# strings begin to differ at char 4 (line 1 column 4)!);
is_string("123", "123xyz", "123 is 123xyz");
test_test("small number string different from small string");
test_out("not ok 1 - 0+'123' is 123xyz");
test_fail(6);
test_diag(qq! got: "123"
# length: 3
# expected: "123xyz"
# length: 6
# strings begin to differ at char 4 (line 1 column 4)!);
is_string(0+"123", "123xyz", "123 is 123xyz");
test_test("small number different from small string");
# --- END TESTS ---
Notes
================
[1] http://perldoc.perl.org/perlop.html#Bitwise-String-Operators