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