xref: /openbsd-src/gnu/usr.bin/perl/cpan/Module-Metadata/t/metadata.t (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
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 lib 't/lib';
7use IO::File;
8use MBTest;
9
10my $undef;
11
12# parse various module $VERSION lines
13# these will be reversed later to create %modules
14my @modules = (
15  $undef => <<'---', # no $VERSION line
16package Simple;
17---
18  $undef => <<'---', # undefined $VERSION
19package Simple;
20our $VERSION;
21---
22  '1.23' => <<'---', # declared & defined on same line with 'our'
23package Simple;
24our $VERSION = '1.23';
25---
26  '1.23' => <<'---', # declared & defined on separate lines with 'our'
27package Simple;
28our $VERSION;
29$VERSION = '1.23';
30---
31  '1.23' => <<'---', # commented & defined on same line
32package Simple;
33our $VERSION = '1.23'; # our $VERSION = '4.56';
34---
35  '1.23' => <<'---', # commented & defined on separate lines
36package Simple;
37# our $VERSION = '4.56';
38our $VERSION = '1.23';
39---
40  '1.23' => <<'---', # use vars
41package Simple;
42use vars qw( $VERSION );
43$VERSION = '1.23';
44---
45  '1.23' => <<'---', # choose the right default package based on package/file name
46package Simple::_private;
47$VERSION = '0';
48package Simple;
49$VERSION = '1.23'; # this should be chosen for version
50---
51  '1.23' => <<'---', # just read the first $VERSION line
52package 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)
57package Simple;
58$VERSION = '1.23';
59package Error::Simple;
60$VERSION = '2.34';
61package Simple;
62---
63  '1.23' => <<'---', # just read the first $VERSION line in reopened package (2)
64package Simple;
65package Error::Simple;
66$VERSION = '2.34';
67package Simple;
68$VERSION = '1.23';
69---
70  '1.23' => <<'---', # mentions another module's $VERSION
71package Simple;
72$VERSION = '1.23';
73if ( $Other::VERSION ) {
74    # whatever
75}
76---
77  '1.23' => <<'---', # mentions another module's $VERSION in a different package
78package Simple;
79$VERSION = '1.23';
80package Simple2;
81if ( $Simple::VERSION ) {
82    # whatever
83}
84---
85  '1.23' => <<'---', # $VERSION checked only in assignments, not regexp ops
86package Simple;
87$VERSION = '1.23';
88if ( $VERSION =~ /1\.23/ ) {
89    # whatever
90}
91---
92  '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
93package Simple;
94$VERSION = '1.23';
95if ( $VERSION == 3.45 ) {
96    # whatever
97}
98---
99  '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
100package Simple;
101$VERSION = '1.23';
102package Simple2;
103if ( $Simple::VERSION == 3.45 ) {
104    # whatever
105}
106---
107  '1.23' => <<'---', # Fully qualified $VERSION declared in package
108package Simple;
109$Simple::VERSION = 1.23;
110---
111  '1.23' => <<'---', # Differentiate fully qualified $VERSION in a package
112package Simple;
113$Simple2::VERSION = '999';
114$Simple::VERSION = 1.23;
115---
116  '1.23' => <<'---', # Differentiate fully qualified $VERSION and unqualified
117package 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
129package Simple;
130( $VERSION ) = '1.23';
131---
132  '1.23' => <<'---', # $VERSION wrapped in parens - no space inside
133package Simple;
134($VERSION) = '1.23';
135---
136  '1.23' => <<'---', # $VERSION follows a spurious 'package' in a quoted construct
137package Simple;
138__PACKAGE__->mk_accessors(qw(
139    program socket proc
140    package filename line codeline subroutine finished));
141
142our $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'
157package Simple;
158our $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
193package Simple {
194  our $VERSION;
195}
196---
197  '1.23' => <<'---', # package NAME BLOCK, with $VERSION
198package Simple {
199  our $VERSION = '1.23';
200}
201---
202  '1.23' => <<'---', # package NAME VERSION BLOCK
203package Simple 1.23 {
204  1;
205}
206---
207  'v1.2.3_4' => <<'---', # package NAME VERSION BLOCK
208package Simple v1.2.3_4 {
209  1;
210}
211---
212);
213my %modules = reverse @modules;
214
215plan tests => 54 + 2 * keys( %modules );
216
217require_ok('Module::Metadata');
218
219# class method C<find_module_by_name>
220my $module = Module::Metadata->find_module_by_name(
221               'Module::Metadata' );
222ok( -e $module, 'find_module_by_name() succeeds' );
223
224#########################
225
226my $tmp = MBTest->tmpdir;
227
228use DistGen;
229my $dist = DistGen->new( dir => $tmp );
230$dist->regen;
231
232$dist->chdir_in;
233
234
235# fail on invalid module name
236my $pm_info = Module::Metadata->new_from_module(
237		'Foo::Bar', inc => [] );
238ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
239
240
241# fail on invalid filename
242my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
243$pm_info = Module::Metadata->new_from_file( $file, inc => [] );
244ok( !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 );
250ok( defined( $pm_info ), 'new_from_file() succeeds' );
251
252# construct from filehandle
253my $handle = IO::File->new($file);
254$pm_info = Module::Metadata->new_from_handle( $handle, $file );
255ok( defined( $pm_info ), 'new_from_handle() succeeds' );
256$pm_info = Module::Metadata->new_from_handle( $handle );
257is( $pm_info, undef, "new_from_handle() without filename returns undef" );
258close($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 ] );
263ok( defined( $pm_info ), 'new_from_module() succeeds' );
264
265
266foreach 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', <<'---' );
303package Simple;
304$VERSION = '1.23';
305package Error::Simple;
306$VERSION = '2.34';
307package Simple;
308---
309
310$dist->regen;
311
312$pm_info = Module::Metadata->new_from_file( $file );
313
314my @packages = $pm_info->packages_inside;
315is( @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', <<'---' );
321package Simple::Not;
322$VERSION = '1.23';
323---
324
325$dist->regen;
326$pm_info = Module::Metadata->new_from_file( $file );
327
328is( $pm_info->name, undef, 'no default package' );
329is( $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', <<'---' );
334package 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
342is( $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
350ok( $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
356my @scripts = (
357  <<'---', # package main declared
358#!perl -w
359package 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
368use strict;
369$VERSION = '0.01';
370---
371  <<'---', # 1st declared package
372#!perl -w
373package main;
374$VERSION = '0.01';
375package _private;
376$VERSION = '999';
377---
378  <<'---', # 2nd declared package
379#!perl -w
380package _private;
381$VERSION = '999';
382package main;
383$VERSION = '0.01';
384---
385  <<'---', # split package
386#!perl -w
387package main;
388package _private;
389$VERSION = '999';
390package main;
391$VERSION = '0.01';
392---
393  <<'---', # define 'main' version from other package
394package _private;
395$::VERSION = 0.01;
396$VERSION = '999';
397---
398  <<'---', # define 'main' version from other package
399package _private;
400$VERSION = '999';
401$::VERSION = 0.01;
402---
403);
404
405my ( $i, $n ) = ( 1, scalar( @scripts ) );
406foreach 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', <<'---' );
419package Simple;
420$VERSION = '0.01';
421package Simple::Ex;
422$VERSION = '0.02';
423
424=head1 NAME
425
426Simple - It's easy.
427
428=head1 AUTHOR
429
430Simple Simon
431
432You 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
442is( $pm_info->name, 'Simple', 'found default package' );
443is( $pm_info->version, '0.01', 'version for default package' );
444
445# got correct version for secondary package
446is( $pm_info->version( 'Simple::Ex' ), '0.02',
447    'version for secondary package' );
448
449my $filename = $pm_info->filename;
450ok( defined( $filename ) && -e $filename,
451    'filename() returns valid path to module file' );
452
453@packages = $pm_info->packages_inside;
454is( @packages, 2, 'found correct number of packages' );
455is( $packages[0], 'Simple', 'packages stored in order found' );
456
457# we can detect presence of pod regardless of whether we are collecting it
458ok( $pm_info->contains_pod, 'contains_pod() succeeds' );
459
460my @pod = $pm_info->pod_inside;
461is_deeply( \@pod, [qw(NAME AUTHOR)], 'found all pod sections' );
462
463is( $pm_info->pod('NONE') , undef,
464    'return undef() if pod section not present' );
465
466is( $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'
487Simple Simon
488
489You can find me on the IRC channel
490#simon on irc.perl.org.
491EXPECTED
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', <<'---' );
504package Simple;
505
506=YES THIS STARTS POD
507
508our $VERSION = '999';
509
510=cute
511
512our $VERSION = '666';
513
514=cut
515
516*foo
517=*no_this_does_not_start_pod;
518
519our $VERSION = '1.23';
520
521---
522  $dist->regen;
523  $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
524  is( $pm_info->name, 'Simple', 'found default package' );
525  is( $pm_info->version, '1.23', 'version for default package' );
526}
527
528{
529  # Make sure processing stops after __DATA__
530  $dist->change_file( 'lib/Simple.pm', <<'---' );
531package Simple;
532$VERSION = '0.01';
533__DATA__
534*UNIVERSAL::VERSION = sub {
535  foo();
536};
537---
538  $dist->regen;
539
540  $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
541  is( $pm_info->name, 'Simple', 'found default package' );
542  is( $pm_info->version, '0.01', 'version for default package' );
543  my @packages = $pm_info->packages_inside;
544  is_deeply(\@packages, ['Simple'], 'packages inside');
545}
546
547{
548  # Make sure we handle version.pm $VERSIONs well
549  $dist->change_file( 'lib/Simple.pm', <<'---' );
550package Simple;
551$VERSION = version->new('0.60.' . (qw$Revision: 128 $)[1]);
552package Simple::Simon;
553$VERSION = version->new('0.61.' . (qw$Revision: 129 $)[1]);
554---
555  $dist->regen;
556
557  $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
558  is( $pm_info->name, 'Simple', 'found default package' );
559  is( $pm_info->version, '0.60.128', 'version for default package' );
560  my @packages = $pm_info->packages_inside;
561  is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
562  is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
563}
564
565# check that package_versions_from_directory works
566
567$dist->change_file( 'lib/Simple.pm', <<'---' );
568package Simple;
569$VERSION = '0.01';
570package Simple::Ex;
571$VERSION = '0.02';
572{
573  package main; # should ignore this
574}
575{
576  package DB; # should ignore this
577}
578{
579  package Simple::_private; # should ignore this
580}
581
582=head1 NAME
583
584Simple - It's easy.
585
586=head1 AUTHOR
587
588Simple Simon
589
590=cut
591---
592$dist->regen;
593
594my $exp_pvfd = {
595  'Simple' => {
596    'file' => 'Simple.pm',
597    'version' => '0.01'
598  },
599  'Simple::Ex' => {
600    'file' => 'Simple.pm',
601    'version' => '0.02'
602  }
603};
604
605my $got_pvfd = Module::Metadata->package_versions_from_directory('lib');
606
607is_deeply( $got_pvfd, $exp_pvfd, "package_version_from_directory()" )
608  or diag explain $got_pvfd;
609
610{
611  my $got_provides = Module::Metadata->provides(dir => 'lib', version => 2);
612  my $exp_provides = {
613    'Simple' => {
614      'file' => 'lib/Simple.pm',
615      'version' => '0.01'
616    },
617    'Simple::Ex' => {
618      'file' => 'lib/Simple.pm',
619      'version' => '0.02'
620    }
621  };
622
623  is_deeply( $got_provides, $exp_provides, "provides()" )
624    or diag explain $got_provides;
625}
626
627{
628  my $got_provides = Module::Metadata->provides(dir => 'lib', prefix => 'other', version => 1.4);
629  my $exp_provides = {
630    'Simple' => {
631      'file' => 'other/Simple.pm',
632      'version' => '0.01'
633    },
634    'Simple::Ex' => {
635      'file' => 'other/Simple.pm',
636      'version' => '0.02'
637    }
638  };
639
640  is_deeply( $got_provides, $exp_provides, "provides()" )
641    or diag explain $got_provides;
642}
643
644# Check package_versions_from_directory with regard to case-sensitivity
645{
646  $dist->change_file( 'lib/Simple.pm', <<'---' );
647package simple;
648$VERSION = '0.01';
649---
650  $dist->regen;
651
652  $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
653  is( $pm_info->name, undef, 'no default package' );
654  is( $pm_info->version, undef, 'version for default package' );
655  is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
656  is( $pm_info->version('Simple'), undef, 'version for capitalized package' );
657
658  $dist->change_file( 'lib/Simple.pm', <<'---' );
659package simple;
660$VERSION = '0.01';
661package Simple;
662$VERSION = '0.02';
663package SiMpLe;
664$VERSION = '0.03';
665---
666  $dist->regen;
667
668  $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
669  is( $pm_info->name, 'Simple', 'found default package' );
670  is( $pm_info->version, '0.02', 'version for default package' );
671  is( $pm_info->version('simple'), '0.01', 'version for lower-case package' );
672  is( $pm_info->version('Simple'), '0.02', 'version for capitalized package' );
673  is( $pm_info->version('SiMpLe'), '0.03', 'version for mixed-case package' );
674}
675