4a64b54eae1b9d07744bc7d14b30a9969f19aff9
[p5sagit/Module-Metadata.git] / t / metadata.t
1 #!/usr/bin/perl -w
2 # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
3 # vim:ts=8:sw=2:et:sta:sts=2
4
5 use strict;
6 use lib 't/lib';
7 use IO::File;
8 use MBTest;
9
10 my $undef;
11
12 # parse various module $VERSION lines
13 # these will be reversed later to create %modules
14 my @modules = (
15   $undef => <<'---', # no $VERSION line
16 package Simple;
17 ---
18   $undef => <<'---', # undefined $VERSION
19 package Simple;
20 our $VERSION;
21 ---
22   '1.23' => <<'---', # declared & defined on same line with 'our'
23 package Simple;
24 our $VERSION = '1.23';
25 ---
26   '1.23' => <<'---', # declared & defined on separate lines with 'our'
27 package Simple;
28 our $VERSION;
29 $VERSION = '1.23';
30 ---
31   '1.23' => <<'---', # commented & defined on same line
32 package Simple;
33 our $VERSION = '1.23'; # our $VERSION = '4.56';
34 ---
35   '1.23' => <<'---', # commented & defined on separate lines
36 package Simple;
37 # our $VERSION = '4.56';
38 our $VERSION = '1.23';
39 ---
40   '1.23' => <<'---', # use vars
41 package Simple;
42 use vars qw( $VERSION );
43 $VERSION = '1.23';
44 ---
45   '1.23' => <<'---', # choose the right default package based on package/file name
46 package Simple::_private;
47 $VERSION = '0';
48 package Simple;
49 $VERSION = '1.23'; # this should be chosen for version
50 ---
51   '1.23' => <<'---', # just read the first $VERSION line
52 package Simple;
53 $VERSION = '1.23'; # we should see this line
54 $VERSION = eval $VERSION; # and ignore this one
55 ---
56   '1.23' => <<'---', # just read the first $VERSION line in reopened package (1)
57 package Simple;
58 $VERSION = '1.23';
59 package Error::Simple;
60 $VERSION = '2.34';
61 package Simple;
62 ---
63   '1.23' => <<'---', # just read the first $VERSION line in reopened package (2)
64 package Simple;
65 package Error::Simple;
66 $VERSION = '2.34';
67 package Simple;
68 $VERSION = '1.23';
69 ---
70   '1.23' => <<'---', # mentions another module's $VERSION
71 package Simple;
72 $VERSION = '1.23';
73 if ( $Other::VERSION ) {
74     # whatever
75 }
76 ---
77   '1.23' => <<'---', # mentions another module's $VERSION in a different package
78 package Simple;
79 $VERSION = '1.23';
80 package Simple2;
81 if ( $Simple::VERSION ) {
82     # whatever
83 }
84 ---
85   '1.23' => <<'---', # $VERSION checked only in assignments, not regexp ops
86 package Simple;
87 $VERSION = '1.23';
88 if ( $VERSION =~ /1\.23/ ) {
89     # whatever
90 }
91 ---
92   '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
93 package Simple;
94 $VERSION = '1.23';
95 if ( $VERSION == 3.45 ) {
96     # whatever
97 }
98 ---
99   '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
100 package Simple;
101 $VERSION = '1.23';
102 package Simple2;
103 if ( $Simple::VERSION == 3.45 ) {
104     # whatever
105 }
106 ---
107   '1.23' => <<'---', # Fully qualified $VERSION declared in package
108 package Simple;
109 $Simple::VERSION = 1.23;
110 ---
111   '1.23' => <<'---', # Differentiate fully qualified $VERSION in a package
112 package Simple;
113 $Simple2::VERSION = '999';
114 $Simple::VERSION = 1.23;
115 ---
116   '1.23' => <<'---', # Differentiate fully qualified $VERSION and unqualified
117 package Simple;
118 $Simple2::VERSION = '999';
119 $VERSION = 1.23;
120 ---
121   '1.23' => <<'---', # $VERSION declared as package variable from within 'main' package
122 $Simple::VERSION = '1.23';
123 {
124   package Simple;
125   $x = $y, $cats = $dogs;
126 }
127 ---
128   '1.23' => <<'---', # $VERSION wrapped in parens - space inside
129 package Simple;
130 ( $VERSION ) = '1.23';
131 ---
132   '1.23' => <<'---', # $VERSION wrapped in parens - no space inside
133 package Simple;
134 ($VERSION) = '1.23';
135 ---
136   '1.23' => <<'---', # $VERSION follows a spurious 'package' in a quoted construct
137 package Simple;
138 __PACKAGE__->mk_accessors(qw(
139     program socket proc
140     package filename line codeline subroutine finished));
141
142 our $VERSION = "1.23";
143 ---
144   '1.23' => <<'---', # $VERSION using version.pm
145   package Simple;
146   use version; our $VERSION = version->new('1.23');
147 ---
148   '1.23' => <<'---', # $VERSION using version.pm and qv()
149   package Simple;
150   use version; our $VERSION = qv('1.230');
151 ---
152   '1.23' => <<'---', # Two version assignments, should ignore second one
153   $Simple::VERSION = '1.230';
154   $Simple::VERSION = eval $Simple::VERSION;
155 ---
156   '1.23' => <<'---', # declared & defined on same line with 'our'
157 package Simple;
158 our $VERSION = '1.23_00_00';
159 ---
160   '1.23' => <<'---', # package NAME VERSION
161   package Simple 1.23;
162 ---
163   '1.23_01' => <<'---', # package NAME VERSION
164   package Simple 1.23_01;
165 ---
166   'v1.2.3' => <<'---', # package NAME VERSION
167   package Simple v1.2.3;
168 ---
169   'v1.2_3' => <<'---', # package NAME VERSION
170   package Simple v1.2_3;
171 ---
172   '1.23' => <<'---', # trailing crud
173   package Simple;
174   our $VERSION;
175   $VERSION = '1.23-alpha';
176 ---
177   '1.23' => <<'---', # trailing crud
178   package Simple;
179   our $VERSION;
180   $VERSION = '1.23b';
181 ---
182   '1.234' => <<'---', # multi_underscore
183   package Simple;
184   our $VERSION;
185   $VERSION = '1.2_3_4';
186 ---
187   '0' => <<'---', # non-numeric
188   package Simple;
189   our $VERSION;
190   $VERSION = 'onetwothree';
191 ---
192   $undef => <<'---', # package NAME BLOCK, undef $VERSION
193 package Simple {
194   our $VERSION;
195 }
196 ---
197   '1.23' => <<'---', # package NAME BLOCK, with $VERSION
198 package Simple {
199   our $VERSION = '1.23';
200 }
201 ---
202   '1.23' => <<'---', # package NAME VERSION BLOCK
203 package Simple 1.23 {
204   1;
205 }
206 ---
207   'v1.2.3_4' => <<'---', # package NAME VERSION BLOCK
208 package Simple v1.2.3_4 {
209   1;
210 }
211 ---
212 );
213 my %modules = reverse @modules;
214
215 plan tests => 52 + 2 * keys( %modules );
216
217 require_ok('Module::Metadata');
218
219 # class method C<find_module_by_name>
220 my $module = Module::Metadata->find_module_by_name(
221                'Module::Metadata' );
222 ok( -e $module, 'find_module_by_name() succeeds' );
223
224 #########################
225
226 my $tmp = MBTest->tmpdir;
227
228 use DistGen;
229 my $dist = DistGen->new( dir => $tmp );
230 $dist->regen;
231
232 $dist->chdir_in;
233
234
235 # fail on invalid module name
236 my $pm_info = Module::Metadata->new_from_module(
237                 'Foo::Bar', inc => [] );
238 ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
239
240
241 # fail on invalid filename
242 my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
243 $pm_info = Module::Metadata->new_from_file( $file, inc => [] );
244 ok( !defined( $pm_info ), 'fail if can\'t find module by file name' );
245
246
247 # construct from module filename
248 $file = File::Spec->catfile( 'lib', split( /::/, $dist->name ) ) . '.pm';
249 $pm_info = Module::Metadata->new_from_file( $file );
250 ok( defined( $pm_info ), 'new_from_file() succeeds' );
251
252 # construct from filehandle
253 my $handle = IO::File->new($file);
254 $pm_info = Module::Metadata->new_from_handle( $handle, $file );
255 ok( defined( $pm_info ), 'new_from_handle() succeeds' );
256 $pm_info = Module::Metadata->new_from_handle( $handle );
257 is( $pm_info, undef, "new_from_handle() without filename returns undef" );
258 close($handle);
259
260 # construct from module name, using custom include path
261 $pm_info = Module::Metadata->new_from_module(
262              $dist->name, inc => [ 'lib', @INC ] );
263 ok( defined( $pm_info ), 'new_from_module() succeeds' );
264
265
266 foreach my $module ( sort keys %modules ) {
267     my $expected = $modules{$module};
268  SKIP: {
269     skip( "No our() support until perl 5.6", 2 )
270         if $] < 5.006 && $module =~ /\bour\b/;
271     skip( "No package NAME VERSION support until perl 5.11.1", 2 )
272         if $] < 5.011001 && $module =~ /package\s+[\w\:\']+\s+v?[0-9._]+/;
273
274     $dist->change_file( 'lib/Simple.pm', $module );
275     $dist->regen;
276
277     my $warnings = '';
278     local $SIG{__WARN__} = sub { $warnings .= $_ for @_ };
279     my $pm_info = Module::Metadata->new_from_file( $file );
280
281     # Test::Builder will prematurely numify objects, so use this form
282     my $errs;
283     my $got = $pm_info->version;
284     if ( defined $expected ) {
285         ok( $got eq $expected,
286             "correct module version (expected '$expected')" )
287             or $errs++;
288     } else {
289         ok( !defined($got),
290             "correct module version (expected undef)" )
291             or $errs++;
292     }
293     is( $warnings, '', 'no warnings from parsing' ) or $errs++;
294     diag "Got: '$got'\nModule contents:\n$module" if $errs;
295   }
296 }
297
298 # revert to pristine state
299 $dist->regen( clean => 1 );
300
301 # Find each package only once
302 $dist->change_file( 'lib/Simple.pm', <<'---' );
303 package Simple;
304 $VERSION = '1.23';
305 package Error::Simple;
306 $VERSION = '2.34';
307 package Simple;
308 ---
309
310 $dist->regen;
311
312 $pm_info = Module::Metadata->new_from_file( $file );
313
314 my @packages = $pm_info->packages_inside;
315 is( @packages, 2, 'record only one occurence of each package' );
316
317
318 # Module 'Simple.pm' does not contain package 'Simple';
319 # constructor should not complain, no default module name or version
320 $dist->change_file( 'lib/Simple.pm', <<'---' );
321 package Simple::Not;
322 $VERSION = '1.23';
323 ---
324
325 $dist->regen;
326 $pm_info = Module::Metadata->new_from_file( $file );
327
328 is( $pm_info->name, undef, 'no default package' );
329 is( $pm_info->version, undef, 'no version w/o default package' );
330
331 # Module 'Simple.pm' contains an alpha version
332 # constructor should report first $VERSION found
333 $dist->change_file( 'lib/Simple.pm', <<'---' );
334 package Simple;
335 $VERSION = '1.23_01';
336 $VERSION = eval $VERSION;
337 ---
338
339 $dist->regen;
340 $pm_info = Module::Metadata->new_from_file( $file );
341
342 is( $pm_info->version, '1.23_01', 'alpha version reported');
343
344 # NOTE the following test has be done this way because Test::Builder is
345 # too smart for our own good and tries to see if the version object is a
346 # dual-var, which breaks with alpha versions:
347 #    Argument "1.23_0100" isn't numeric in addition (+) at
348 #    /usr/lib/perl5/5.8.7/Test/Builder.pm line 505.
349
350 ok( $pm_info->version > 1.23, 'alpha version greater than non');
351
352 # revert to pristine state
353 $dist->regen( clean => 1 );
354
355 # parse $VERSION lines scripts for package main
356 my @scripts = (
357   <<'---', # package main declared
358 #!perl -w
359 package main;
360 $VERSION = '0.01';
361 ---
362   <<'---', # on first non-comment line, non declared package main
363 #!perl -w
364 $VERSION = '0.01';
365 ---
366   <<'---', # after non-comment line
367 #!perl -w
368 use strict;
369 $VERSION = '0.01';
370 ---
371   <<'---', # 1st declared package
372 #!perl -w
373 package main;
374 $VERSION = '0.01';
375 package _private;
376 $VERSION = '999';
377 ---
378   <<'---', # 2nd declared package
379 #!perl -w
380 package _private;
381 $VERSION = '999';
382 package main;
383 $VERSION = '0.01';
384 ---
385   <<'---', # split package
386 #!perl -w
387 package main;
388 package _private;
389 $VERSION = '999';
390 package main;
391 $VERSION = '0.01';
392 ---
393   <<'---', # define 'main' version from other package
394 package _private;
395 $::VERSION = 0.01;
396 $VERSION = '999';
397 ---
398   <<'---', # define 'main' version from other package
399 package _private;
400 $VERSION = '999';
401 $::VERSION = 0.01;
402 ---
403 );
404
405 my ( $i, $n ) = ( 1, scalar( @scripts ) );
406 foreach my $script ( @scripts ) {
407   $dist->change_file( 'bin/simple.plx', $script );
408   $dist->regen;
409   $pm_info = Module::Metadata->new_from_file(
410                File::Spec->catfile( 'bin', 'simple.plx' ) );
411
412   is( $pm_info->version, '0.01', "correct script version ($i of $n)" );
413   $i++;
414 }
415
416
417 # examine properties of a module: name, pod, etc
418 $dist->change_file( 'lib/Simple.pm', <<'---' );
419 package Simple;
420 $VERSION = '0.01';
421 package Simple::Ex;
422 $VERSION = '0.02';
423
424 =head1 NAME
425
426 Simple - It's easy.
427
428 =head1 AUTHOR
429
430 Simple Simon
431
432 =cut
433 ---
434 $dist->regen;
435
436 $pm_info = Module::Metadata->new_from_module(
437              $dist->name, inc => [ 'lib', @INC ] );
438
439 is( $pm_info->name, 'Simple', 'found default package' );
440 is( $pm_info->version, '0.01', 'version for default package' );
441
442 # got correct version for secondary package
443 is( $pm_info->version( 'Simple::Ex' ), '0.02',
444     'version for secondary package' );
445
446 my $filename = $pm_info->filename;
447 ok( defined( $filename ) && -e $filename,
448     'filename() returns valid path to module file' );
449
450 @packages = $pm_info->packages_inside;
451 is( @packages, 2, 'found correct number of packages' );
452 is( $packages[0], 'Simple', 'packages stored in order found' );
453
454 # we can detect presence of pod regardless of whether we are collecting it
455 ok( $pm_info->contains_pod, 'contains_pod() succeeds' );
456
457 my @pod = $pm_info->pod_inside;
458 is_deeply( \@pod, [qw(NAME AUTHOR)], 'found all pod sections' );
459
460 is( $pm_info->pod('NONE') , undef,
461     'return undef() if pod section not present' );
462
463 is( $pm_info->pod('NAME'), undef,
464     'return undef() if pod section not collected' );
465
466
467 # collect_pod
468 $pm_info = Module::Metadata->new_from_module(
469              $dist->name, inc => [ 'lib', @INC ], collect_pod => 1 );
470
471 {
472   my %pod;
473   for my $section (qw(NAME AUTHOR)) {
474     my $content = $pm_info->pod( $section );
475     if ( $content ) {
476       $content =~ s/^\s+//;
477       $content =~ s/\s+$//;
478     }
479     $pod{$section} = $content;
480   }
481   is( $pod{NAME}, q|Simple - It's easy.|, 'collected NAME pod section' );
482   is( $pod{AUTHOR}, q|Simple Simon|, 'collected AUTHOR pod section' );
483 }
484
485 {
486   # Make sure processing stops after __DATA__
487   $dist->change_file( 'lib/Simple.pm', <<'---' );
488 package Simple;
489 $VERSION = '0.01';
490 __DATA__
491 *UNIVERSAL::VERSION = sub {
492   foo();
493 };
494 ---
495   $dist->regen;
496
497   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
498   is( $pm_info->name, 'Simple', 'found default package' );
499   is( $pm_info->version, '0.01', 'version for default package' );
500   my @packages = $pm_info->packages_inside;
501   is_deeply(\@packages, ['Simple'], 'packages inside');
502 }
503
504 {
505   # Make sure we handle version.pm $VERSIONs well
506   $dist->change_file( 'lib/Simple.pm', <<'---' );
507 package Simple;
508 $VERSION = version->new('0.60.' . (qw$Revision: 128 $)[1]);
509 package Simple::Simon;
510 $VERSION = version->new('0.61.' . (qw$Revision: 129 $)[1]);
511 ---
512   $dist->regen;
513
514   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
515   is( $pm_info->name, 'Simple', 'found default package' );
516   is( $pm_info->version, '0.60.128', 'version for default package' );
517   my @packages = $pm_info->packages_inside;
518   is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
519   is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
520 }
521
522 # check that package_versions_from_directory works
523
524 $dist->change_file( 'lib/Simple.pm', <<'---' );
525 package Simple;
526 $VERSION = '0.01';
527 package Simple::Ex;
528 $VERSION = '0.02';
529 {
530   package main; # should ignore this
531 }
532 {
533   package DB; # should ignore this
534 }
535 {
536   package Simple::_private; # should ignore this
537 }
538
539 =head1 NAME
540
541 Simple - It's easy.
542
543 =head1 AUTHOR
544
545 Simple Simon
546
547 =cut
548 ---
549 $dist->regen;
550
551 my $exp_pvfd = {
552   'Simple' => {
553     'file' => 'Simple.pm',
554     'version' => '0.01'
555   },
556   'Simple::Ex' => {
557     'file' => 'Simple.pm',
558     'version' => '0.02'
559   }
560 };
561
562 my $got_pvfd = Module::Metadata->package_versions_from_directory('lib');
563
564 is_deeply( $got_pvfd, $exp_pvfd, "package_version_from_directory()" )
565   or diag explain $got_pvfd;
566
567 {
568   my $got_provides = Module::Metadata->provides(dir => 'lib', version => 2);
569   my $exp_provides = {
570     'Simple' => {
571       'file' => 'lib/Simple.pm',
572       'version' => '0.01'
573     },
574     'Simple::Ex' => {
575       'file' => 'lib/Simple.pm',
576       'version' => '0.02'
577     }
578   };
579
580   is_deeply( $got_provides, $exp_provides, "provides()" )
581     or diag explain $got_provides;
582 }
583
584 {
585   my $got_provides = Module::Metadata->provides(dir => 'lib', prefix => 'other', version => 1.4);
586   my $exp_provides = {
587     'Simple' => {
588       'file' => 'other/Simple.pm',
589       'version' => '0.01'
590     },
591     'Simple::Ex' => {
592       'file' => 'other/Simple.pm',
593       'version' => '0.02'
594     }
595   };
596
597   is_deeply( $got_provides, $exp_provides, "provides()" )
598     or diag explain $got_provides;
599 }
600
601 # Check package_versions_from_directory with regard to case-sensitivity
602 {
603   $dist->change_file( 'lib/Simple.pm', <<'---' );
604 package simple;
605 $VERSION = '0.01';
606 ---
607   $dist->regen;
608
609   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
610   is( $pm_info->name, undef, 'no default package' );
611   is( $pm_info->version, undef, 'version for default package' );
612   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
613   is( $pm_info->version('Simple'), undef, 'version for capitalized package' );
614
615   $dist->change_file( 'lib/Simple.pm', <<'---' );
616 package simple;
617 $VERSION = '0.01';
618 package Simple;
619 $VERSION = '0.02';
620 package SiMpLe;
621 $VERSION = '0.03';
622 ---
623   $dist->regen;
624
625   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
626   is( $pm_info->name, 'Simple', 'found default package' );
627   is( $pm_info->version, '0.02', 'version for default package' );
628   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
629   is( $pm_info->version('Simple'), '0.02', 'version for capitalized package' );
630   is( $pm_info->version('SiMpLe'), '0.03', 'version for mixed-case package' );
631 }