line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2008-2011 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Test::HexString; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
23423
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
78
|
|
11
|
2
|
|
|
2
|
|
16
|
use warnings; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
71
|
|
12
|
2
|
|
|
2
|
|
13
|
use base qw( Test::Builder::Module ); |
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
1952
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $CLASS = __PACKAGE__; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our @EXPORT = qw( |
17
|
|
|
|
|
|
|
is_hexstr |
18
|
|
|
|
|
|
|
); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $BYTES_PER_BLOCK = 16; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 NAME |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
C - test binary strings with hex dump diagnostics |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 SYNOPSIS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
use Test::More tests => 1; |
29
|
|
|
|
|
|
|
use Test::HexString; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $data = generate_some_output; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
is_hexstr( $data, "\x01\x02\x03\x04", 'Generated output' ); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 DESCRIPTION |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
This testing module provides a single function, C, which asserts |
38
|
|
|
|
|
|
|
that the given string matches what was expected. When the strings match (i.e. |
39
|
|
|
|
|
|
|
compare equal using the C operator), the behaviour is identical to the |
40
|
|
|
|
|
|
|
usual C function provided by C. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
When the strings are different, a hex dump is produced as diagnostic, rather |
43
|
|
|
|
|
|
|
than the string values being printed raw. This may be beneficial if the string |
44
|
|
|
|
|
|
|
contains largely binary data, such as may be produced by binary file or |
45
|
|
|
|
|
|
|
network protocol modules. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
To print the hex dump when it fails, each string is broken into 16 byte |
48
|
|
|
|
|
|
|
chunks. The first pair of chunks that fail to match are then printed, in both |
49
|
|
|
|
|
|
|
hexadecimal and character form, in a message in the following format: |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Failed test at -e line 1. |
52
|
|
|
|
|
|
|
# at bytes 0-0xf (0-15) |
53
|
|
|
|
|
|
|
# got: | 61 20 6c 6f 6e 67 20 73 74 72 69 6e 67 20 68 65 |a long string he| |
54
|
|
|
|
|
|
|
# exp: | 61 20 6c 6f 6e 67 20 53 74 72 69 6e 67 20 68 65 |a long String he| |
55
|
|
|
|
|
|
|
# Looks like you failed 1 test of 1. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Only bytes in the range C<0x20-0x7e> are printed as literal characters. Any |
58
|
|
|
|
|
|
|
other byte is rendered as C<.>: |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Failed test at -e line 1. |
61
|
|
|
|
|
|
|
# at bytes 0-0xf (0-15) |
62
|
|
|
|
|
|
|
# got: | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................| |
63
|
|
|
|
|
|
|
# exp: | 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 |................| |
64
|
|
|
|
|
|
|
# Looks like you failed 1 test of 1. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Only the first differing line is printed; because otherwise it may result in a |
67
|
|
|
|
|
|
|
long output because of misaligned bytes. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
If STDOUT is a terminal, then different bytes are printed in bold for |
70
|
|
|
|
|
|
|
visibility. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _bold |
75
|
|
|
|
|
|
|
{ |
76
|
104
|
|
|
104
|
|
131
|
my ( $str, $bold ) = @_; |
77
|
104
|
50
|
|
|
|
748
|
return $str unless -t STDOUT; |
78
|
0
|
0
|
|
|
|
0
|
return $bold ? "\e[1m$str\e[m" : $str; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub _hexline |
82
|
|
|
|
|
|
|
{ |
83
|
6
|
|
|
6
|
|
13
|
my ( $bytes, $boldmap ) = @_; |
84
|
|
|
|
|
|
|
|
85
|
6
|
|
|
|
|
46
|
my @b = split( m//, $bytes ); |
86
|
|
|
|
|
|
|
|
87
|
6
|
|
|
|
|
10
|
my $ret = "| "; |
88
|
6
|
|
|
|
|
34
|
$ret .= _bold(sprintf( "%02x ", ord $b[$_] ), $boldmap->[$_] ) for 0 .. $#b; |
89
|
6
|
|
|
|
|
17
|
$ret .= ".. " x ( $BYTES_PER_BLOCK - @b ); |
90
|
6
|
|
|
|
|
9
|
$ret .= "|"; |
91
|
6
|
100
|
|
|
|
31
|
$ret .= _bold($b[$_] =~ /[\x20-\x7e]/ ? $b[$_] : ".", $boldmap->[$_] ) for 0 .. $#b; |
92
|
6
|
|
|
|
|
14
|
$ret .= " " x ( $BYTES_PER_BLOCK - @b ); |
93
|
6
|
|
|
|
|
7
|
$ret .= "|"; |
94
|
|
|
|
|
|
|
|
95
|
6
|
|
|
|
|
34
|
return $ret; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head1 FUNCTIONS |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head2 is_hexstr( $got, $expect, $name ) |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Test that the string $got is what was expected by $expect. If the strings are |
105
|
|
|
|
|
|
|
not equal, a hex dump is printed showing the region where they first start to |
106
|
|
|
|
|
|
|
differ. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub is_hexstr($$;$) |
111
|
|
|
|
|
|
|
{ |
112
|
5
|
|
|
5
|
1
|
3222
|
my ( $got, $expected, $name ) = @_; |
113
|
|
|
|
|
|
|
|
114
|
5
|
|
|
|
|
34
|
my $tb = $CLASS->builder; |
115
|
|
|
|
|
|
|
|
116
|
5
|
100
|
|
|
|
45
|
if( ref $got ) { |
117
|
1
|
|
|
|
|
4
|
my $ok = $tb->ok( 0, $name ); |
118
|
1
|
|
|
|
|
582
|
$tb->diag( " expected a plain string, was given a reference to " . ref($got) ); |
119
|
1
|
|
|
|
|
81
|
return $ok; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
4
|
|
|
|
|
15
|
my $ok = $tb->ok( $got eq $expected, $name ); |
123
|
|
|
|
|
|
|
|
124
|
4
|
100
|
|
|
|
1859
|
unless( $ok ) { |
125
|
|
|
|
|
|
|
# Try to find where they differ |
126
|
3
|
|
|
|
|
14
|
for( my $offs = 0; $offs < length $got; $offs += $BYTES_PER_BLOCK ) { |
127
|
65
|
|
|
|
|
76
|
my $g = substr( $got, $offs, $BYTES_PER_BLOCK ); |
128
|
65
|
|
|
|
|
72
|
my $e = substr( $expected, $offs, $BYTES_PER_BLOCK ); |
129
|
65
|
100
|
|
|
|
195
|
next if $g eq $e; |
130
|
|
|
|
|
|
|
|
131
|
3
|
100
|
66
|
|
|
9
|
my @bold = map { $_ < length $g and $_ < length $e and substr( $g, $_, 1 ) ne substr( $e, $_, 1 ) } |
|
48
|
|
|
|
|
202
|
|
132
|
|
|
|
|
|
|
( 0 .. $BYTES_PER_BLOCK-1 ); |
133
|
|
|
|
|
|
|
|
134
|
3
|
|
|
|
|
29
|
$tb->diag( sprintf( " at bytes %#x-%#x (%d-%d)\n", |
135
|
|
|
|
|
|
|
$offs, $offs+$BYTES_PER_BLOCK-1, $offs, $offs+$BYTES_PER_BLOCK-1 ) . |
136
|
|
|
|
|
|
|
" got: " . _hexline( $g, \@bold ) . "\n" . |
137
|
|
|
|
|
|
|
" exp: " . _hexline( $e, \@bold ) |
138
|
|
|
|
|
|
|
); |
139
|
|
|
|
|
|
|
|
140
|
3
|
|
|
|
|
264
|
last; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
4
|
|
|
|
|
10
|
return $ok; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head1 AUTHOR |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Paul Evans |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=cut |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
0x55AA; |