b5eda173f7aafe08d08785c13488cf84720c1ad1
[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' => <<'---', # use vars
32 package Simple;
33 use vars qw( $VERSION );
34 $VERSION = '1.23';
35 ---
36   '1.23' => <<'---', # choose the right default package based on package/file name
37 package Simple::_private;
38 $VERSION = '0';
39 package Simple;
40 $VERSION = '1.23'; # this should be chosen for version
41 ---
42   '1.23' => <<'---', # just read the first $VERSION line
43 package Simple;
44 $VERSION = '1.23'; # we should see this line
45 $VERSION = eval $VERSION; # and ignore this one
46 ---
47   '1.23' => <<'---', # just read the first $VERSION line in reopened package (1)
48 package Simple;
49 $VERSION = '1.23';
50 package Error::Simple;
51 $VERSION = '2.34';
52 package Simple;
53 ---
54   '1.23' => <<'---', # just read the first $VERSION line in reopened package (2)
55 package Simple;
56 package Error::Simple;
57 $VERSION = '2.34';
58 package Simple;
59 $VERSION = '1.23';
60 ---
61   '1.23' => <<'---', # mentions another module's $VERSION
62 package Simple;
63 $VERSION = '1.23';
64 if ( $Other::VERSION ) {
65     # whatever
66 }
67 ---
68   '1.23' => <<'---', # mentions another module's $VERSION in a different package
69 package Simple;
70 $VERSION = '1.23';
71 package Simple2;
72 if ( $Simple::VERSION ) {
73     # whatever
74 }
75 ---
76   '1.23' => <<'---', # $VERSION checked only in assignments, not regexp ops
77 package Simple;
78 $VERSION = '1.23';
79 if ( $VERSION =~ /1\.23/ ) {
80     # whatever
81 }
82 ---
83   '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
84 package Simple;
85 $VERSION = '1.23';
86 if ( $VERSION == 3.45 ) {
87     # whatever
88 }
89 ---
90   '1.23' => <<'---', # $VERSION checked only in assignments, not relational ops
91 package Simple;
92 $VERSION = '1.23';
93 package Simple2;
94 if ( $Simple::VERSION == 3.45 ) {
95     # whatever
96 }
97 ---
98   '1.23' => <<'---', # Fully qualified $VERSION declared in package
99 package Simple;
100 $Simple::VERSION = 1.23;
101 ---
102   '1.23' => <<'---', # Differentiate fully qualified $VERSION in a package
103 package Simple;
104 $Simple2::VERSION = '999';
105 $Simple::VERSION = 1.23;
106 ---
107   '1.23' => <<'---', # Differentiate fully qualified $VERSION and unqualified
108 package Simple;
109 $Simple2::VERSION = '999';
110 $VERSION = 1.23;
111 ---
112   '1.23' => <<'---', # $VERSION declared as package variable from within 'main' package
113 $Simple::VERSION = '1.23';
114 {
115   package Simple;
116   $x = $y, $cats = $dogs;
117 }
118 ---
119   '1.23' => <<'---', # $VERSION wrapped in parens - space inside
120 package Simple;
121 ( $VERSION ) = '1.23';
122 ---
123   '1.23' => <<'---', # $VERSION wrapped in parens - no space inside
124 package Simple;
125 ($VERSION) = '1.23';
126 ---
127   '1.23' => <<'---', # $VERSION follows a spurious 'package' in a quoted construct
128 package Simple;
129 __PACKAGE__->mk_accessors(qw(
130     program socket proc
131     package filename line codeline subroutine finished));
132
133 our $VERSION = "1.23";
134 ---
135   '1.23' => <<'---', # $VERSION using version.pm
136   package Simple;
137   use version; our $VERSION = version->new('1.23');
138 ---
139   '1.23' => <<'---', # $VERSION using version.pm and qv()
140   package Simple;
141   use version; our $VERSION = qv('1.230');
142 ---
143   '1.23' => <<'---', # Two version assignments, should ignore second one
144   $Simple::VERSION = '1.230';
145   $Simple::VERSION = eval $Simple::VERSION;
146 ---
147   '1.23' => <<'---', # declared & defined on same line with 'our'
148 package Simple;
149 our $VERSION = '1.23_00_00';
150 ---
151   '1.23' => <<'---', # package NAME VERSION
152   package Simple 1.23;
153 ---
154   '1.23_01' => <<'---', # package NAME VERSION
155   package Simple 1.23_01;
156 ---
157   'v1.2.3' => <<'---', # package NAME VERSION
158   package Simple v1.2.3;
159 ---
160   'v1.2_3' => <<'---', # package NAME VERSION
161   package Simple v1.2_3;
162 ---
163   '1.23' => <<'---', # trailing crud
164   package Simple;
165   our $VERSION;
166   $VERSION = '1.23-alpha';
167 ---
168   '1.23' => <<'---', # trailing crud
169   package Simple;
170   our $VERSION;
171   $VERSION = '1.23b';
172 ---
173   '1.234' => <<'---', # multi_underscore
174   package Simple;
175   our $VERSION;
176   $VERSION = '1.2_3_4';
177 ---
178   '0' => <<'---', # non-numeric
179   package Simple;
180   our $VERSION;
181   $VERSION = 'onetwothree';
182 ---
183 );
184 my %modules = reverse @modules;
185
186 plan tests => 39 + 2 * keys( %modules );
187
188 require_ok('Module::Metadata');
189
190 # class method C<find_module_by_name>
191 my $module = Module::Metadata->find_module_by_name(
192                'Module::Metadata' );
193 ok( -e $module, 'find_module_by_name() succeeds' );
194
195 #########################
196
197 my $tmp = MBTest->tmpdir;
198
199 use DistGen;
200 my $dist = DistGen->new( dir => $tmp );
201 $dist->regen;
202
203 $dist->chdir_in;
204
205
206 # fail on invalid module name
207 my $pm_info = Module::Metadata->new_from_module(
208                 'Foo::Bar', inc => [] );
209 ok( !defined( $pm_info ), 'fail if can\'t find module by module name' );
210
211
212 # fail on invalid filename
213 my $file = File::Spec->catfile( 'Foo', 'Bar.pm' );
214 $pm_info = Module::Metadata->new_from_file( $file, inc => [] );
215 ok( !defined( $pm_info ), 'fail if can\'t find module by file name' );
216
217
218 # construct from module filename
219 $file = File::Spec->catfile( 'lib', split( /::/, $dist->name ) ) . '.pm';
220 $pm_info = Module::Metadata->new_from_file( $file );
221 ok( defined( $pm_info ), 'new_from_file() succeeds' );
222
223 # construct from filehandle
224 my $handle = IO::File->new($file);
225 $pm_info = Module::Metadata->new_from_handle( $handle, $file );
226 ok( defined( $pm_info ), 'new_from_handle() succeeds' );
227 $pm_info = Module::Metadata->new_from_handle( $handle );
228 is( $pm_info, undef, "new_from_handle() without filename returns undef" );
229
230
231 # construct from module name, using custom include path
232 $pm_info = Module::Metadata->new_from_module(
233              $dist->name, inc => [ 'lib', @INC ] );
234 ok( defined( $pm_info ), 'new_from_module() succeeds' );
235
236
237 foreach my $module ( sort keys %modules ) {
238     my $expected = $modules{$module};
239  SKIP: {
240     skip( "No our() support until perl 5.6", 2 )
241         if $] < 5.006 && $module =~ /\bour\b/;
242     skip( "No package NAME VERSION support until perl 5.11.1", 2 )
243         if $] < 5.011001 && $module =~ /package\s+[\w\:\']+\s+v?[0-9._]+/;
244
245     $dist->change_file( 'lib/Simple.pm', $module );
246     $dist->regen;
247
248     my $warnings = '';
249     local $SIG{__WARN__} = sub { $warnings .= $_ for @_ };
250     my $pm_info = Module::Metadata->new_from_file( $file );
251
252     # Test::Builder will prematurely numify objects, so use this form
253     my $errs;
254     my $got = $pm_info->version;
255     if ( defined $expected ) {
256         ok( $got eq $expected,
257             "correct module version (expected '$expected')" )
258             or $errs++;
259     } else {
260         ok( !defined($got),
261             "correct module version (expected undef)" )
262             or $errs++;
263     }
264     is( $warnings, '', 'no warnings from parsing' ) or $errs++;
265     diag "Got: '$got'\nModule contents:\n$module" if $errs;
266   }
267 }
268
269 # revert to pristine state
270 $dist->regen( clean => 1 );
271
272 # Find each package only once
273 $dist->change_file( 'lib/Simple.pm', <<'---' );
274 package Simple;
275 $VERSION = '1.23';
276 package Error::Simple;
277 $VERSION = '2.34';
278 package Simple;
279 ---
280
281 $dist->regen;
282
283 $pm_info = Module::Metadata->new_from_file( $file );
284
285 my @packages = $pm_info->packages_inside;
286 is( @packages, 2, 'record only one occurence of each package' );
287
288
289 # Module 'Simple.pm' does not contain package 'Simple';
290 # constructor should not complain, no default module name or version
291 $dist->change_file( 'lib/Simple.pm', <<'---' );
292 package Simple::Not;
293 $VERSION = '1.23';
294 ---
295
296 $dist->regen;
297 $pm_info = Module::Metadata->new_from_file( $file );
298
299 is( $pm_info->name, undef, 'no default package' );
300 is( $pm_info->version, undef, 'no version w/o default package' );
301
302 # Module 'Simple.pm' contains an alpha version
303 # constructor should report first $VERSION found
304 $dist->change_file( 'lib/Simple.pm', <<'---' );
305 package Simple;
306 $VERSION = '1.23_01';
307 $VERSION = eval $VERSION;
308 ---
309
310 $dist->regen;
311 $pm_info = Module::Metadata->new_from_file( $file );
312
313 is( $pm_info->version, '1.23_01', 'alpha version reported');
314
315 # NOTE the following test has be done this way because Test::Builder is
316 # too smart for our own good and tries to see if the version object is a
317 # dual-var, which breaks with alpha versions:
318 #    Argument "1.23_0100" isn't numeric in addition (+) at
319 #    /usr/lib/perl5/5.8.7/Test/Builder.pm line 505.
320
321 ok( $pm_info->version > 1.23, 'alpha version greater than non');
322
323 # revert to pristine state
324 $dist->regen( clean => 1 );
325
326 # parse $VERSION lines scripts for package main
327 my @scripts = (
328   <<'---', # package main declared
329 #!perl -w
330 package main;
331 $VERSION = '0.01';
332 ---
333   <<'---', # on first non-comment line, non declared package main
334 #!perl -w
335 $VERSION = '0.01';
336 ---
337   <<'---', # after non-comment line
338 #!perl -w
339 use strict;
340 $VERSION = '0.01';
341 ---
342   <<'---', # 1st declared package
343 #!perl -w
344 package main;
345 $VERSION = '0.01';
346 package _private;
347 $VERSION = '999';
348 ---
349   <<'---', # 2nd declared package
350 #!perl -w
351 package _private;
352 $VERSION = '999';
353 package main;
354 $VERSION = '0.01';
355 ---
356   <<'---', # split package
357 #!perl -w
358 package main;
359 package _private;
360 $VERSION = '999';
361 package main;
362 $VERSION = '0.01';
363 ---
364   <<'---', # define 'main' version from other package
365 package _private;
366 $::VERSION = 0.01;
367 $VERSION = '999';
368 ---
369   <<'---', # define 'main' version from other package
370 package _private;
371 $VERSION = '999';
372 $::VERSION = 0.01;
373 ---
374 );
375
376 my ( $i, $n ) = ( 1, scalar( @scripts ) );
377 foreach my $script ( @scripts ) {
378   $dist->change_file( 'bin/simple.plx', $script );
379   $dist->regen;
380   $pm_info = Module::Metadata->new_from_file(
381                File::Spec->catfile( 'bin', 'simple.plx' ) );
382
383   is( $pm_info->version, '0.01', "correct script version ($i of $n)" );
384   $i++;
385 }
386
387
388 # examine properties of a module: name, pod, etc
389 $dist->change_file( 'lib/Simple.pm', <<'---' );
390 package Simple;
391 $VERSION = '0.01';
392 package Simple::Ex;
393 $VERSION = '0.02';
394 =head1 NAME
395
396 Simple - It's easy.
397
398 =head1 AUTHOR
399
400 Simple Simon
401
402 =cut
403 ---
404 $dist->regen;
405
406 $pm_info = Module::Metadata->new_from_module(
407              $dist->name, inc => [ 'lib', @INC ] );
408
409 is( $pm_info->name, 'Simple', 'found default package' );
410 is( $pm_info->version, '0.01', 'version for default package' );
411
412 # got correct version for secondary package
413 is( $pm_info->version( 'Simple::Ex' ), '0.02',
414     'version for secondary package' );
415
416 my $filename = $pm_info->filename;
417 ok( defined( $filename ) && -e $filename,
418     'filename() returns valid path to module file' );
419
420 @packages = $pm_info->packages_inside;
421 is( @packages, 2, 'found correct number of packages' );
422 is( $packages[0], 'Simple', 'packages stored in order found' );
423
424 # we can detect presence of pod regardless of whether we are collecting it
425 ok( $pm_info->contains_pod, 'contains_pod() succeeds' );
426
427 my @pod = $pm_info->pod_inside;
428 is_deeply( \@pod, [qw(NAME AUTHOR)], 'found all pod sections' );
429
430 is( $pm_info->pod('NONE') , undef,
431     'return undef() if pod section not present' );
432
433 is( $pm_info->pod('NAME'), undef,
434     'return undef() if pod section not collected' );
435
436
437 # collect_pod
438 $pm_info = Module::Metadata->new_from_module(
439              $dist->name, inc => [ 'lib', @INC ], collect_pod => 1 );
440
441 my $name = $pm_info->pod('NAME');
442 if ( $name ) {
443   $name =~ s/^\s+//;
444   $name =~ s/\s+$//;
445 }
446 is( $name, q|Simple - It's easy.|, 'collected pod section' );
447
448
449 {
450   # Make sure processing stops after __DATA__
451   $dist->change_file( 'lib/Simple.pm', <<'---' );
452 package Simple;
453 $VERSION = '0.01';
454 __DATA__
455 *UNIVERSAL::VERSION = sub {
456   foo();
457 };
458 ---
459   $dist->regen;
460
461   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
462   is( $pm_info->name, 'Simple', 'found default package' );
463   is( $pm_info->version, '0.01', 'version for default package' );
464   my @packages = $pm_info->packages_inside;
465   is_deeply(\@packages, ['Simple'], 'packages inside');
466 }
467
468 {
469   # Make sure we handle version.pm $VERSIONs well
470   $dist->change_file( 'lib/Simple.pm', <<'---' );
471 package Simple;
472 $VERSION = version->new('0.60.' . (qw$Revision: 128 $)[1]);
473 package Simple::Simon;
474 $VERSION = version->new('0.61.' . (qw$Revision: 129 $)[1]);
475 ---
476   $dist->regen;
477
478   $pm_info = Module::Metadata->new_from_file('lib/Simple.pm');
479   is( $pm_info->name, 'Simple', 'found default package' );
480   is( $pm_info->version, '0.60.128', 'version for default package' );
481   my @packages = $pm_info->packages_inside;
482   is_deeply([sort @packages], ['Simple', 'Simple::Simon'], 'packages inside');
483   is( $pm_info->version('Simple::Simon'), '0.61.129', 'version for embedded package' );
484 }
485