Some standards compliance for the new Time::Piece test.
[p5sagit/p5-mst-13.2.git] / ext / B / t / concise-xs.t
CommitLineData
c0939cee 1#!./perl
2
3# 2 purpose file: 1-test 2-demonstrate (via args, -v -a options)
4
5b493bdf 5=head1 SYNOPSIS
c0939cee 6
d51cf0c9 7To verify that B::Concise properly reports whether functions are XS,
8perl, or optimized constant subs, we test against a few core packages
9which have a stable API, and which have functions of all 3 types.
10
11=head1 WHAT IS TESTED
12
135 core packages are tested; Digest::MD5, B, B::Deparse, Data::Dumper,
14and POSIX. These have a mix of the 3 expected implementation types;
15perl, XS, and constant (optimized constant subs).
16
17%$testpkgs specifies what packages are tested; each package is loaded,
18and the stash is scanned for the function-names in that package.
19
20Each value in %$testpkgs is a hash-of-lists (HoL) whose keys are
21implementation-types and values are lists of function-names of that type.
22
23To keep these HoLs smaller and more managable, they may carry an
24additional 'dflt' => $impl_Type, which means that unnamed functions
25are expected to be of that default implementation type. Those unnamed
26functions are known from the scan of the package stash.
27
28=head1 HOW THEY'RE TESTED
29
30Each function is 'rendered' by B::Concise, and result is matched
31against regexs for each possible implementation-type. For some
32packages, some functions may be unimplemented on some platforms.
33
34To slay this maintenance dragon, the regexs used in like() match
35against renderings which indicate that there is no implementation.
36
37If a function is implemented differently on different platforms, the
38test for that function will fail on one of those platforms. These
39specific functions can be skipped by a 'skip' => [ @list ] to the HoL
40mentioned previously. See usage for skip in B's HoL, which avoids
41testing a function which doesnt exist on non-threaded builds.
c0939cee 42
5b493bdf 43=head1 OPTIONS AND ARGUMENTS
44
45C<-v> and C<-V> trigger 2 levels of verbosity.
46
47C<-a> uses Module::CoreList to run all core packages through the test, which
48gives some interesting results.
49
50C<-c> causes the expected XS/non-XS results to be marked with
d51cf0c9 51corrections, which are then reported at program END, in a form that's
52readily cut-and-pastable into this file.
53
5b493bdf 54
55C<< -r <file> >> reads a file, as written by C<-c>, and adjusts the expected
56results accordingly. The file is 'required', so @INC settings apply.
57
c0939cee 58If module-names are given as args, those packages are run through the
59test harness; this is handy for collecting further items to test, and
60may be useful otherwise (ie just to see).
61
5b493bdf 62=head1 EXAMPLES
63
5b493bdf 64=over 4
65
66=item ./perl -Ilib -wS ext/B/t/concise-xs.t -c Storable
67
68Tests Storable.pm for XS/non-XS routines, writes findings (along with
69test results) to stdout. You could edit results to produce a test
70file, as in next example
71
72=item ./perl -Ilib -wS ext/B/t/concise-xs.t -r ./storable
c0939cee 73
5b493bdf 74Loads file, and uses it to set expectations, and run tests
75
76=item ./perl -Ilib -wS ext/B/t/concise-xs.t -avc > ../foo-avc 2> ../foo-avc2
77
78Gets module list from Module::Corelist, and runs them all through the
79test. Since -c is used, this generates corrections, which are saved
80in a file, which is edited down to produce ../all-xs
81
82=item ./perl -Ilib -wS ext/B/t/concise-xs.t -cr ../all-xs > ../foo 2> ../foo2
83
84This runs the tests specified in the file created in previous example.
85-c is used again, and stdout verifies that all the expected results
86given by -r ../all-xs are now seen.
87
88Looking at ../foo2, you'll see 34 occurrences of the following error:
89
90# err: Can't use an undefined value as a SCALAR reference at
91# lib/B/Concise.pm line 634, <DATA> line 1.
92
93=back
c0939cee 94
95=cut
96
97BEGIN {
98 if ($ENV{PERL_CORE}) {
99 chdir('t') if -d 't';
100 @INC = ('.', '../lib');
101 } else {
102 unshift @INC, 't';
103 push @INC, "../../t";
104 }
105 require Config;
106 if (($Config::Config{'extensions'} !~ /\bB\b/) ){
107 print "1..0 # Skip -- Perl configured without B module\n";
108 exit 0;
109 }
28380c63 110 unless ($Config::Config{useperlio}) {
111 print "1..0 # Skip -- Perl configured without perlio\n";
112 exit 0;
113 }
c0939cee 114}
115
116use Getopt::Std;
117use Carp;
3cd6a7dc 118use Test::More 'no_plan';
c0939cee 119
120require_ok("B::Concise");
121
d51cf0c9 122my %matchers =
123 ( constant => qr{ (?-x: is a constant sub, optimized to a \w+)
124 |(?-x: exists in stash, but has no START) }x,
2018a5c3 125 XS => qr/ is XS code/,
126 perl => qr/ (next|db)state/,
127 noSTART => qr/ exists in stash, but has no START/,
d51cf0c9 128);
5b493bdf 129
d51cf0c9 130my $testpkgs = {
131 # packages to test, with expected types for named funcs
132
133 Digest::MD5 => { perl => [qw/ import /],
134 dflt => 'XS' },
135
136 Data::Dumper => { XS => [qw/ bootstrap Dumpxs /],
137 dflt => 'perl' },
138 B => {
c737faaf 139 dflt => 'constant', # all but 47/297
d51cf0c9 140 skip => [ 'regex_padav' ], # threaded only
141 perl => [qw(
142 walksymtable walkoptree_slow walkoptree_exec
143 timing_info savesym peekop parents objsym debug
144 compile_stats clearsym class
145 )],
146 XS => [qw(
147 warnhook walkoptree_debug walkoptree threadsv_names
148 svref_2object sv_yes sv_undef sv_no save_BEGINs
149 regex_padav ppname perlstring opnumber minus_c
150 main_start main_root main_cv init_av inc_gv hash
151 formfeed end_av dowarn diehook defstash curstash
152 cstring comppadlist check_av cchar cast_I32 bootstrap
5ce57cc0 153 begin_av amagic_generation sub_generation address
e412117e 154 ), $] > 5.009 ? ('unitcheck_av') : ()],
d51cf0c9 155 },
156
157 B::Deparse => { dflt => 'perl', # 235 functions
158
159 XS => [qw( svref_2object perlstring opnumber main_start
160 main_root main_cv )],
161
584420f0 162 constant => [qw/ ASSIGN CVf_LOCKED CVf_LVALUE
d51cf0c9 163 CVf_METHOD LIST_CONTEXT OP_CONST OP_LIST OP_RV2SV
164 OP_STRINGIFY OPf_KIDS OPf_MOD OPf_REF OPf_SPECIAL
165 OPf_STACKED OPf_WANT OPf_WANT_LIST OPf_WANT_SCALAR
166 OPf_WANT_VOID OPpCONST_ARYBASE OPpCONST_BARE
167 OPpENTERSUB_AMPER OPpEXISTS_SUB OPpITER_REVERSED
168 OPpLVAL_INTRO OPpOUR_INTRO OPpSLICE OPpSORT_DESCEND
169 OPpSORT_INPLACE OPpSORT_INTEGER OPpSORT_NUMERIC
170 OPpSORT_REVERSE OPpTARGET_MY OPpTRANS_COMPLEMENT
171 OPpTRANS_DELETE OPpTRANS_SQUASH PMf_CONTINUE
172 PMf_EVAL PMf_EXTENDED PMf_FOLD PMf_GLOBAL PMf_KEEP
7c1f70cb 173 PMf_MULTILINE PMf_ONCE PMf_SINGLELINE
d51cf0c9 174 POSTFIX SVf_FAKE SVf_IOK SVf_NOK SVf_POK SVf_ROK
d449ddce 175 SVpad_OUR SVs_RMG SVs_SMG SWAP_CHILDREN OPpPAD_STATE
7c1f70cb 176 /, $] > 5.009 ? ('RXf_SKIPWHITE') : ('PMf_SKIPWHITE')],
d51cf0c9 177 },
178
f9f861ec 179 POSIX => { dflt => 'constant', # all but 252/589
9b68a132 180 skip => [qw/ _POSIX_JOB_CONTROL /, # platform varying
61699fd9 181 # Might be XS or imported from Fcntl, depending on your
9b68a132 182 # perl version:
e99d581a 183 qw / S_ISBLK S_ISCHR S_ISDIR S_ISFIFO S_ISREG /,
184 # Might be XS or AUTOLOADed, depending on your perl
185 # version:
186 qw /WEXITSTATUS WIFEXITED WIFSIGNALED WIFSTOPPED
187 WSTOPSIG WTERMSIG/,
188 'int_macro_int', # Removed in POSIX 1.16
189 ],
d51cf0c9 190 perl => [qw/ import croak AUTOLOAD /],
191
192 XS => [qw/ write wctomb wcstombs uname tzset tzname
193 ttyname tmpnam times tcsetpgrp tcsendbreak
194 tcgetpgrp tcflush tcflow tcdrain tanh tan
195 sysconf strxfrm strtoul strtol strtod
196 strftime strcoll sinh sigsuspend sigprocmask
197 sigpending sigaction setuid setsid setpgid
198 setlocale setgid read pipe pause pathconf
199 open nice modf mktime mkfifo mbtowc mbstowcs
200 mblen lseek log10 localeconv ldexp lchown
201 isxdigit isupper isspace ispunct isprint
202 islower isgraph isdigit iscntrl isalpha
e99d581a 203 isalnum getcwd frexp fpathconf
d51cf0c9 204 fmod floor dup2 dup difftime cuserid ctime
205 ctermid cosh constant close clock ceil
206 bootstrap atan asin asctime acos access abort
2018a5c3 207 _exit
d51cf0c9 208 /],
209 },
2018a5c3 210
f84b4b22 211 IO::Socket => { dflt => 'constant', # 157/190
2018a5c3 212
213 perl => [qw/ timeout socktype sockopt sockname
214 socketpair socket sockdomain sockaddr_un
215 sockaddr_in shutdown setsockopt send
216 register_domain recv protocol peername
217 new listen import getsockopt croak
218 connected connect configure confess close
e412117e 219 carp bind atmark accept
220 /, $] > 5.009 ? ('blocking') : () ],
2018a5c3 221
222 XS => [qw/ unpack_sockaddr_un unpack_sockaddr_in
223 sockatmark sockaddr_family pack_sockaddr_un
224 pack_sockaddr_in inet_ntoa inet_aton
225 /],
226 },
c0939cee 227};
228
229############
230
231B::Concise::compile('-nobanner'); # set a silent default
5b493bdf 232getopts('vaVcr:', \my %opts) or
c0939cee 233 die <<EODIE;
234
235usage: PERL_CORE=1 ./perl ext/B/t/concise-xs.t [-av] [module-list]
236 tests ability to discern XS funcs using Digest::MD5 package
237 -v : runs verbosely
238 -V : more verbosity
239 -a : runs all modules in CoreList
5b493bdf 240 -c : writes test corrections as a Data::Dumper expression
241 -r <file> : reads file of tests, as written by -c
468aa647 242 <args> : additional modules are loaded and tested
d51cf0c9 243 (will report failures, since no XS funcs are known apriori)
c0939cee 244
245EODIE
246 ;
247
248if (%opts) {
249 require Data::Dumper;
250 Data::Dumper->import('Dumper');
251 $Data::Dumper::Sortkeys = 1;
252}
253my @argpkgs = @ARGV;
5b493bdf 254my %report;
255
256if ($opts{r}) {
257 my $refpkgs = require "$opts{r}";
258 $testpkgs->{$_} = $refpkgs->{$_} foreach keys %$refpkgs;
468aa647 259}
5b493bdf 260
261unless ($opts{a}) {
262 unless (@argpkgs) {
263 foreach $pkg (sort keys %$testpkgs) {
264 test_pkg($pkg, $testpkgs->{$pkg});
265 }
266 } else {
267 foreach $pkg (@argpkgs) {
268 test_pkg($pkg, $testpkgs->{$pkg});
269 }
270 }
271} else {
272 corecheck();
c0939cee 273}
c0939cee 274############
275
276sub test_pkg {
d51cf0c9 277 my ($pkg, $fntypes) = @_;
278 require_ok($pkg);
c0939cee 279
d51cf0c9 280 # build %stash: keys are func-names, vals filled in below
c0939cee 281 my (%stash) = map
d51cf0c9 282 ( ($_ => 0)
283 => ( grep exists &{"$pkg\::$_"} # grab CODE symbols
c0939cee 284 => grep !/__ANON__/ # but not anon subs
d51cf0c9 285 => keys %{$pkg.'::'} # from symbol table
c0939cee 286 ));
468aa647 287
d51cf0c9 288 for my $type (keys %matchers) {
289 foreach my $fn (@{$fntypes->{$type}}) {
290 carp "$fn can only be one of $type, $stash{$fn}\n"
291 if $stash{$fn};
292 $stash{$fn} = $type;
293 }
294 }
295 # set default type for un-named functions
296 my $dflt = $fntypes->{dflt} || 'perl';
297 for my $k (keys %stash) {
298 $stash{$k} = $dflt unless $stash{$k};
299 }
300 $stash{$_} = 'skip' foreach @{$fntypes->{skip}};
c0939cee 301
5b493bdf 302 if ($opts{v}) {
d51cf0c9 303 diag("fntypes: " => Dumper($fntypes));
304 diag("$pkg stash: " => Dumper(\%stash));
c0939cee 305 }
d51cf0c9 306 foreach my $fn (reverse sort keys %stash) {
307 next if $stash{$fn} eq 'skip';
308 my $res = checkXS("${pkg}::$fn", $stash{$fn});
309 if ($res ne '1') {
310 push @{$report{$pkg}{$res}}, $fn;
5b493bdf 311 }
c0939cee 312 }
313}
314
315sub checkXS {
d51cf0c9 316 my ($func_name, $want) = @_;
317
318 croak "unknown type $want: $func_name\n"
319 unless defined $matchers{$want};
c0939cee 320
321 my ($buf, $err) = render($func_name);
d51cf0c9 322 my $res = like($buf, $matchers{$want}, "$want sub:\t $func_name");
323
324 unless ($res) {
325 # test failed. return type that would give success
326 for my $m (keys %matchers) {
327 return $m if $buf =~ $matchers{$m};
328 }
c0939cee 329 }
d51cf0c9 330 $res;
c0939cee 331}
332
333sub render {
334 my ($func_name) = @_;
335
336 B::Concise::reset_sequence();
337 B::Concise::walk_output(\my $buf);
338
339 my $walker = B::Concise::compile($func_name);
340 eval { $walker->() };
341 diag("err: $@ $buf") if $@;
342 diag("verbose: $buf") if $opts{V};
343
344 return ($buf, $@);
345}
346
347sub corecheck {
348 eval { require Module::CoreList };
349 if ($@) {
350 warn "Module::CoreList not available on $]\n";
351 return;
352 }
5b493bdf 353 my $mods = $Module::CoreList::version{'5.009002'};
354 $mods = [ sort keys %$mods ];
c0939cee 355 print Dumper($mods);
356
5b493bdf 357 foreach my $pkgnm (@$mods) {
c0939cee 358 test_pkg($pkgnm);
359 }
360}
361
5b493bdf 362END {
363 if ($opts{c}) {
d51cf0c9 364 $Data::Dumper::Indent = 1;
365 print "Corrections: ", Dumper(\%report);
5b493bdf 366
367 foreach my $pkg (sort keys %report) {
d51cf0c9 368 for my $type (keys %matchers) {
369 print "$pkg: $type: @{$report{$pkg}{$type}}\n"
370 if @{$report{$pkg}{$type}};
371 }
5b493bdf 372 }
5b493bdf 373 }
374}
375
c0939cee 376__END__