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