| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
65908
|
use strict; |
|
|
1
|
|
|
|
|
11
|
|
|
|
1
|
|
|
|
|
25
|
|
|
2
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
38
|
|
|
3
|
|
|
|
|
|
|
package Test::BinaryData 0.015; |
|
4
|
|
|
|
|
|
|
# ABSTRACT: compare two things, give hex dumps if they differ |
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
27
|
use 5.006; |
|
|
1
|
|
|
|
|
4
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
|
9
|
|
|
|
|
|
|
#pod |
|
10
|
|
|
|
|
|
|
#pod use Test::BinaryData; |
|
11
|
|
|
|
|
|
|
#pod |
|
12
|
|
|
|
|
|
|
#pod my $computed_data = do_something_complicated; |
|
13
|
|
|
|
|
|
|
#pod my $expected_data = read_file('correct.data'); |
|
14
|
|
|
|
|
|
|
#pod |
|
15
|
|
|
|
|
|
|
#pod is_binary( |
|
16
|
|
|
|
|
|
|
#pod $computed_data, |
|
17
|
|
|
|
|
|
|
#pod $expected_data, |
|
18
|
|
|
|
|
|
|
#pod "basic data computation", |
|
19
|
|
|
|
|
|
|
#pod ); |
|
20
|
|
|
|
|
|
|
#pod |
|
21
|
|
|
|
|
|
|
#pod =head1 DESCRIPTION |
|
22
|
|
|
|
|
|
|
#pod |
|
23
|
|
|
|
|
|
|
#pod Sometimes using Test::More's C test isn't good enough. Its diagnostics may |
|
24
|
|
|
|
|
|
|
#pod make it easy to miss differences between strings. |
|
25
|
|
|
|
|
|
|
#pod |
|
26
|
|
|
|
|
|
|
#pod For example, given two strings which differ only in their line endings, you can |
|
27
|
|
|
|
|
|
|
#pod end up with diagnostic output like this: |
|
28
|
|
|
|
|
|
|
#pod |
|
29
|
|
|
|
|
|
|
#pod not ok 1 |
|
30
|
|
|
|
|
|
|
#pod # Failed test in demo.t at line 8. |
|
31
|
|
|
|
|
|
|
#pod # got: 'foo |
|
32
|
|
|
|
|
|
|
#pod # bar |
|
33
|
|
|
|
|
|
|
#pod # ' |
|
34
|
|
|
|
|
|
|
#pod # expected: 'foo |
|
35
|
|
|
|
|
|
|
#pod # bar |
|
36
|
|
|
|
|
|
|
#pod # ' |
|
37
|
|
|
|
|
|
|
#pod |
|
38
|
|
|
|
|
|
|
#pod That's not very helpful, except to tell you that the alphanumeric characters |
|
39
|
|
|
|
|
|
|
#pod seem to be in the right place. By using C instead of C, this |
|
40
|
|
|
|
|
|
|
#pod output would be generated instead: |
|
41
|
|
|
|
|
|
|
#pod |
|
42
|
|
|
|
|
|
|
#pod not ok 2 |
|
43
|
|
|
|
|
|
|
#pod # Failed test in demo.t at line 10. |
|
44
|
|
|
|
|
|
|
#pod # have (hex) have want (hex) want |
|
45
|
|
|
|
|
|
|
#pod # 666f6f0a6261720a---- foo.bar. ! 666f6f0d0a6261720d0a foo..bar.. |
|
46
|
|
|
|
|
|
|
#pod |
|
47
|
|
|
|
|
|
|
#pod The "!" tells us that the lines differ, and we can quickly scan the bytes that |
|
48
|
|
|
|
|
|
|
#pod make up the line to see which differ. |
|
49
|
|
|
|
|
|
|
#pod |
|
50
|
|
|
|
|
|
|
#pod When comparing very long strings, we can stop after we've seen a few |
|
51
|
|
|
|
|
|
|
#pod differences. Here, we'll just look for two: |
|
52
|
|
|
|
|
|
|
#pod |
|
53
|
|
|
|
|
|
|
#pod # have (hex) have want (hex) want |
|
54
|
|
|
|
|
|
|
#pod # 416c6c20435220616e64 All CR and = 416c6c20435220616e64 All CR and |
|
55
|
|
|
|
|
|
|
#pod # 206e6f204c46206d616b no LF mak = 206e6f204c46206d616b no LF mak |
|
56
|
|
|
|
|
|
|
#pod # 6573204d616320612064 es Mac a d = 6573204d616320612064 es Mac a d |
|
57
|
|
|
|
|
|
|
#pod # 756c6c20626f792e0d41 ull boy..A = 756c6c20626f792e0d41 ull boy..A |
|
58
|
|
|
|
|
|
|
#pod # 6c6c20435220616e6420 ll CR and = 6c6c20435220616e6420 ll CR and |
|
59
|
|
|
|
|
|
|
#pod # 6e6f204c46206d616b65 no LF make = 6e6f204c46206d616b65 no LF make |
|
60
|
|
|
|
|
|
|
#pod # 73204d61632061206475 s Mac a du = 73204d61632061206475 s Mac a du |
|
61
|
|
|
|
|
|
|
#pod # 6c6c20626f792e0d416c ll boy..Al ! 6c6c20626f792e0a416c ll boy..Al |
|
62
|
|
|
|
|
|
|
#pod # 6c20435220616e64206e l CR and n = 6c20435220616e64206e l CR and n |
|
63
|
|
|
|
|
|
|
#pod # 6f204c46206d616b6573 o LF makes = 6f204c46206d616b6573 o LF makes |
|
64
|
|
|
|
|
|
|
#pod # 204d616320612064756c Mac a dul = 204d616320612064756c Mac a dul |
|
65
|
|
|
|
|
|
|
#pod # 6c20626f792e0d416c6c l boy..All ! 6c20626f792e0a416c6c l boy..All |
|
66
|
|
|
|
|
|
|
#pod # 20435220616e64206e6f CR and no = 20435220616e64206e6f CR and no |
|
67
|
|
|
|
|
|
|
#pod # ... |
|
68
|
|
|
|
|
|
|
#pod |
|
69
|
|
|
|
|
|
|
#pod =head1 WARNING |
|
70
|
|
|
|
|
|
|
#pod |
|
71
|
|
|
|
|
|
|
#pod This library is for comparing B data. That is, B. |
|
72
|
|
|
|
|
|
|
#pod Often, in Perl 5, it is not clear whether a scalar contains a byte string or a |
|
73
|
|
|
|
|
|
|
#pod character strings. You should use this library for comparing byte strings |
|
74
|
|
|
|
|
|
|
#pod only. If either the "have" or "want" values contain wide characters -- that is, |
|
75
|
|
|
|
|
|
|
#pod characters that won't fit in one byte -- then the test will fail. |
|
76
|
|
|
|
|
|
|
#pod |
|
77
|
|
|
|
|
|
|
#pod =cut |
|
78
|
|
|
|
|
|
|
|
|
79
|
1
|
|
|
1
|
|
5
|
use Carp (); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
13
|
|
|
80
|
1
|
|
|
1
|
|
5
|
use Test::Builder; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
737
|
|
|
81
|
|
|
|
|
|
|
require Exporter; |
|
82
|
|
|
|
|
|
|
@Test::BinaryData::ISA = qw(Exporter); |
|
83
|
|
|
|
|
|
|
@Test::BinaryData::EXPORT = qw(is_binary); |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub import { |
|
86
|
1
|
|
|
1
|
|
5
|
my($self) = shift; |
|
87
|
1
|
|
|
|
|
2
|
my $pack = caller; |
|
88
|
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
4
|
my $Test = Test::Builder->new; |
|
90
|
|
|
|
|
|
|
|
|
91
|
1
|
|
|
|
|
7
|
$Test->exported_to($pack); |
|
92
|
1
|
50
|
|
|
|
25
|
$Test->plan(@_) if @_; |
|
93
|
|
|
|
|
|
|
|
|
94
|
1
|
|
|
|
|
108
|
$self->export_to_level(1, $self, @Test::BinaryData::EXPORT); |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
#pod =func is_binary |
|
98
|
|
|
|
|
|
|
#pod |
|
99
|
|
|
|
|
|
|
#pod is_binary($have, $want, $comment, \%arg); |
|
100
|
|
|
|
|
|
|
#pod |
|
101
|
|
|
|
|
|
|
#pod This test behaves like Test::More's C test, but if the given data are not |
|
102
|
|
|
|
|
|
|
#pod string equal, the diagnostics emits four columns, describing the strings in |
|
103
|
|
|
|
|
|
|
#pod parallel, showing a simplified ASCII representation and a hexadecimal dump. |
|
104
|
|
|
|
|
|
|
#pod |
|
105
|
|
|
|
|
|
|
#pod If C<$want> is an arrayref, it's treated as a sequence of strings giving hex |
|
106
|
|
|
|
|
|
|
#pod values for expected bytes. For example, this is a passing test: |
|
107
|
|
|
|
|
|
|
#pod |
|
108
|
|
|
|
|
|
|
#pod is_binary( |
|
109
|
|
|
|
|
|
|
#pod "Mumblefrotz", |
|
110
|
|
|
|
|
|
|
#pod [ qw(4d75 6d62 6c65 6672 6f74 7a0a) ], |
|
111
|
|
|
|
|
|
|
#pod ); |
|
112
|
|
|
|
|
|
|
#pod |
|
113
|
|
|
|
|
|
|
#pod Notice that each string in the sequence is broken into two-character pieces. |
|
114
|
|
|
|
|
|
|
#pod This makes this interface accept the kind of dumps produced by F or |
|
115
|
|
|
|
|
|
|
#pod Test::BinaryData itself. |
|
116
|
|
|
|
|
|
|
#pod |
|
117
|
|
|
|
|
|
|
#pod Between the got and expected data for each line, a "=" or "!" indicates whether |
|
118
|
|
|
|
|
|
|
#pod the chunks are identical or different. |
|
119
|
|
|
|
|
|
|
#pod |
|
120
|
|
|
|
|
|
|
#pod The C<$comment> and C<%arg> arguments are optional. Valid arguments are: |
|
121
|
|
|
|
|
|
|
#pod |
|
122
|
|
|
|
|
|
|
#pod columns - the number of screen columns available |
|
123
|
|
|
|
|
|
|
#pod if the COLUMNS environment variable is an positive integer, then |
|
124
|
|
|
|
|
|
|
#pod COLUMNS - is used; otherwise, the default is 79 |
|
125
|
|
|
|
|
|
|
#pod |
|
126
|
|
|
|
|
|
|
#pod max_diffs - if given, this is the maximum number of differing lines that will |
|
127
|
|
|
|
|
|
|
#pod be compared; if output would have been given beyond this line, |
|
128
|
|
|
|
|
|
|
#pod it will be replaced with an elipsis ("...") |
|
129
|
|
|
|
|
|
|
#pod |
|
130
|
|
|
|
|
|
|
#pod =cut |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub _widths { |
|
133
|
10
|
|
|
10
|
|
20
|
my ($total) = @_; |
|
134
|
|
|
|
|
|
|
|
|
135
|
10
|
|
|
|
|
15
|
$total = $total |
|
136
|
|
|
|
|
|
|
- 2 # the "# " that begins each diagnostics line |
|
137
|
|
|
|
|
|
|
- 3 # the " ! " or " = " line between got / expected |
|
138
|
|
|
|
|
|
|
- 2 # the space between hex/ascii representations |
|
139
|
|
|
|
|
|
|
; |
|
140
|
|
|
|
|
|
|
|
|
141
|
10
|
|
|
|
|
23
|
my $sixth = int($total / 6); |
|
142
|
10
|
|
|
|
|
21
|
return ($sixth * 2, $sixth); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub is_binary { |
|
146
|
10
|
|
|
10
|
1
|
22506
|
my ($have, $want, $comment, $arg) = @_; |
|
147
|
|
|
|
|
|
|
|
|
148
|
10
|
|
|
|
|
30
|
my $Test = Test::Builder->new; |
|
149
|
|
|
|
|
|
|
|
|
150
|
10
|
|
100
|
|
|
60
|
$arg ||= {}; |
|
151
|
|
|
|
|
|
|
|
|
152
|
10
|
50
|
|
|
|
25
|
unless (defined $arg->{columns}) { |
|
153
|
10
|
50
|
50
|
|
|
79
|
if (($ENV{COLUMNS}||'') =~ /\A\d+\z/ and $ENV{COLUMNS} > 0) { |
|
|
|
|
33
|
|
|
|
|
|
154
|
10
|
|
|
|
|
23
|
$arg->{columns} = $ENV{COLUMNS} - 1; |
|
155
|
|
|
|
|
|
|
} else { |
|
156
|
0
|
|
|
|
|
0
|
$arg->{columns} = 79; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
10
|
50
|
|
|
|
26
|
Carp::croak 'minimum columns is 44' if $arg->{columns} < 44; |
|
161
|
|
|
|
|
|
|
|
|
162
|
10
|
|
|
|
|
22
|
my ($hw, $aw) = _widths($arg->{columns}); |
|
163
|
|
|
|
|
|
|
|
|
164
|
10
|
100
|
|
|
|
36
|
if (ref $want) { |
|
165
|
1
|
|
|
|
|
4
|
$want = join q{}, map { chr hex } map { unpack "(a2)*", $_ } @$want; |
|
|
12
|
|
|
|
|
21
|
|
|
|
6
|
|
|
|
|
16
|
|
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
10
|
|
|
|
|
198
|
my $have_is_wide = grep { ord > 255 } split //, $have; |
|
|
1523
|
|
|
|
|
1842
|
|
|
169
|
10
|
|
|
|
|
173
|
my $want_is_wide = grep { ord > 255 } split //, $want; |
|
|
1553
|
|
|
|
|
1854
|
|
|
170
|
|
|
|
|
|
|
|
|
171
|
10
|
100
|
66
|
|
|
86
|
if ($have_is_wide or $want_is_wide) { |
|
172
|
1
|
|
|
|
|
7
|
$Test->ok(0, $comment); |
|
173
|
|
|
|
|
|
|
|
|
174
|
1
|
50
|
|
|
|
317
|
$Test->diag("value for 'have' contains wide bytes") if $have_is_wide; |
|
175
|
1
|
50
|
|
|
|
113
|
$Test->diag("value for 'want' contains wide bytes") if $want_is_wide; |
|
176
|
|
|
|
|
|
|
|
|
177
|
1
|
|
|
|
|
104
|
return; |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
9
|
100
|
|
|
|
20
|
if ($have eq $want) { |
|
181
|
2
|
|
|
|
|
15
|
return $Test->ok(1, $comment); |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
7
|
|
|
|
|
40
|
$Test->ok(0, $comment); |
|
185
|
|
|
|
|
|
|
|
|
186
|
7
|
|
|
|
|
2296
|
my $max_length = (sort map { length($_) } $have, $want)[1]; |
|
|
14
|
|
|
|
|
40
|
|
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
$Test->diag( |
|
189
|
|
|
|
|
|
|
sprintf "%-${hw}s %-${aw}s %-${hw}s %-${aw}s", |
|
190
|
7
|
|
|
|
|
27
|
map {; "$_ (hex)", "$_" } qw(have want) |
|
|
14
|
|
|
|
|
74
|
|
|
191
|
|
|
|
|
|
|
); |
|
192
|
|
|
|
|
|
|
|
|
193
|
7
|
|
|
|
|
853
|
my $seen_diffs = 0; |
|
194
|
7
|
|
|
|
|
22
|
CHUNK: for (my $pos = 0; $pos < $max_length; $pos += $aw) { |
|
195
|
80
|
100
|
100
|
|
|
167
|
if ($arg->{max_diffs} and $seen_diffs == $arg->{max_diffs}) { |
|
196
|
2
|
|
|
|
|
10
|
$Test->diag("..."); |
|
197
|
2
|
|
|
|
|
204
|
last CHUNK; |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
78
|
|
|
|
|
140
|
my $g_substr = substr($have, $pos, $aw); |
|
201
|
78
|
|
|
|
|
112
|
my $e_substr = substr($want, $pos, $aw); |
|
202
|
|
|
|
|
|
|
|
|
203
|
78
|
|
|
|
|
99
|
my $eq = $g_substr eq $e_substr; |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
my $g_hex = |
|
206
|
|
|
|
|
|
|
join q{}, |
|
207
|
78
|
|
|
|
|
145
|
map { sprintf '%02x', ord(substr($g_substr, $_, 1)) } |
|
|
891
|
|
|
|
|
1586
|
|
|
208
|
|
|
|
|
|
|
0 .. length($g_substr) - 1; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $e_hex = |
|
211
|
|
|
|
|
|
|
join q{}, |
|
212
|
78
|
|
|
|
|
172
|
map { sprintf '%02x', ord(substr($e_substr, $_, 1)) } |
|
|
907
|
|
|
|
|
1415
|
|
|
213
|
|
|
|
|
|
|
0 .. length($e_substr) - 1; |
|
214
|
|
|
|
|
|
|
|
|
215
|
78
|
|
|
|
|
188
|
for my $str ($g_substr, $e_substr) { |
|
216
|
156
|
|
|
|
|
220
|
for my $pos (0 .. length($str) - 1) { |
|
217
|
1798
|
|
|
|
|
2027
|
my $c = substr($str, $pos, 1); |
|
218
|
1798
|
100
|
100
|
|
|
3821
|
substr($str, $pos, 1, q{.}) if ord($c) < 0x20 or ord($c) > 0x7e; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
78
|
|
|
|
|
223
|
$_ = sprintf "%-${aw}s", $_ for ($g_substr, $e_substr); |
|
223
|
78
|
|
|
|
|
159
|
$_ .= q{-} x ($hw - length($_)) for ($g_hex, $e_hex); |
|
224
|
|
|
|
|
|
|
|
|
225
|
78
|
100
|
|
|
|
405
|
$Test->diag( |
|
226
|
|
|
|
|
|
|
"$g_hex $g_substr", |
|
227
|
|
|
|
|
|
|
($eq ? q{ = } : q{ ! }), |
|
228
|
|
|
|
|
|
|
"$e_hex $e_substr" |
|
229
|
|
|
|
|
|
|
); |
|
230
|
|
|
|
|
|
|
|
|
231
|
78
|
100
|
|
|
|
8522
|
$seen_diffs++ unless $eq; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
7
|
|
|
|
|
23
|
return; |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
#pod =head1 TODO |
|
238
|
|
|
|
|
|
|
#pod |
|
239
|
|
|
|
|
|
|
#pod =begin :list |
|
240
|
|
|
|
|
|
|
#pod |
|
241
|
|
|
|
|
|
|
#pod * optional position markers |
|
242
|
|
|
|
|
|
|
#pod |
|
243
|
|
|
|
|
|
|
#pod have (hex) have want (hex) want |
|
244
|
|
|
|
|
|
|
#pod 00 46726f6d206d6169 From mai = 46726f6d206d6169 From mai |
|
245
|
|
|
|
|
|
|
#pod 08 3130353239406c6f 10529@lo = 3130353239406c6f 10529@lo |
|
246
|
|
|
|
|
|
|
#pod 16 63616c686f737420 calhost = 63616c686f737420 calhost |
|
247
|
|
|
|
|
|
|
#pod 24 5765642044656320 Wed Dec = 5765642044656320 Wed Dec |
|
248
|
|
|
|
|
|
|
#pod 32 31382031323a3037 18 12:07 = 31382031323a3037 18 12:07 |
|
249
|
|
|
|
|
|
|
#pod 40 3a35352032303032 :55 2002 = 3a35352032303032 :55 2002 |
|
250
|
|
|
|
|
|
|
#pod 48 0a52656365697665 .Receive ! 0d0a526563656976 ..Receiv |
|
251
|
|
|
|
|
|
|
#pod |
|
252
|
|
|
|
|
|
|
#pod * investigate probably bugs with wide chars, multibyte strings |
|
253
|
|
|
|
|
|
|
#pod |
|
254
|
|
|
|
|
|
|
#pod I wrote this primarily for detecting CRLF problems, but it's also very useful |
|
255
|
|
|
|
|
|
|
#pod for dealing with encoded strings. |
|
256
|
|
|
|
|
|
|
#pod |
|
257
|
|
|
|
|
|
|
#pod =end :list |
|
258
|
|
|
|
|
|
|
#pod |
|
259
|
|
|
|
|
|
|
#pod =cut |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
1; |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
__END__ |