286b1aeaa040f7daa268dec52960f59630299cb7
[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 my @pkg_names = (
216   [ 'Simple' ] => <<'---', # package NAME
217 package Simple;
218 ---
219   [ 'Simple::Edward' ] => <<'---', # package NAME::SUBNAME
220 package Simple::Edward;
221 ---
222   [ 'Simple::Edward::' ] => <<'---', # package NAME::SUBNAME::
223 package Simple::Edward::;
224 ---
225   [ "Simple'Edward" ] => <<'---', # package NAME'SUBNAME
226 package Simple'Edward;
227 ---
228   [ "Simple'Edward::" ] => <<'---', # package NAME'SUBNAME::
229 package Simple'Edward::;
230 ---
231   [ 'Simple::::Edward' ] => <<'---', # package NAME::::SUBNAME
232 package Simple::::Edward;
233 ---
234   [ '::Simple::Edward' ] => <<'---', # package ::NAME::SUBNAME
235 package ::Simple::Edward;
236 ---
237   [ 'main' ] => <<'---', # package NAME:SUBNAME (fail)
238 package Simple:Edward;
239 ---
240   [ 'main' ] => <<'---', # package NAME' (fail)
241 package Simple';
242 ---
243   [ 'main' ] => <<'---', # package NAME::SUBNAME' (fail)
244 package Simple::Edward';
245 ---
246   [ 'main' ] => <<'---', # package NAME''SUBNAME (fail)
247 package Simple''Edward;
248 ---
249   [ 'main' ] => <<'---', # package NAME-SUBNAME (fail)
250 package Simple-Edward;
251 ---
252 );
253 my %pkg_names = reverse @pkg_names;
254
255 plan tests => 54 + (2 * keys( %modules )) + (2 * keys( %pkg_names ));
256
257 require_ok('Module::Metadata');
258
259 # class method C<find_module_by_name>
260 my $module = Module::Metadata->find_module_by_name(
261                'Module::Metadata' );
262 ok( -e $module, 'find_module_by_name() succeeds' );
263
264 #########################
265
266 my $tmp = MBTest->tmpdir;
267
268 use DistGen;
269 my $dist = DistGen->new( dir => $tmp );
270 $dist->regen;
271
272 $dist->chdir_in;
273
274
275 # fail on invalid module name
276 my $pm_info = Module::Metadata->new_from_module(
277                 'Foo::Bar', inc => [] );
278 ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
279
280
281 # fail on invalid filename
282 my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
283 $pm_info = Module::Metadata->new_from_file( $file, inc => [] );
284 ok( !defined( $pm_info ), 'fail if can\'t find module by file name' );
285
286
287 # construct from module filename
288 $file = File::Spec->catfile( 'lib', split( /::/, $dist->name ) ) . '.pm';
289 $pm_info = Module::Metadata->new_from_file( $file );
290 ok( defined( $pm_info ), 'new_from_file() succeeds' );
291
292 # construct from filehandle
293 my $handle = IO::File->new($file);
294 $pm_info = Module::Metadata->new_from_handle( $handle, $file );
295 ok( defined( $pm_info ), 'new_from_handle() succeeds' );
296 $pm_info = Module::Metadata->new_from_handle( $handle );
297 is( $pm_info, undef, "new_from_handle() without filename returns undef" );
298 close($handle);
299
300 # construct from module name, using custom include path
301 $pm_info = Module::Metadata->new_from_module(
302              $dist->name, inc => [ 'lib', @INC ] );
303 ok( defined( $pm_info ), 'new_from_module() succeeds' );
304
305
306 foreach my $module ( sort keys %modules ) {
307     my $expected = $modules{$module};
308  SKIP: {
309     skip( "No our() support until perl 5.6", 2 )
310         if $] < 5.006 && $module =~ /\bour\b/;
311     skip( "No package NAME VERSION support until perl 5.11.1", 2 )
312         if $] < 5.011001 && $module =~ /package\s+[\w\:\']+\s+v?[0-9._]+/;
313
314     $dist->change_file( 'lib/Simple.pm', $module );
315     $dist->regen;
316
317     my $warnings = '';
318     local $SIG{__WARN__} = sub { $warnings .= $_ for @_ };
319     my $pm_info = Module::Metadata->new_from_file( $file );
320
321     # Test::Builder will prematurely numify objects, so use this form
322     my $errs;
323     my $got = $pm_info->version;
324     if ( defined $expected ) {
325         ok( $got eq $expected,
326             "correct module version (expected '$expected')" )
327             or $errs++;
328     } else {
329         ok( !defined($got),
330             "correct module version (expected undef)" )
331             or $errs++;
332     }
333     is( $warnings, '', 'no warnings from parsing' ) or $errs++;
334     diag "Got: '$got'\nModule contents:\n$module" if $errs;
335   }
336 }
337
338 # revert to pristine state
339 $dist->regen( clean => 1 );
340
341 foreach my $pkg_name ( sort keys %pkg_names ) {
342     my $expected = $pkg_names{$pkg_name};
343
344     $dist->change_file( 'lib/Simple.pm', $pkg_name );
345     $dist->regen;
346
347     my $warnings = '';
348     local $SIG{__WARN__} = sub { $warnings .= $_ for @_ };
349     my $pm_info = Module::Metadata->new_from_file( $file );
350
351     # Test::Builder will prematurely numify objects, so use this form
352     my $errs;
353     my @got = $pm_info->packages_inside();
354     is_deeply( \@got, $expected,
355                "correct package names (expected '" . join(', ', @$expected) . "')" )
356             or $errs++;
357     is( $warnings, '', 'no warnings from parsing' ) or $errs++;
358     diag "Got: '" . join(', ', @got) . "'\nModule contents:\n$pkg_name" if $errs;
359 }
360
361 # revert to pristine state
362 $dist->regen( clean => 1 );
363
364 # Find each package only once
365 $dist->change_file( 'lib/Simple.pm', <<'---' );
366 package Simple;
367 $VERSION = '1.23';
368 package Error::Simple;
369 $VERSION = '2.34';
370 package Simple;
371 ---
372
373 $dist->regen;
374
375 $pm_info = Module::Metadata->new_from_file( $file );
376
377 my @packages = $pm_info->packages_inside;
378 is( @packages, 2, 'record only one occurence of each package' );
379
380
381 # Module 'Simple.pm' does not contain package 'Simple';
382 # constructor should not complain, no default module name or version
383 $dist->change_file( 'lib/Simple.pm', <<'---' );
384 package Simple::Not;
385 $VERSION = '1.23';
386 ---
387
388 $dist->regen;
389 $pm_info = Module::Metadata->new_from_file( $file );
390
391 is( $pm_info->name, undef, 'no default package' );
392 is( $pm_info->version, undef, 'no version w/o default package' );
393
394 # Module 'Simple.pm' contains an alpha version
395 # constructor should report first $VERSION found
396 $dist->change_file( 'lib/Simple.pm', <<'---' );
397 package Simple;
398 $VERSION = '1.23_01';
399 $VERSION = eval $VERSION;
400 ---
401
402 $dist->regen;
403 $pm_info = Module::Metadata->new_from_file( $file );
404
405 is( $pm_info->version, '1.23_01', 'alpha version reported');
406
407 # NOTE the following test has be done this way because Test::Builder is
408 # too smart for our own good and tries to see if the version object is a
409 # dual-var, which breaks with alpha versions:
410 #    Argument "1.23_0100" isn't numeric in addition (+) at
411 #    /usr/lib/perl5/5.8.7/Test/Builder.pm line 505.
412
413 ok( $pm_info->version > 1.23, 'alpha version greater than non');
414
415 # revert to pristine state
416 $dist->regen( clean => 1 );
417
418 # parse $VERSION lines scripts for package main
419 my @scripts = (
420   <<'---', # package main declared
421 #!perl -w
422 package main;
423 $VERSION = '0.01';
424 ---
425   <<'---', # on first non-comment line, non declared package main
426 #!perl -w
427 $VERSION = '0.01';
428 ---
429   <<'---', # after non-comment line
430 #!perl -w
431 use strict;
432 $VERSION = '0.01';
433 ---
434   <<'---', # 1st declared package
435 #!perl -w
436 package main;
437 $VERSION = '0.01';
438 package _private;
439 $VERSION = '999';
440 ---
441   <<'---', # 2nd declared package
442 #!perl -w
443 package _private;
444 $VERSION = '999';
445 package main;
446 $VERSION = '0.01';
447 ---
448   <<'---', # split package
449 #!perl -w
450 package main;
451 package _private;
452 $VERSION = '999';
453 package main;
454 $VERSION = '0.01';
455 ---
456   <<'---', # define 'main' version from other package
457 package _private;
458 $::VERSION = 0.01;
459 $VERSION = '999';
460 ---
461   <<'---', # define 'main' version from other package
462 package _private;
463 $VERSION = '999';
464 $::VERSION = 0.01;
465 ---
466 );
467
468 my ( $i, $n ) = ( 1, scalar( @scripts ) );
469 foreach my $script ( @scripts ) {
470   $dist->change_file( 'bin/simple.plx', $script );
471   $dist->regen;
472   $pm_info = Module::Metadata->new_from_file(
473                File::Spec->catfile( 'bin', 'simple.plx' ) );
474
475   is( $pm_info->version, '0.01', "correct script version ($i of $n)" );
476   $i++;
477 }
478
479
480 # examine properties of a module: name, pod, etc
481 $dist->change_file( 'lib/Simple.pm', <<'---' );
482 package Simple;
483 $VERSION = '0.01';
484 package Simple::Ex;
485 $VERSION = '0.02';
486
487 =head1 NAME
488
489 Simple - It's easy.
490
491 =head1 AUTHOR
492
493 Simple Simon
494
495 You can find me on the IRC channel
496 #simon on irc.perl.org.
497
498 =cut
499 ---
500 $dist->regen;
501
502 $pm_info = Module::Metadata->new_from_module(
503              $dist->name, inc => [ 'lib', @INC ] );
504
505 is( $pm_info->name, 'Simple', 'found default package' );
506 is( $pm_info->version, '0.01', 'version for default package' );
507
508 # got correct version for secondary package
509 is( $pm_info->version( 'Simple::Ex' ), '0.02',
510     'version for secondary package' );
511
512 my $filename = $pm_info->filename;
513 ok( defined( $filename ) && -e $filename,
514     'filename() returns valid path to module file' );
515
516 @packages = $pm_info->packages_inside;
517 is( @packages, 2, 'found correct number of packages' );
518 is( $packages[0], 'Simple', 'packages stored in order found' );
519
520 # we can detect presence of pod regardless of whether we are collecting it
521 ok( $pm_info->contains_pod, 'contains_pod() succeeds' );
522
523 my @pod = $pm_info->pod_inside;
524 is_deeply( \@pod, [qw(NAME AUTHOR)], 'found all pod sections' );
525
526 is( $pm_info->pod('NONE') , undef,
527     'return undef() if pod section not present' );
528
529 is( $pm_info->pod('NAME'), undef,
530     'return undef() if pod section not collected' );
531
532
533 # collect_pod
534 $pm_info = Module::Metadata->new_from_module(
535              $dist->name, inc => [ 'lib', @INC ], collect_pod => 1 );
536
537 {
538   my %pod;
539   for my $section (qw(NAME AUTHOR)) {
540     my $content = $pm_info->pod( $section );
541     if ( $content ) {
542       $content =~ s/^\s+//;
543       $content =~ s/\s+$//;
544     }
545     $pod{$section} = $content;
546   }
547   my %expected = (
548     NAME   => q|Simple - It's easy.|,
549     AUTHOR => <<'EXPECTED'
550 Simple Simon
551
552 You can find me on the IRC channel
553 #simon on irc.perl.org.
554 EXPECTED
555   );
556   for my $text (values %expected) {
557     $text =~ s/^\s+//;
558     $text =~ s/\s+$//;
559   }
560   is( $pod{NAME},   $expected{NAME},   'collected NAME pod section' );
561   is( $pod{AUTHOR}, $expected{AUTHOR}, 'collected AUTHOR pod section' );
562 }
563
564 {
565   # test things that look like POD, but aren't
566 $dist->change_file( 'lib/Simple.pm', <<'---' );
567 package Simple;
568
569 =YES THIS STARTS POD
570
571 our $VERSION = '999';
572
573 =cute
574
575 our $VERSION = '666';
576
577 =cut
578
579 *foo
580 =*no_this_does_not_start_pod;
581
582 our $VERSION = '1.23';
583
584 ---
585   $dist->regen;
586   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
587   is( $pm_info->name, 'Simple', 'found default package' );
588   is( $pm_info->version, '1.23', 'version for default package' );
589 }
590
591 {
592   # Make sure processing stops after __DATA__
593   $dist->change_file( 'lib/Simple.pm', <<'---' );
594 package Simple;
595 $VERSION = '0.01';
596 __DATA__
597 *UNIVERSAL::VERSION = sub {
598   foo();
599 };
600 ---
601   $dist->regen;
602
603   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
604   is( $pm_info->name, 'Simple', 'found default package' );
605   is( $pm_info->version, '0.01', 'version for default package' );
606   my @packages = $pm_info->packages_inside;
607   is_deeply(\@packages, ['Simple'], 'packages inside');
608 }
609
610 {
611   # Make sure we handle version.pm $VERSIONs well
612   $dist->change_file( 'lib/Simple.pm', <<'---' );
613 package Simple;
614 $VERSION = version->new('0.60.' . (qw$Revision: 128 $)[1]);
615 package Simple::Simon;
616 $VERSION = version->new('0.61.' . (qw$Revision: 129 $)[1]);
617 ---
618   $dist->regen;
619
620   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
621   is( $pm_info->name, 'Simple', 'found default package' );
622   is( $pm_info->version, '0.60.128', 'version for default package' );
623   my @packages = $pm_info->packages_inside;
624   is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
625   is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
626 }
627
628 # check that package_versions_from_directory works
629
630 $dist->change_file( 'lib/Simple.pm', <<'---' );
631 package Simple;
632 $VERSION = '0.01';
633 package Simple::Ex;
634 $VERSION = '0.02';
635 {
636   package main; # should ignore this
637 }
638 {
639   package DB; # should ignore this
640 }
641 {
642   package Simple::_private; # should ignore this
643 }
644
645 =head1 NAME
646
647 Simple - It's easy.
648
649 =head1 AUTHOR
650
651 Simple Simon
652
653 =cut
654 ---
655 $dist->regen;
656
657 my $exp_pvfd = {
658   'Simple' => {
659     'file' => 'Simple.pm',
660     'version' => '0.01'
661   },
662   'Simple::Ex' => {
663     'file' => 'Simple.pm',
664     'version' => '0.02'
665   }
666 };
667
668 my $got_pvfd = Module::Metadata->package_versions_from_directory('lib');
669
670 is_deeply( $got_pvfd, $exp_pvfd, "package_version_from_directory()" )
671   or diag explain $got_pvfd;
672
673 {
674   my $got_provides = Module::Metadata->provides(dir => 'lib', version => 2);
675   my $exp_provides = {
676     'Simple' => {
677       'file' => 'lib/Simple.pm',
678       'version' => '0.01'
679     },
680     'Simple::Ex' => {
681       'file' => 'lib/Simple.pm',
682       'version' => '0.02'
683     }
684   };
685
686   is_deeply( $got_provides, $exp_provides, "provides()" )
687     or diag explain $got_provides;
688 }
689
690 {
691   my $got_provides = Module::Metadata->provides(dir => 'lib', prefix => 'other', version => 1.4);
692   my $exp_provides = {
693     'Simple' => {
694       'file' => 'other/Simple.pm',
695       'version' => '0.01'
696     },
697     'Simple::Ex' => {
698       'file' => 'other/Simple.pm',
699       'version' => '0.02'
700     }
701   };
702
703   is_deeply( $got_provides, $exp_provides, "provides()" )
704     or diag explain $got_provides;
705 }
706
707 # Check package_versions_from_directory with regard to case-sensitivity
708 {
709   $dist->change_file( 'lib/Simple.pm', <<'---' );
710 package simple;
711 $VERSION = '0.01';
712 ---
713   $dist->regen;
714
715   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
716   is( $pm_info->name, undef, 'no default package' );
717   is( $pm_info->version, undef, 'version for default package' );
718   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
719   is( $pm_info->version('Simple'), undef, 'version for capitalized package' );
720
721   $dist->change_file( 'lib/Simple.pm', <<'---' );
722 package simple;
723 $VERSION = '0.01';
724 package Simple;
725 $VERSION = '0.02';
726 package SiMpLe;
727 $VERSION = '0.03';
728 ---
729   $dist->regen;
730
731   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
732   is( $pm_info->name, 'Simple', 'found default package' );
733   is( $pm_info->version, '0.02', 'version for default package' );
734   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
735   is( $pm_info->version('Simple'), '0.02', 'version for capitalized package' );
736   is( $pm_info->version('SiMpLe'), '0.03', 'version for mixed-case package' );
737 }