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