line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Test::Usage; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
46858
|
use 5.008; |
|
4
|
|
|
|
|
12
|
|
4
|
|
|
|
|
|
|
our $VERSION = '0.07_50'; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
Test::Usage - A different approach to testing: selective, quieter, colorful. |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSIS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
package Foo_T; |
13
|
|
|
|
|
|
|
use strict; |
14
|
|
|
|
|
|
|
use warnings; |
15
|
|
|
|
|
|
|
use Test::Usage; |
16
|
|
|
|
|
|
|
use Foo; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
example('e1', sub { ... ok(...); ... die "Uh oh"; ... }); |
19
|
|
|
|
|
|
|
example('a1', sub { ... ok(...); ... }); |
20
|
|
|
|
|
|
|
example('a2', sub { |
21
|
|
|
|
|
|
|
my $f = Foo->new(); |
22
|
|
|
|
|
|
|
my $got_foo = $f->foo(); |
23
|
|
|
|
|
|
|
my $exp_foo = 'FOO'; |
24
|
|
|
|
|
|
|
ok( |
25
|
|
|
|
|
|
|
$got_foo eq $exp_foo, |
26
|
|
|
|
|
|
|
"Expecting foo() to return '$exp_foo'.", |
27
|
|
|
|
|
|
|
"But got '$got_foo'." |
28
|
|
|
|
|
|
|
); |
29
|
|
|
|
|
|
|
}); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Then, from the command line: |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Run all examples found in the test module, that is, e1, a1, |
34
|
|
|
|
|
|
|
# and a2. |
35
|
|
|
|
|
|
|
perl -MFoo_T -e test |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Run all examples whose label matches glob 'a*': a1, a2. |
38
|
|
|
|
|
|
|
perl -MFoo_T -e 'test(a => "a*")' |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# Run example 'a2', reporting successes also, but without color. |
41
|
|
|
|
|
|
|
perl -MFoo_T -e 'test(a => "a2", v => 2, c => 0)' |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Run and summarize all examples in all "*_T.pm" files found |
44
|
|
|
|
|
|
|
# under current directory. |
45
|
|
|
|
|
|
|
perl -MTest::Usage -e files |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The module exports some of its methods to the calling package and some |
48
|
|
|
|
|
|
|
to main, to make them easier to use, usually from the shell. When the |
49
|
|
|
|
|
|
|
developer wishes to run a test, he invokes it as shown in the synopsis |
50
|
|
|
|
|
|
|
(perhaps with a coating of shell syntaxic sugar). |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Bleah. Under cygwin's screen, Win32Console fails, so do this before |
53
|
|
|
|
|
|
|
invoking screen: |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
export USE_ANSI_COLOR=1 |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=cut |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
60
|
4
|
|
|
4
|
|
14
|
use strict; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
59
|
|
61
|
4
|
|
|
4
|
|
11
|
use warnings; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
80
|
|
62
|
4
|
|
|
4
|
|
11
|
use Carp; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
228
|
|
63
|
|
|
|
|
|
|
$Carp::MaxArgLen = 0; # So error messages don't get truncated. |
64
|
4
|
|
|
4
|
|
2424
|
use File::Temp qw(tempfile); |
|
4
|
|
|
|
|
72233
|
|
|
4
|
|
|
|
|
230
|
|
65
|
4
|
|
|
4
|
|
26
|
use File::Find; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
188
|
|
66
|
4
|
|
|
4
|
|
1924
|
use File::Slurp; |
|
4
|
|
|
|
|
40428
|
|
|
4
|
|
|
|
|
232
|
|
67
|
4
|
|
|
4
|
|
26
|
use File::Spec; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
71
|
|
68
|
4
|
|
|
4
|
|
1942
|
use IO::File; |
|
4
|
|
|
|
|
2887
|
|
|
4
|
|
|
|
|
961
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Main accumulator. |
71
|
|
|
|
|
|
|
my $t; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
74
|
|
|
|
|
|
|
# Color management. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
my $gColor = {}; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Explicit initializations, for the maintainer's benefit. |
79
|
|
|
|
|
|
|
$gColor->{id} = undef; |
80
|
|
|
|
|
|
|
$gColor->{palette} = undef; |
81
|
|
|
|
|
|
|
# Will be set only for Win32::Console usage. |
82
|
|
|
|
|
|
|
$gColor->{out} = undef; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
if (! defined($ENV{USE_ANSI_COLOR}) && $^O eq 'MSWin32') { |
85
|
|
|
|
|
|
|
eval "use Win32::Console"; |
86
|
|
|
|
|
|
|
if ($@ eq '') { |
87
|
|
|
|
|
|
|
$gColor->{out} = Win32::Console->new(STD_OUTPUT_HANDLE()) |
88
|
|
|
|
|
|
|
or die "Couldn't get new Win32::Console instance."; |
89
|
|
|
|
|
|
|
$gColor->{palette} = { |
90
|
|
|
|
|
|
|
cBoldWhite => $Win32::Console::FG_WHITE | $Win32::Console::BG_BLACK, |
91
|
|
|
|
|
|
|
cBoldMagenta => $Win32::Console::FG_LIGHTMAGENTA | $Win32::Console::BG_BLACK, |
92
|
|
|
|
|
|
|
cBoldCyan => $Win32::Console::FG_LIGHTCYAN | $Win32::Console::BG_BLACK, |
93
|
|
|
|
|
|
|
cYellow => $Win32::Console::FG_YELLOW | $Win32::Console::BG_BLACK, |
94
|
|
|
|
|
|
|
cBoldGreen => $Win32::Console::FG_LIGHTGREEN | $Win32::Console::BG_BLACK, |
95
|
|
|
|
|
|
|
cBoldRed => $Win32::Console::FG_LIGHTRED | $Win32::Console::BG_BLACK, |
96
|
|
|
|
|
|
|
cWhite => $Win32::Console::FG_WHITE | $Win32::Console::BG_BLACK, |
97
|
|
|
|
|
|
|
cBlack => $Win32::Console::FG_BLACK | $Win32::Console::BG_BLACK, |
98
|
|
|
|
|
|
|
}; |
99
|
|
|
|
|
|
|
$gColor->{id} = 'Win32Console'; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
else { |
103
|
4
|
|
|
4
|
|
2205
|
eval "use Term::ANSIColor ()"; |
|
4
|
|
|
|
|
16836
|
|
|
4
|
|
|
|
|
44
|
|
104
|
|
|
|
|
|
|
if ($@ eq '') { |
105
|
|
|
|
|
|
|
$gColor->{palette} = { |
106
|
|
|
|
|
|
|
cBoldWhite => 'bold white', |
107
|
|
|
|
|
|
|
cBoldMagenta => 'bold magenta', |
108
|
|
|
|
|
|
|
cBoldCyan => 'bold cyan', |
109
|
|
|
|
|
|
|
cYellow => 'yellow', |
110
|
|
|
|
|
|
|
cBoldGreen => 'bold green', |
111
|
|
|
|
|
|
|
cBoldRed => 'bold red', |
112
|
|
|
|
|
|
|
cWhite => 'white', |
113
|
|
|
|
|
|
|
cBlack => 'black', |
114
|
|
|
|
|
|
|
}; |
115
|
|
|
|
|
|
|
$gColor->{id} = 'ANSI'; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
120
|
|
|
|
|
|
|
# Verbosity level constants. |
121
|
|
|
|
|
|
|
|
122
|
4
|
|
|
4
|
|
47
|
use constant REPORT_NOTHING => 0; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
278
|
|
123
|
4
|
|
|
4
|
|
13
|
use constant REPORT_FAILURES => 1; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
147
|
|
124
|
4
|
|
|
4
|
|
14
|
use constant REPORT_ALL => 2; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
8968
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
127
|
|
|
|
|
|
|
# Default options. Change if necessary. |
128
|
|
|
|
|
|
|
# |
129
|
|
|
|
|
|
|
# Implementation note: all leaf keys of %_D must be different, since |
130
|
|
|
|
|
|
|
# they become keys to $t->{options}. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
my %_D; |
133
|
|
|
|
|
|
|
# Can be set by test(). |
134
|
|
|
|
|
|
|
$_D{t} = { |
135
|
|
|
|
|
|
|
# Accept tests whose label matches this glob. |
136
|
|
|
|
|
|
|
a => '*', |
137
|
|
|
|
|
|
|
# Exclude tests whose label matches this glob. |
138
|
|
|
|
|
|
|
e => '__*', |
139
|
|
|
|
|
|
|
# Print a summary line if true. |
140
|
|
|
|
|
|
|
s => 1, |
141
|
|
|
|
|
|
|
# Verbosity level. |
142
|
|
|
|
|
|
|
v => REPORT_FAILURES, |
143
|
|
|
|
|
|
|
# Fail tests systematically if true. |
144
|
|
|
|
|
|
|
f => 0, |
145
|
|
|
|
|
|
|
}; |
146
|
|
|
|
|
|
|
# Can be set by files(). |
147
|
|
|
|
|
|
|
$_D{f} = { |
148
|
|
|
|
|
|
|
# Directory in which to look for files. |
149
|
|
|
|
|
|
|
d => '.', |
150
|
|
|
|
|
|
|
# Test files whose name matches this glob. |
151
|
|
|
|
|
|
|
g => '*_T.pm', |
152
|
|
|
|
|
|
|
# Look for files recursively through dir if true. |
153
|
|
|
|
|
|
|
r => 1, |
154
|
|
|
|
|
|
|
# Add to Perl %INC path. |
155
|
|
|
|
|
|
|
i => '', |
156
|
|
|
|
|
|
|
# Option values to pass to test() for each file. |
157
|
|
|
|
|
|
|
t => {}, |
158
|
|
|
|
|
|
|
}; |
159
|
|
|
|
|
|
|
# Miscellaneous. Can be set by test() or files(). |
160
|
|
|
|
|
|
|
$_D{m} = { |
161
|
|
|
|
|
|
|
# Use color if possible. |
162
|
|
|
|
|
|
|
c => 1, |
163
|
|
|
|
|
|
|
}; |
164
|
|
|
|
|
|
|
# Color map. |
165
|
|
|
|
|
|
|
$_D{c} = { |
166
|
|
|
|
|
|
|
what => 'cBoldWhite', |
167
|
|
|
|
|
|
|
died => 'cBoldMagenta', |
168
|
|
|
|
|
|
|
warned => 'cBoldCyan', |
169
|
|
|
|
|
|
|
summary => 'cYellow', |
170
|
|
|
|
|
|
|
success => 'cBoldGreen', |
171
|
|
|
|
|
|
|
failure => 'cBoldRed', |
172
|
|
|
|
|
|
|
diag => 'cBoldRed', |
173
|
|
|
|
|
|
|
default => 'cWhite', |
174
|
|
|
|
|
|
|
}; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head1 METHODS AND FUNCTIONS |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
All methods apply to a single instance of Test::Usage, named $t, |
181
|
|
|
|
|
|
|
initialized by import(). |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
The module defines the following methods and functions. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=cut |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 import ($pkg) |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Sets $t to an empty hash ref, blessed in Test::Usage. |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Resets $t's counters to 0: |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Number of 'ok' that failed. |
196
|
|
|
|
|
|
|
Number of 'ok' that succeeded. |
197
|
|
|
|
|
|
|
Number of examples that died. |
198
|
|
|
|
|
|
|
Number of examples that had warnings. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Sets $t's default label to '-'. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
Resets $t's options to default values. Here are the as-shipped |
203
|
|
|
|
|
|
|
values: |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
For the test() method: |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
a => '*' # Accept tests whose label matches this glob. |
208
|
|
|
|
|
|
|
e => '__*' # Exclude tests whose label matches this glob. |
209
|
|
|
|
|
|
|
s => 1 # Print a summary line if true. |
210
|
|
|
|
|
|
|
v => REPORT_FAILURES # Verbosity level. |
211
|
|
|
|
|
|
|
f => 0 # Fail tests systematically if true. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
For the files() method: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
d => '.' # Directory in which to look for files. |
216
|
|
|
|
|
|
|
g => '*_T.pm' # Test files whose name matches this glob. |
217
|
|
|
|
|
|
|
r => 1 # Look for files recursively through dir if true. |
218
|
|
|
|
|
|
|
i => '' # Add to Perl %INC path. |
219
|
|
|
|
|
|
|
t => {} # Option values to pass to test() for each file. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
For both test() and files(): |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
c => 1 # Use color if possible. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Exports these methods to the calling package: |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
t |
228
|
|
|
|
|
|
|
example |
229
|
|
|
|
|
|
|
ok |
230
|
|
|
|
|
|
|
ok_labeled |
231
|
|
|
|
|
|
|
diag |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
Exports these methods to main: |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
t |
236
|
|
|
|
|
|
|
test |
237
|
|
|
|
|
|
|
files |
238
|
|
|
|
|
|
|
labels |
239
|
|
|
|
|
|
|
plabels |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=cut |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub import { |
244
|
4
|
|
|
4
|
|
31
|
$t = bless {}, __PACKAGE__; |
245
|
4
|
|
|
|
|
7
|
my $caller = caller; |
246
|
4
|
|
|
|
|
27
|
$t->{name} = $caller; |
247
|
|
|
|
|
|
|
# example() will push elements like [$label, $sub_ref] |
248
|
|
|
|
|
|
|
# onto this array ref. |
249
|
4
|
|
|
|
|
6
|
$t->{examples} = []; |
250
|
|
|
|
|
|
|
# Default label. |
251
|
4
|
|
|
|
|
6
|
$t->{label} = '-'; |
252
|
|
|
|
|
|
|
# Private counters. |
253
|
4
|
|
|
|
|
5
|
$t->{nb_succ} = 0; |
254
|
4
|
|
|
|
|
4
|
$t->{nb_fail} = 0; |
255
|
|
|
|
|
|
|
# Incremented respectively when a die or a warning occur within an |
256
|
|
|
|
|
|
|
# example(). |
257
|
4
|
|
|
|
|
5
|
$t->{died} = 0; |
258
|
4
|
|
|
|
|
4
|
$t->{warned} = 0; |
259
|
|
|
|
|
|
|
|
260
|
4
|
|
|
|
|
8
|
reset_options(); |
261
|
|
|
|
|
|
|
|
262
|
4
|
|
|
11
|
|
478
|
eval << "EOT"; |
|
11
|
|
|
|
|
3567
|
|
|
64
|
|
|
|
|
18926
|
|
|
0
|
|
|
|
|
0
|
|
|
17
|
|
|
|
|
308
|
|
|
76
|
|
|
|
|
2808
|
|
263
|
|
|
|
|
|
|
package $caller; |
264
|
|
|
|
|
|
|
*t = sub { \$t }; |
265
|
|
|
|
|
|
|
*example = sub { \$t->example(\@_) }; |
266
|
|
|
|
|
|
|
*ok = sub { \$t->ok(\@_) }; |
267
|
|
|
|
|
|
|
*ok_labeled = sub { \$t->ok_labeled(\@_) }; |
268
|
|
|
|
|
|
|
*diag = sub { \$t->diag(\@_) }; |
269
|
|
|
|
|
|
|
EOT |
270
|
4
|
100
|
|
11
|
|
145
|
eval "*main::t = sub { \$t }" unless $caller eq 'main'; |
|
11
|
|
|
|
|
14000
|
|
271
|
4
|
|
|
0
|
|
364
|
eval << "EOT"; |
|
0
|
|
|
|
|
0
|
|
|
12
|
|
|
|
|
1372
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
272
|
|
|
|
|
|
|
package main; |
273
|
|
|
|
|
|
|
*test = sub { \$t->test(\@_) }; |
274
|
|
|
|
|
|
|
*files = sub { \$t->files(\@_) }; |
275
|
|
|
|
|
|
|
*labels = sub { \$t->labels(\@_) }; |
276
|
|
|
|
|
|
|
*plabels = sub { \$t->plabels(\@_) }; |
277
|
|
|
|
|
|
|
EOT |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=head2 $pkg::t (), ::t () |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Both return $t, effectively giving access to all Test::Usage methods. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head2 $pkg::example ($label, $sub_ref) |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Add a test example labeled $label and implemented by $sub_ref to the |
293
|
|
|
|
|
|
|
tests that can be run by $t->test(). |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
$label is an arbitrary string that is used to identify the example. |
296
|
|
|
|
|
|
|
The label will be displayed when reporting tests results. Labels can |
297
|
|
|
|
|
|
|
be chosen to make it easy to run selected subsets; for example, you |
298
|
|
|
|
|
|
|
may want to label a bunch of examples that you usually run together |
299
|
|
|
|
|
|
|
with a common prefix. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
The $sub_ref is a reference to the subroutine implementing the test. |
302
|
|
|
|
|
|
|
It often calls a number of ok(), wrapped in setup/tear-down |
303
|
|
|
|
|
|
|
scaffolding to express the intended usage. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
Here's a full example: |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
example('t1', sub { |
308
|
|
|
|
|
|
|
my $f = Foo->new(); |
309
|
|
|
|
|
|
|
my $exp = 1; |
310
|
|
|
|
|
|
|
my $got = $f->get_val(); |
311
|
|
|
|
|
|
|
ok( |
312
|
|
|
|
|
|
|
$got == $exp, |
313
|
|
|
|
|
|
|
"Expected get_val() to return $exp for a new Foo object.", |
314
|
|
|
|
|
|
|
"But got $got.", |
315
|
|
|
|
|
|
|
); |
316
|
|
|
|
|
|
|
}); |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub example { |
321
|
11
|
|
|
11
|
1
|
15
|
my ($self, $label, $sub_ref) = @_; |
322
|
|
|
|
|
|
|
# We store test examples in an array to guarantee that they will |
323
|
|
|
|
|
|
|
# be executed in the order they appear in the test file. |
324
|
11
|
|
|
|
|
6
|
push @{$self->{examples}}, |
|
11
|
|
|
|
|
35
|
|
325
|
|
|
|
|
|
|
Test::Usage::Example->new($label, $sub_ref); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head2 $pkg::ok ($bool, $exp_msg, $got_msg) |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
$bool is an expression that will be evaluated as true or false, and |
333
|
|
|
|
|
|
|
thus determine the return value of the method. Also, if $bool is |
334
|
|
|
|
|
|
|
true, $t will increment the number of successful tests it has seen, |
335
|
|
|
|
|
|
|
else the number of failed tests. |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Note that $bool will be evaluated in list context; for example, if you |
338
|
|
|
|
|
|
|
want to use a bind operator here, make sure you wrap it with 'scalar'. |
339
|
|
|
|
|
|
|
For example: |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
ok(scalar($x =~ /abc/), |
342
|
|
|
|
|
|
|
"Expected \$x to match /abc/.", |
343
|
|
|
|
|
|
|
"But its value was '$x'." |
344
|
|
|
|
|
|
|
); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
In that example, if 'scalar' is not used, the bind operator is |
347
|
|
|
|
|
|
|
evaluated in list context, and if there is no match, an empty list is |
348
|
|
|
|
|
|
|
returned, which results in ok() receiving only the last two arguments. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
If the test() flags are such that the result of the ok() is to be |
351
|
|
|
|
|
|
|
printed, something like one of the following will be printed: |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
ok a1 |
354
|
|
|
|
|
|
|
# Expected $x to match /abc/. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
not ok a1 |
357
|
|
|
|
|
|
|
# Expected $x to match /abc/. |
358
|
|
|
|
|
|
|
# But its value was 'def'. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=cut |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
sub ok { |
363
|
84
|
|
|
84
|
1
|
92
|
my ($self, $bool, $exp_msg, $got_msg) = @_; |
364
|
84
|
|
|
|
|
114
|
$self->_confirm($self->{label}, $bool, $exp_msg, $got_msg); |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 $pkg::ok_labeled ($sub_label, $bool, $exp_msg, $got_msg) |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Same as ok(), except that ".$sub_label" is appended to the label in |
372
|
|
|
|
|
|
|
the printed output. This is useful for examples containing many ok() |
373
|
|
|
|
|
|
|
whose labels we want to distinguish. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=cut |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub ok_labeled { |
378
|
1
|
|
|
1
|
1
|
1
|
my ($self, $sub_label, $bool, $exp_msg, $got_msg) = @_; |
379
|
|
|
|
|
|
|
$self->_confirm( |
380
|
1
|
|
|
|
|
4
|
$self->{label} . '.' . $sub_label, |
381
|
|
|
|
|
|
|
$bool, $exp_msg, $got_msg |
382
|
|
|
|
|
|
|
); |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=begin maintenance $self->_confirm ($label, $bool, $exp_msg, $got_msg) |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
Returns true if argument $bool is true, false otherwise. Also returns |
390
|
|
|
|
|
|
|
false if the 'fail' option is set. |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=end maintenance |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=cut |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub _confirm { |
397
|
85
|
|
|
85
|
|
91
|
my ($self, $label, $bool, $exp_msg, $got_msg) = @_; |
398
|
85
|
100
|
|
|
|
106
|
$bool = 0 if $self->options()->{f}; |
399
|
85
|
100
|
66
|
|
|
209
|
($bool && ++$self->{nb_succ}) || ++$self->{nb_fail}; |
400
|
85
|
|
|
|
|
86
|
my $verbosity = $self->options()->{v}; |
401
|
85
|
100
|
|
|
|
111
|
$exp_msg = '' unless defined $exp_msg; |
402
|
85
|
100
|
|
|
|
104
|
$got_msg = '' unless defined $got_msg; |
403
|
85
|
100
|
100
|
|
|
256
|
if ($verbosity == REPORT_ALL || ($verbosity == REPORT_FAILURES && ! $bool)) { |
|
|
|
66
|
|
|
|
|
404
|
43
|
100
|
|
|
|
76
|
$self->printk('what', $bool ? 'ok ' : 'not ok '); |
405
|
43
|
100
|
|
|
|
169
|
my $printk_type = $bool ? 'success' : 'failure'; |
406
|
43
|
|
|
|
|
66
|
$self->printk($printk_type, "$label\n"); |
407
|
43
|
100
|
|
|
|
162
|
if ($exp_msg ne '') { |
408
|
38
|
|
|
|
|
94
|
$exp_msg =~ s/^/ # /gm; |
409
|
38
|
|
|
|
|
123
|
$exp_msg =~ s/\n*$/\n/; |
410
|
38
|
|
|
|
|
49
|
$self->printk($printk_type, $exp_msg); |
411
|
|
|
|
|
|
|
} |
412
|
43
|
100
|
100
|
|
|
209
|
if (! $bool && $got_msg ne '') { |
413
|
27
|
|
|
|
|
50
|
$got_msg =~ s/^/ # /gm; |
414
|
27
|
|
|
|
|
74
|
$got_msg =~ s/\n*$/\n/; |
415
|
27
|
|
|
|
|
33
|
$self->printk($printk_type, $got_msg); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
} |
418
|
85
|
|
|
|
|
376
|
return $bool; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 $pkg::diag (@msgs) |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Prefixes each line of each string in @msgs with ' # ' and displays |
426
|
|
|
|
|
|
|
them using the 'diag' color tag. Returns true (contrary to |
427
|
|
|
|
|
|
|
Test::Builder, Test::More, et al.). |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=cut |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub diag { |
432
|
0
|
|
|
0
|
1
|
0
|
my($self, @msgs) = @_; |
433
|
0
|
0
|
0
|
|
|
0
|
return unless @msgs && $self->options()->{v} > 0; |
434
|
|
|
|
|
|
|
# Prefix each line to make it a comment, to avoid interference |
435
|
|
|
|
|
|
|
# when used with Test::Harness. |
436
|
0
|
|
|
|
|
0
|
foreach (@msgs) { |
437
|
0
|
0
|
|
|
|
0
|
$_ = 'undef' unless defined; |
438
|
0
|
|
|
|
|
0
|
s/^/ # /gm; |
439
|
|
|
|
|
|
|
} |
440
|
0
|
0
|
|
|
|
0
|
push @msgs, "\n" unless $msgs[-1] =~ /\n\Z/; |
441
|
0
|
|
|
|
|
0
|
$self->printk('diag', "@msgs"); |
442
|
0
|
|
|
|
|
0
|
return 1; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head2 ::labels () |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
Returns a ref to an array holding the labels of all the examples, in |
450
|
|
|
|
|
|
|
the order they were defined. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=cut |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub labels { |
455
|
1
|
|
|
1
|
0
|
2
|
[map {$_->{label}} @{$_[0]->{examples}}]; |
|
2
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3
|
|
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=head2 ::plabels () |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Prints space separated known labels to STDOUT. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=cut |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub plabels { |
467
|
0
|
|
|
0
|
0
|
0
|
print STDOUT join ' ', @{::labels()}; |
|
0
|
|
|
|
|
0
|
|
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head2 ::test (%options) |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Clears counters and runs all the examples in the module, subject to |
475
|
|
|
|
|
|
|
the constraints indicated by %options. If %options is undefined or if |
476
|
|
|
|
|
|
|
some of its keys are missing, default values apply. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Returns a list containing the following values: |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Name of the module being tested. |
481
|
|
|
|
|
|
|
Number of seconds it took to run the examples. |
482
|
|
|
|
|
|
|
Number of 'ok' that succeeded. |
483
|
|
|
|
|
|
|
Number of 'ok' that failed. |
484
|
|
|
|
|
|
|
Number of examples that died. |
485
|
|
|
|
|
|
|
Number of examples that had warnings. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Here is the meaning and default value of the elements of %options: |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=over 4 |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=item a => '*' # Accept. |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
The value is a glob. Tests whose label matches this glob will be run. |
494
|
|
|
|
|
|
|
All tests are run when the value is the default. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=item e => '__*' # Exclude. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
The value is a glob. Tests whose label matches this glob will not be |
499
|
|
|
|
|
|
|
run. I use this when I want to keep a test in the test module, but I |
500
|
|
|
|
|
|
|
don't want to run it for some reason. When using the default value, |
501
|
|
|
|
|
|
|
prepending the string '__' to a test label will effectively |
502
|
|
|
|
|
|
|
disactivate it. When you are ready to run those tests, remove the '__' |
503
|
|
|
|
|
|
|
prefix from the label, or pass the 'e => ""' argument. |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=item v => 1 # Verbosity. |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
Determines the verbosity of the testing mechanism: |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
0: Display no individual results. |
510
|
|
|
|
|
|
|
1: Display individual results for failing tests only. |
511
|
|
|
|
|
|
|
2: Display individual results for all tests. |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=item s => 1 # Summary. |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
If true, two lines like the following will wrap the test output: |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# module_name |
518
|
|
|
|
|
|
|
... |
519
|
|
|
|
|
|
|
# +3 -1 -d +w (00h:00m:02s) module_name |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
That means that of the ok*() calls that were made, 3 succeeded and 1 |
522
|
|
|
|
|
|
|
failed, that no dies but some warnings occurred, and it took about 2 |
523
|
|
|
|
|
|
|
seconds to run. |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=item f => 0 # Fail. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
If true, any ok*() invoked will act as though it failed. When combined |
528
|
|
|
|
|
|
|
with a verbosity of 1 or 2, (to display failures), you will see all |
529
|
|
|
|
|
|
|
the actual messages that would get printed when failures occur. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=back |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=cut |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
{ |
536
|
|
|
|
|
|
|
my $tee_hdl; |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub _tee_to { |
539
|
0
|
|
|
0
|
|
0
|
my ($self, $file_name) = @_; |
540
|
0
|
0
|
|
|
|
0
|
$tee_hdl = IO::File->new($file_name, O_WRONLY|O_APPEND) |
541
|
|
|
|
|
|
|
or die "Couldn't write to '$file_name'."; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub test { |
545
|
15
|
|
|
15
|
1
|
30
|
my ($self, %options) = @_; |
546
|
15
|
|
|
|
|
21
|
$self->{nb_succ} = 0; |
547
|
15
|
|
|
|
|
13
|
$self->{nb_fail} = 0; |
548
|
15
|
|
|
|
|
18
|
$self->{died} = 0; |
549
|
15
|
|
|
|
|
11
|
$self->{warned} = 0; |
550
|
|
|
|
|
|
|
# Run examples matching this glob. |
551
|
|
|
|
|
|
|
my $accept = glob_to_regex( |
552
|
|
|
|
|
|
|
defined($options{a}) |
553
|
|
|
|
|
|
|
? $options{a} |
554
|
|
|
|
|
|
|
: $self->{options}{a} |
555
|
15
|
100
|
|
|
|
43
|
); |
556
|
|
|
|
|
|
|
# Don't run examples matching this glob. |
557
|
|
|
|
|
|
|
my $exclude = glob_to_regex( |
558
|
|
|
|
|
|
|
defined($options{e}) |
559
|
|
|
|
|
|
|
? $options{e} |
560
|
|
|
|
|
|
|
: $self->{options}{e} |
561
|
15
|
100
|
|
|
|
36
|
); |
562
|
15
|
50
|
|
|
|
33
|
$self->{options}{c} = $options{c} if defined $options{c}; |
563
|
15
|
100
|
|
|
|
32
|
$self->{options}{v} = _adjust_verbosity($options{v}) if defined $options{v}; |
564
|
15
|
100
|
|
|
|
26
|
$self->{options}{f} = $options{f} if defined $options{f}; |
565
|
15
|
|
|
|
|
24
|
my $start_time = time; |
566
|
|
|
|
|
|
|
# Run the examples. |
567
|
15
|
|
|
|
|
41
|
$self->printk('summary', '# ' . $self->{name} . "\n"); |
568
|
15
|
|
|
|
|
63
|
for my $example (@{$self->{examples}}) { |
|
15
|
|
|
|
|
27
|
|
569
|
107
|
|
|
|
|
88
|
my $label = $example->{label}; |
570
|
107
|
|
|
|
|
66
|
my $sub_ref = $example->{sub_ref}; |
571
|
107
|
100
|
|
|
|
304
|
next unless $label =~ /$accept/; |
572
|
56
|
100
|
|
|
|
111
|
next if $label =~ /$exclude/; |
573
|
48
|
|
|
|
|
37
|
$self->{label} = $label; |
574
|
48
|
|
|
|
|
36
|
my $warnings = ''; |
575
|
48
|
|
|
|
|
38
|
eval { |
576
|
|
|
|
|
|
|
local $SIG{__DIE__} = sub { |
577
|
1
|
|
|
1
|
|
215
|
Carp::confess(); |
578
|
48
|
|
|
|
|
164
|
}; |
579
|
|
|
|
|
|
|
local $SIG{__WARN__} = sub { |
580
|
1
|
|
|
1
|
|
9
|
$DB::single = 1; |
581
|
1
|
|
|
|
|
97
|
$warnings .= Carp::longmess(@_) . "\n"; |
582
|
48
|
|
|
|
|
135
|
}; |
583
|
48
|
|
|
|
|
70
|
$sub_ref->($self); |
584
|
|
|
|
|
|
|
}; |
585
|
48
|
100
|
|
|
|
265
|
if ($warnings) { |
586
|
1
|
|
|
|
|
2
|
$self->printk('keyword', 'WARNED '); |
587
|
1
|
|
|
|
|
6
|
$self->printk('warned', $warnings); |
588
|
1
|
|
|
|
|
4
|
++$self->{warned}; |
589
|
|
|
|
|
|
|
} |
590
|
48
|
100
|
|
|
|
74
|
if ($@) { |
591
|
1
|
|
|
|
|
4
|
$self->printk('keyword', 'DIED '); |
592
|
1
|
|
|
|
|
6
|
$self->printk('died', $@); |
593
|
1
|
|
|
|
|
4
|
++$self->{died}; |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
} |
596
|
15
|
|
|
|
|
18
|
my $time_took = time - $start_time; |
597
|
|
|
|
|
|
|
my $summary = join(' ', |
598
|
|
|
|
|
|
|
$self->sprintk('summary', ' #'), |
599
|
|
|
|
|
|
|
$self->sprintk('success', '+' . $self->{nb_succ}), |
600
|
|
|
|
|
|
|
$self->sprintk('failure', '-' . $self->{nb_fail}), |
601
|
|
|
|
|
|
|
$self->sprintk('died', ($self->{died} ? '+' : '-') . 'd'), |
602
|
|
|
|
|
|
|
$self->sprintk('warned', ($self->{warned} ? '+' : '-') . 'w'), |
603
|
|
|
|
|
|
|
$self->sprintk('summary', '(' . _elapsed_str($time_took) . ') '), |
604
|
15
|
100
|
|
|
|
21
|
$self->sprintk('summary', $self->{name}), |
|
|
100
|
|
|
|
|
|
605
|
|
|
|
|
|
|
) . "\n"; |
606
|
15
|
50
|
|
|
|
30
|
if ($tee_hdl) { |
607
|
|
|
|
|
|
|
print $tee_hdl |
608
|
|
|
|
|
|
|
' nb_succ ', $self->{nb_succ}, |
609
|
|
|
|
|
|
|
' nb_fail ', $self->{nb_fail}, |
610
|
|
|
|
|
|
|
' died ', $self->{died}, |
611
|
|
|
|
|
|
|
' warned ', $self->{warned}, |
612
|
0
|
|
|
|
|
0
|
' time_took ', _elapsed_str($time_took), |
613
|
|
|
|
|
|
|
"\n" |
614
|
|
|
|
|
|
|
; |
615
|
|
|
|
|
|
|
} |
616
|
15
|
100
|
|
|
|
38
|
if (defined($options{s}) ? $options{s} : $self->{options}{s}) { |
|
|
100
|
|
|
|
|
|
617
|
13
|
|
|
|
|
21
|
print $summary; |
618
|
|
|
|
|
|
|
# $self->printk('summary', $summary); |
619
|
|
|
|
|
|
|
} |
620
|
15
|
|
|
|
|
57
|
$self->reset_options(); |
621
|
|
|
|
|
|
|
return |
622
|
|
|
|
|
|
|
$self->{name}, |
623
|
|
|
|
|
|
|
$time_took, |
624
|
|
|
|
|
|
|
$self->{nb_succ}, |
625
|
|
|
|
|
|
|
$self->{nb_fail}, |
626
|
|
|
|
|
|
|
$self->{died}, |
627
|
|
|
|
|
|
|
$self->{warned}, |
628
|
15
|
|
|
|
|
126
|
; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head2 ::files (%options) |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
After having found all the files that correspond to the criteria |
637
|
|
|
|
|
|
|
defined in %options (for example, directory to look in), for each file |
638
|
|
|
|
|
|
|
calls perl in a subshell to run something like this: |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
perl -M$file -e 'test()' |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
The results of each run are collected, examined and tallied, and a |
643
|
|
|
|
|
|
|
summary line and a '1..n' line are displayed, something like this: |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
# Total +7 -5 0d 1w (00h:00m:00s) in 4 modules |
646
|
|
|
|
|
|
|
1..12 |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
Returns a list of: |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
Number of seconds it took to run the examples. |
651
|
|
|
|
|
|
|
Number of 'ok' that succeeded. |
652
|
|
|
|
|
|
|
Number of 'ok' that failed. |
653
|
|
|
|
|
|
|
Number of examples that died. |
654
|
|
|
|
|
|
|
Number of examples that had warnings. |
655
|
|
|
|
|
|
|
Number of modules that were run. |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
All values in %options are optional. Their meaning and default value |
658
|
|
|
|
|
|
|
are as follows: |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=over 4 |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=item d* => '.' # Glob Directory. |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
All options starting with the letter 'd' designate directories in |
665
|
|
|
|
|
|
|
which to look for files matching the glob specified by option 'g'. |
666
|
|
|
|
|
|
|
These directories should be in perl's current module search path, else |
667
|
|
|
|
|
|
|
add to the path using the 'i' option. |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
=item g => '*_T.pm' # Glob for files to test. |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
Only files matching this glob will be tested. |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=item r => 1 # Search for files recursively. |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
If set to true, files matching the 'g' glob will be searched for |
676
|
|
|
|
|
|
|
recursively in all subdirs starting from (and including) those |
677
|
|
|
|
|
|
|
specified by the 'd' options. FIXME: Currently, it's always true. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=item i* => '' # Directories to add to perl @INC paths. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
All options starting with the letter 'i' designate directories that |
682
|
|
|
|
|
|
|
you want to add to the @INC path for finding modules. They will be |
683
|
|
|
|
|
|
|
added in the order of the sorted 'i*' keys. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=item t => {} # test() options. |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
These options will be passed to the test() method, invoked for each |
688
|
|
|
|
|
|
|
tested file. |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=item follow => 1 # Follow symlinks when looking for files. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
This is hard-coded for now, it cannot change. FIXME |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=back |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=cut |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
sub files { |
699
|
1
|
|
|
1
|
1
|
5
|
my ($pkg, %options) = @_; |
700
|
|
|
|
|
|
|
defined($options{$_}) || ($options{$_} = $_D{f}{$_}) |
701
|
1
|
|
66
|
|
|
10
|
for qw(g r t); |
702
|
1
|
50
|
|
|
|
7
|
$pkg->{options}{c} = defined($options{c}) ? $options{c} : $_D{m}{c}; |
703
|
|
|
|
|
|
|
$options{d} = $_D{f}{d} |
704
|
1
|
50
|
|
|
|
8
|
unless grep substr($_, 0, 1) eq 'd', keys %options; |
705
|
1
|
|
|
|
|
27
|
my @dirs = map File::Spec->rel2abs($options{$_}), |
706
|
|
|
|
|
|
|
grep substr($_, 0, 1) eq 'd', keys %options; |
707
|
|
|
|
|
|
|
# Make the options to pass to test() into a string. |
708
|
|
|
|
|
|
|
my $t_options = join ', ', map { |
709
|
2
|
|
|
|
|
7
|
"$_ => '$options{t}{$_}'" |
710
|
1
|
|
|
|
|
2
|
} keys %{$options{t}}; |
|
1
|
|
|
|
|
3
|
|
711
|
|
|
|
|
|
|
# Use the user supplied -i* options and the current contents of |
712
|
|
|
|
|
|
|
# @INC as the include path (-I) to the perl we will call. |
713
|
1
|
|
|
|
|
2
|
my $libs = ''; |
714
|
1
|
|
|
|
|
18
|
$libs = join ' ', |
715
|
|
|
|
|
|
|
map(qq|"-I$options{$_}"|, |
716
|
|
|
|
|
|
|
grep substr($_, 0, 1) eq 'i', sort keys %options), |
717
|
|
|
|
|
|
|
map(qq|"-I$_"|, @INC); |
718
|
1
|
|
|
|
|
2
|
my $tot_nb_succ = 0; |
719
|
1
|
|
|
|
|
2
|
my $tot_nb_fail = 0; |
720
|
1
|
|
|
|
|
1
|
my $tot_died = 0; |
721
|
1
|
|
|
|
|
1
|
my $tot_warned = 0; |
722
|
1
|
|
|
|
|
2
|
my $tot_hrs = 0; |
723
|
1
|
|
|
|
|
1
|
my $tot_mins = 0; |
724
|
1
|
|
|
|
|
2
|
my $tot_secs = 0; |
725
|
1
|
|
|
|
|
1
|
my $nb_modules = 0; |
726
|
1
|
|
|
|
|
1
|
my @found_modules; |
727
|
|
|
|
|
|
|
my $wanted = sub { |
728
|
11
|
|
|
11
|
|
10
|
my $dir = $File::Find::dir; |
729
|
11
|
|
|
|
|
8
|
my $file = $_; |
730
|
11
|
|
|
|
|
13
|
my $spec = "$dir/$file"; |
731
|
11
|
100
|
|
|
|
216
|
return if -d $spec; |
732
|
9
|
100
|
|
|
|
14
|
return unless matches_glob($spec, $options{g}); |
733
|
4
|
|
|
|
|
6
|
my $module = extract_module_name($spec); |
734
|
4
|
50
|
|
|
|
8
|
return unless defined $module; |
735
|
4
|
|
|
|
|
84
|
push @found_modules, $module; |
736
|
1
|
|
|
|
|
11
|
}; |
737
|
1
|
|
|
|
|
141
|
find({wanted => $wanted, follow => 1}, $_) for @dirs; |
738
|
1
|
|
|
|
|
4
|
my $start_time = time; |
739
|
1
|
|
|
|
|
5
|
for my $module (sort @found_modules) { |
740
|
4
|
|
|
|
|
30
|
my (undef, $file_name) = tempfile(UNLINK => 1); |
741
|
|
|
|
|
|
|
# Try to make quotes OS-neutral. |
742
|
4
|
|
|
|
|
2155
|
my $prog = qq{$^X $libs -w -e "use $module; } |
743
|
|
|
|
|
|
|
. qq{t()->_tee_to(q[$file_name]); test($t_options)"}; |
744
|
4
|
|
|
|
|
273146
|
system "$prog"; |
745
|
4
|
|
|
|
|
100
|
my $result = read_file($file_name); |
746
|
4
|
|
|
|
|
559
|
my ($nb_succ, $nb_fail, $died, $warned, $hrs, $mins, $secs) |
747
|
|
|
|
|
|
|
= $result =~ / |
748
|
|
|
|
|
|
|
nb_succ \s+ (\S+) \s+ |
749
|
|
|
|
|
|
|
nb_fail \s+ (\S+) \s+ |
750
|
|
|
|
|
|
|
died \s+ (\S+) \s+ |
751
|
|
|
|
|
|
|
warned \s+ (\S+) \s+ |
752
|
|
|
|
|
|
|
time_took \s+ (..)h:(..)m:(..) |
753
|
|
|
|
|
|
|
/x; |
754
|
4
|
|
50
|
|
|
26
|
$tot_nb_succ += $nb_succ || 0; |
755
|
4
|
|
100
|
|
|
23
|
$tot_nb_fail += $nb_fail || 0; |
756
|
4
|
|
100
|
|
|
28
|
$tot_died += $died || 0; |
757
|
4
|
|
100
|
|
|
14
|
$tot_warned += $warned || 0; |
758
|
4
|
|
50
|
|
|
15
|
$tot_hrs += $hrs || 0; |
759
|
4
|
|
50
|
|
|
20
|
$tot_mins += $mins || 0; |
760
|
4
|
|
50
|
|
|
12
|
$tot_secs += $secs || 0; |
761
|
4
|
|
|
|
|
13
|
++$nb_modules; |
762
|
|
|
|
|
|
|
}; |
763
|
1
|
|
|
|
|
15
|
my $tot_time = _elapsed_str(time - $start_time); |
764
|
|
|
|
|
|
|
# Summary line. |
765
|
1
|
|
|
|
|
9
|
$pkg->printk('summary', '# Total '); |
766
|
1
|
|
|
|
|
10
|
$pkg->printk('success', "+$tot_nb_succ "); |
767
|
1
|
|
|
|
|
7
|
$pkg->printk('failure', "-$tot_nb_fail "); |
768
|
1
|
|
|
|
|
6
|
$pkg->printk('died', "${tot_died}d "); |
769
|
1
|
|
|
|
|
11
|
$pkg->printk('warned', "${tot_warned}w "); |
770
|
1
|
|
|
|
|
5
|
$pkg->printk('summary', "($tot_time) "); |
771
|
1
|
|
|
|
|
6
|
$pkg->printk('summary', "in $nb_modules modules.\n"); |
772
|
|
|
|
|
|
|
# '1..n' line. |
773
|
1
|
|
|
|
|
9
|
$pkg->printk('summary', sprintf "1..%d\n", $tot_nb_succ + $tot_nb_fail); |
774
|
|
|
|
|
|
|
return |
775
|
1
|
|
|
|
|
34
|
$tot_time, |
776
|
|
|
|
|
|
|
$tot_nb_succ, |
777
|
|
|
|
|
|
|
$tot_nb_fail, |
778
|
|
|
|
|
|
|
$tot_died, |
779
|
|
|
|
|
|
|
$tot_warned, |
780
|
|
|
|
|
|
|
$nb_modules, |
781
|
|
|
|
|
|
|
; |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
785
|
|
|
|
|
|
|
sub glob_to_regex { |
786
|
39
|
|
|
39
|
0
|
41
|
my $glob = shift; |
787
|
39
|
|
|
|
|
45
|
$glob =~ s/\./\\./g; |
788
|
39
|
|
|
|
|
56
|
$glob =~ s/\*/\.*/g; |
789
|
39
|
|
|
|
|
29
|
$glob =~ s/\?/./g; |
790
|
|
|
|
|
|
|
# Insert anchors for start and end of string. |
791
|
39
|
|
|
|
|
71
|
$glob =~ s/^/\^/g; |
792
|
39
|
|
|
|
|
73
|
$glob =~ s/$/\$/g; |
793
|
39
|
|
|
|
|
44
|
return $glob; |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
797
|
|
|
|
|
|
|
sub _adjust_verbosity { |
798
|
10
|
|
|
10
|
|
9
|
my $val = shift; |
799
|
10
|
50
|
|
|
|
35
|
return ($val =~ /^(0|1|2)$/) ? $val : REPORT_FAILURES; |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
=begin maintenance $t->sprintk ($color_tag, $text) |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
$color_tag, which will map into the color table, is one of: |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
what |
809
|
|
|
|
|
|
|
died |
810
|
|
|
|
|
|
|
warned |
811
|
|
|
|
|
|
|
summary |
812
|
|
|
|
|
|
|
success |
813
|
|
|
|
|
|
|
failure |
814
|
|
|
|
|
|
|
diag |
815
|
|
|
|
|
|
|
default |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
=end maintenance |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
=cut |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
sub sprintk { |
822
|
283
|
|
|
283
|
0
|
236
|
my ($self, $color_tag, $text) = @_; |
823
|
283
|
50
|
33
|
|
|
959
|
return $text unless $gColor->{id} && $self->{options}{c}; |
824
|
0
|
|
0
|
|
|
0
|
my $raw_color = $_D{c}{$color_tag} || $_D{c}{default}; |
825
|
0
|
|
|
|
|
0
|
my $cooked_color = $gColor->{palette}{$raw_color}; |
826
|
0
|
|
|
|
|
0
|
my $ret_str = ''; |
827
|
0
|
0
|
|
|
|
0
|
if ($gColor->{id} eq 'Win32Console') { |
|
|
0
|
|
|
|
|
|
828
|
0
|
|
|
|
|
0
|
my $save_color = $gColor->{out}->Attr(); |
829
|
0
|
|
|
|
|
0
|
$gColor->{out}->Attr($cooked_color); |
830
|
0
|
|
|
|
|
0
|
$gColor->{out}->Write($&) while $text =~ /.{1,1000}/gs; |
831
|
|
|
|
|
|
|
# $gColor->{out}->Attr($Win32::Console::FG_GRAY | $Win32::Console::BG_BLACK); |
832
|
0
|
|
|
|
|
0
|
$gColor->{out}->Attr($save_color); |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
elsif ($gColor->{id} eq 'ANSI') { |
835
|
0
|
|
|
|
|
0
|
$ret_str .= Term::ANSIColor::color($cooked_color); |
836
|
|
|
|
|
|
|
# Make sure the color reset command is part of the last line |
837
|
|
|
|
|
|
|
# (simplifies testing). |
838
|
0
|
|
|
|
|
0
|
my $chomped = chomp $text; |
839
|
0
|
|
|
|
|
0
|
$ret_str .= $text; |
840
|
0
|
|
|
|
|
0
|
$ret_str .= Term::ANSIColor::color('reset'); |
841
|
0
|
0
|
|
|
|
0
|
$ret_str .= "\n" if $chomped; |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
# Unknown color id. |
844
|
|
|
|
|
|
|
else { |
845
|
0
|
|
|
|
|
0
|
$ret_str .= $text; |
846
|
|
|
|
|
|
|
} |
847
|
0
|
|
|
|
|
0
|
return $ret_str; |
848
|
|
|
|
|
|
|
} |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
851
|
|
|
|
|
|
|
sub printk { |
852
|
178
|
|
|
178
|
0
|
174
|
my ($self, $color_tag, $text) = @_; |
853
|
178
|
|
|
|
|
177
|
print $self->sprintk($color_tag, $text); |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
=head2 $t->reset_options () |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
Resets all options to their default values. |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=cut |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
sub reset_options { |
865
|
39
|
|
|
39
|
1
|
61
|
for my $what (qw(t f m c)) { |
866
|
156
|
|
|
|
|
109
|
$t->{options}{$_} = $_D{$what}{$_} for keys %{$_D{$what}}; |
|
156
|
|
|
|
|
605
|
|
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=head2 $t->options () |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
Returns a ref to the hash representing current option settings. |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=cut |
877
|
|
|
|
|
|
|
|
878
|
216
|
|
|
216
|
1
|
284
|
sub options { $t->{options} } |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=begin maintenance $pkg::_elapsed_str ($seconds) |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
Returns a string like '00:00:05' representing a duration of $seconds |
885
|
|
|
|
|
|
|
as a 'hours:minutes:seconds' equivalent. |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=end maintenance |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
=cut |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub _elapsed_str { |
892
|
16
|
|
|
16
|
|
16
|
my $seconds = shift; |
893
|
16
|
|
|
|
|
31
|
my $hr = int($seconds / 3600); |
894
|
16
|
|
|
|
|
17
|
$seconds -= $hr * 3600; |
895
|
16
|
|
|
|
|
17
|
my $mi = int($seconds / 60); |
896
|
16
|
|
|
|
|
14
|
my $se = $seconds - $mi * 60; |
897
|
16
|
|
|
|
|
70
|
sprintf "%02dh:%02dm:%02ds", $hr, $mi, $se; |
898
|
|
|
|
|
|
|
} |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
901
|
|
|
|
|
|
|
sub extract_module_name { |
902
|
4
|
|
|
4
|
0
|
5
|
my $spec = shift; |
903
|
|
|
|
|
|
|
# Extract the module name from the file. |
904
|
4
|
|
|
|
|
11
|
my $contents = read_file($spec); |
905
|
4
|
|
|
|
|
210
|
my ($module) = $contents =~ /^\s*package\s+(\S+);/m; |
906
|
4
|
|
|
|
|
6
|
return $module; |
907
|
|
|
|
|
|
|
} |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
910
|
|
|
|
|
|
|
sub matches_glob { |
911
|
9
|
|
|
9
|
0
|
9
|
my ($file_spec, $glob) = @_; |
912
|
|
|
|
|
|
|
# Strip leading '^' of resulting regex. |
913
|
9
|
|
|
|
|
12
|
my $regex = substr(glob_to_regex($glob), 1); |
914
|
9
|
|
|
|
|
143
|
return $file_spec =~ /$regex/; |
915
|
|
|
|
|
|
|
} |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
918
|
|
|
|
|
|
|
package Test::Usage::Example; |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
sub new { |
921
|
11
|
|
|
11
|
|
13
|
my ($pkg, $label, $sub_ref) = @_; |
922
|
11
|
|
|
|
|
15
|
my $self = bless {}, $pkg; |
923
|
11
|
|
|
|
|
8
|
@{$self}{qw(label sub_ref)} = ($label, $sub_ref); |
|
11
|
|
|
|
|
21
|
|
924
|
11
|
|
|
|
|
17
|
return $self; |
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
928
|
|
|
|
|
|
|
1; |
929
|
|
|
|
|
|
|
__END__ |