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__ |