a0bcaaaa43cda29126345a3288a1e5aa1678c424
[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 => 54 + 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 You can find me on the IRC channel
433 #simon on irc.perl.org.
434
435 =cut
436 ---
437 $dist->regen;
438
439 $pm_info = Module::Metadata->new_from_module(
440              $dist->name, inc => [ 'lib', @INC ] );
441
442 is( $pm_info->name, 'Simple', 'found default package' );
443 is( $pm_info->version, '0.01', 'version for default package' );
444
445 # got correct version for secondary package
446 is( $pm_info->version( 'Simple::Ex' ), '0.02',
447     'version for secondary package' );
448
449 my $filename = $pm_info->filename;
450 ok( defined( $filename ) && -e $filename,
451     'filename() returns valid path to module file' );
452
453 @packages = $pm_info->packages_inside;
454 is( @packages, 2, 'found correct number of packages' );
455 is( $packages[0], 'Simple', 'packages stored in order found' );
456
457 # we can detect presence of pod regardless of whether we are collecting it
458 ok( $pm_info->contains_pod, 'contains_pod() succeeds' );
459
460 my @pod = $pm_info->pod_inside;
461 is_deeply( \@pod, [qw(NAME AUTHOR)], 'found all pod sections' );
462
463 is( $pm_info->pod('NONE') , undef,
464     'return undef() if pod section not present' );
465
466 is( $pm_info->pod('NAME'), undef,
467     'return undef() if pod section not collected' );
468
469
470 # collect_pod
471 $pm_info = Module::Metadata->new_from_module(
472              $dist->name, inc => [ 'lib', @INC ], collect_pod => 1 );
473
474 {
475   my %pod;
476   for my $section (qw(NAME AUTHOR)) {
477     my $content = $pm_info->pod( $section );
478     if ( $content ) {
479       $content =~ s/^\s+//;
480       $content =~ s/\s+$//;
481     }
482     $pod{$section} = $content;
483   }
484   my %expected = (
485     NAME   => q|Simple - It's easy.|,
486     AUTHOR => <<'EXPECTED'
487 Simple Simon
488
489 You can find me on the IRC channel
490 #simon on irc.perl.org.
491 EXPECTED
492   );
493   for my $text (values %expected) {
494     $text =~ s/^\s+//;
495     $text =~ s/\s+$//;
496   }
497   is( $pod{NAME},   $expected{NAME},   'collected NAME pod section' );
498   is( $pod{AUTHOR}, $expected{AUTHOR}, 'collected AUTHOR pod section' );
499 }
500
501 {
502   # test things that look like POD, but aren't
503 $dist->change_file( 'lib/Simple.pm', <<'---' );
504 package Simple;
505 sub podzol () { 1 }
506 sub cute () { 2 }
507 my $x
508 =podzol
509 ;
510
511 our $VERSION = '1.23';
512
513 my $y
514 =cute
515 ;
516
517 our $VERSION = '999';
518
519 ---
520   $dist->regen;
521   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
522   is( $pm_info->name, 'Simple', 'found default package' );
523   is( $pm_info->version, '1.23', 'version for default package' );
524 }
525
526 {
527   # Make sure processing stops after __DATA__
528   $dist->change_file( 'lib/Simple.pm', <<'---' );
529 package Simple;
530 $VERSION = '0.01';
531 __DATA__
532 *UNIVERSAL::VERSION = sub {
533   foo();
534 };
535 ---
536   $dist->regen;
537
538   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
539   is( $pm_info->name, 'Simple', 'found default package' );
540   is( $pm_info->version, '0.01', 'version for default package' );
541   my @packages = $pm_info->packages_inside;
542   is_deeply(\@packages, ['Simple'], 'packages inside');
543 }
544
545 {
546   # Make sure we handle version.pm $VERSIONs well
547   $dist->change_file( 'lib/Simple.pm', <<'---' );
548 package Simple;
549 $VERSION = version->new('0.60.' . (qw$Revision: 128 $)[1]);
550 package Simple::Simon;
551 $VERSION = version->new('0.61.' . (qw$Revision: 129 $)[1]);
552 ---
553   $dist->regen;
554
555   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
556   is( $pm_info->name, 'Simple', 'found default package' );
557   is( $pm_info->version, '0.60.128', 'version for default package' );
558   my @packages = $pm_info->packages_inside;
559   is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
560   is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
561 }
562
563 # check that package_versions_from_directory works
564
565 $dist->change_file( 'lib/Simple.pm', <<'---' );
566 package Simple;
567 $VERSION = '0.01';
568 package Simple::Ex;
569 $VERSION = '0.02';
570 {
571   package main; # should ignore this
572 }
573 {
574   package DB; # should ignore this
575 }
576 {
577   package Simple::_private; # should ignore this
578 }
579
580 =head1 NAME
581
582 Simple - It's easy.
583
584 =head1 AUTHOR
585
586 Simple Simon
587
588 =cut
589 ---
590 $dist->regen;
591
592 my $exp_pvfd = {
593   'Simple' => {
594     'file' => 'Simple.pm',
595     'version' => '0.01'
596   },
597   'Simple::Ex' => {
598     'file' => 'Simple.pm',
599     'version' => '0.02'
600   }
601 };
602
603 my $got_pvfd = Module::Metadata->package_versions_from_directory('lib');
604
605 is_deeply( $got_pvfd, $exp_pvfd, "package_version_from_directory()" )
606   or diag explain $got_pvfd;
607
608 {
609   my $got_provides = Module::Metadata->provides(dir => 'lib', version => 2);
610   my $exp_provides = {
611     'Simple' => {
612       'file' => 'lib/Simple.pm',
613       'version' => '0.01'
614     },
615     'Simple::Ex' => {
616       'file' => 'lib/Simple.pm',
617       'version' => '0.02'
618     }
619   };
620
621   is_deeply( $got_provides, $exp_provides, "provides()" )
622     or diag explain $got_provides;
623 }
624
625 {
626   my $got_provides = Module::Metadata->provides(dir => 'lib', prefix => 'other', version => 1.4);
627   my $exp_provides = {
628     'Simple' => {
629       'file' => 'other/Simple.pm',
630       'version' => '0.01'
631     },
632     'Simple::Ex' => {
633       'file' => 'other/Simple.pm',
634       'version' => '0.02'
635     }
636   };
637
638   is_deeply( $got_provides, $exp_provides, "provides()" )
639     or diag explain $got_provides;
640 }
641
642 # Check package_versions_from_directory with regard to case-sensitivity
643 {
644   $dist->change_file( 'lib/Simple.pm', <<'---' );
645 package simple;
646 $VERSION = '0.01';
647 ---
648   $dist->regen;
649
650   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
651   is( $pm_info->name, undef, 'no default package' );
652   is( $pm_info->version, undef, 'version for default package' );
653   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
654   is( $pm_info->version('Simple'), undef, 'version for capitalized package' );
655
656   $dist->change_file( 'lib/Simple.pm', <<'---' );
657 package simple;
658 $VERSION = '0.01';
659 package Simple;
660 $VERSION = '0.02';
661 package SiMpLe;
662 $VERSION = '0.03';
663 ---
664   $dist->regen;
665
666   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
667   is( $pm_info->name, 'Simple', 'found default package' );
668   is( $pm_info->version, '0.02', 'version for default package' );
669   is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
670   is( $pm_info->version('Simple'), '0.02', 'version for capitalized package' );
671   is( $pm_info->version('SiMpLe'), '0.03', 'version for mixed-case package' );
672 }