Re: perlfunc.pod/split; concerning trailing fields
[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
180 skip => [qw/ _POSIX_JOB_CONTROL /], # platform varying
d51cf0c9 181 perl => [qw/ import croak AUTOLOAD /],
182
183 XS => [qw/ write wctomb wcstombs uname tzset tzname
184 ttyname tmpnam times tcsetpgrp tcsendbreak
185 tcgetpgrp tcflush tcflow tcdrain tanh tan
186 sysconf strxfrm strtoul strtol strtod
187 strftime strcoll sinh sigsuspend sigprocmask
188 sigpending sigaction setuid setsid setpgid
189 setlocale setgid read pipe pause pathconf
190 open nice modf mktime mkfifo mbtowc mbstowcs
191 mblen lseek log10 localeconv ldexp lchown
192 isxdigit isupper isspace ispunct isprint
193 islower isgraph isdigit iscntrl isalpha
194 isalnum int_macro_int getcwd frexp fpathconf
195 fmod floor dup2 dup difftime cuserid ctime
196 ctermid cosh constant close clock ceil
197 bootstrap atan asin asctime acos access abort
2018a5c3 198 _exit
d51cf0c9 199 /],
200 },
2018a5c3 201
f84b4b22 202 IO::Socket => { dflt => 'constant', # 157/190
2018a5c3 203
204 perl => [qw/ timeout socktype sockopt sockname
205 socketpair socket sockdomain sockaddr_un
206 sockaddr_in shutdown setsockopt send
207 register_domain recv protocol peername
208 new listen import getsockopt croak
209 connected connect configure confess close
e412117e 210 carp bind atmark accept
211 /, $] > 5.009 ? ('blocking') : () ],
2018a5c3 212
213 XS => [qw/ unpack_sockaddr_un unpack_sockaddr_in
214 sockatmark sockaddr_family pack_sockaddr_un
215 pack_sockaddr_in inet_ntoa inet_aton
216 /],
217 },
c0939cee 218};
219
220############
221
222B::Concise::compile('-nobanner'); # set a silent default
5b493bdf 223getopts('vaVcr:', \my %opts) or
c0939cee 224 die <<EODIE;
225
226usage: PERL_CORE=1 ./perl ext/B/t/concise-xs.t [-av] [module-list]
227 tests ability to discern XS funcs using Digest::MD5 package
228 -v : runs verbosely
229 -V : more verbosity
230 -a : runs all modules in CoreList
5b493bdf 231 -c : writes test corrections as a Data::Dumper expression
232 -r <file> : reads file of tests, as written by -c
468aa647 233 <args> : additional modules are loaded and tested
d51cf0c9 234 (will report failures, since no XS funcs are known apriori)
c0939cee 235
236EODIE
237 ;
238
239if (%opts) {
240 require Data::Dumper;
241 Data::Dumper->import('Dumper');
242 $Data::Dumper::Sortkeys = 1;
243}
244my @argpkgs = @ARGV;
5b493bdf 245my %report;
246
247if ($opts{r}) {
248 my $refpkgs = require "$opts{r}";
249 $testpkgs->{$_} = $refpkgs->{$_} foreach keys %$refpkgs;
468aa647 250}
5b493bdf 251
252unless ($opts{a}) {
253 unless (@argpkgs) {
254 foreach $pkg (sort keys %$testpkgs) {
255 test_pkg($pkg, $testpkgs->{$pkg});
256 }
257 } else {
258 foreach $pkg (@argpkgs) {
259 test_pkg($pkg, $testpkgs->{$pkg});
260 }
261 }
262} else {
263 corecheck();
c0939cee 264}
c0939cee 265############
266
267sub test_pkg {
d51cf0c9 268 my ($pkg, $fntypes) = @_;
269 require_ok($pkg);
c0939cee 270
d51cf0c9 271 # build %stash: keys are func-names, vals filled in below
c0939cee 272 my (%stash) = map
d51cf0c9 273 ( ($_ => 0)
274 => ( grep exists &{"$pkg\::$_"} # grab CODE symbols
c0939cee 275 => grep !/__ANON__/ # but not anon subs
d51cf0c9 276 => keys %{$pkg.'::'} # from symbol table
c0939cee 277 ));
468aa647 278
d51cf0c9 279 for my $type (keys %matchers) {
280 foreach my $fn (@{$fntypes->{$type}}) {
281 carp "$fn can only be one of $type, $stash{$fn}\n"
282 if $stash{$fn};
283 $stash{$fn} = $type;
284 }
285 }
286 # set default type for un-named functions
287 my $dflt = $fntypes->{dflt} || 'perl';
288 for my $k (keys %stash) {
289 $stash{$k} = $dflt unless $stash{$k};
290 }
291 $stash{$_} = 'skip' foreach @{$fntypes->{skip}};
c0939cee 292
5b493bdf 293 if ($opts{v}) {
d51cf0c9 294 diag("fntypes: " => Dumper($fntypes));
295 diag("$pkg stash: " => Dumper(\%stash));
c0939cee 296 }
d51cf0c9 297 foreach my $fn (reverse sort keys %stash) {
298 next if $stash{$fn} eq 'skip';
299 my $res = checkXS("${pkg}::$fn", $stash{$fn});
300 if ($res ne '1') {
301 push @{$report{$pkg}{$res}}, $fn;
5b493bdf 302 }
c0939cee 303 }
304}
305
306sub checkXS {
d51cf0c9 307 my ($func_name, $want) = @_;
308
309 croak "unknown type $want: $func_name\n"
310 unless defined $matchers{$want};
c0939cee 311
312 my ($buf, $err) = render($func_name);
d51cf0c9 313 my $res = like($buf, $matchers{$want}, "$want sub:\t $func_name");
314
315 unless ($res) {
316 # test failed. return type that would give success
317 for my $m (keys %matchers) {
318 return $m if $buf =~ $matchers{$m};
319 }
c0939cee 320 }
d51cf0c9 321 $res;
c0939cee 322}
323
324sub render {
325 my ($func_name) = @_;
326
327 B::Concise::reset_sequence();
328 B::Concise::walk_output(\my $buf);
329
330 my $walker = B::Concise::compile($func_name);
331 eval { $walker->() };
332 diag("err: $@ $buf") if $@;
333 diag("verbose: $buf") if $opts{V};
334
335 return ($buf, $@);
336}
337
338sub corecheck {
339 eval { require Module::CoreList };
340 if ($@) {
341 warn "Module::CoreList not available on $]\n";
342 return;
343 }
5b493bdf 344 my $mods = $Module::CoreList::version{'5.009002'};
345 $mods = [ sort keys %$mods ];
c0939cee 346 print Dumper($mods);
347
5b493bdf 348 foreach my $pkgnm (@$mods) {
c0939cee 349 test_pkg($pkgnm);
350 }
351}
352
5b493bdf 353END {
354 if ($opts{c}) {
d51cf0c9 355 $Data::Dumper::Indent = 1;
356 print "Corrections: ", Dumper(\%report);
5b493bdf 357
358 foreach my $pkg (sort keys %report) {
d51cf0c9 359 for my $type (keys %matchers) {
360 print "$pkg: $type: @{$report{$pkg}{$type}}\n"
361 if @{$report{$pkg}{$type}};
362 }
5b493bdf 363 }
5b493bdf 364 }
365}
366
c0939cee 367__END__