Integrate Parse::CPAN::Meta 0.04 into core
Steffen Mueller [Tue, 6 Jan 2009 18:49:01 +0000 (19:49 +0100)]
21 files changed:
MANIFEST
lib/Parse/CPAN/Meta.pm [new file with mode: 0644]
lib/Parse/CPAN/Meta/Changes [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/01_compile.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/02_basic.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/03_regression.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/04_scalar.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/11_meta_yml.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/12_plagger.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/13_perl_smith.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/14_yaml_org.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/15_multibyte.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/16_nullrefs.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/17_toolbar.t [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/HTML-WebDAO.yml [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/Template-Provider-Unicode-Japanese.yml [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/multibyte.yml [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/sample.yml [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/toolbar.yml [new file with mode: 0644]
lib/Parse/CPAN/Meta/t/data/vanilla.yml [new file with mode: 0644]
t/lib/Parse/CPAN/Meta/Test.pm [new file with mode: 0644]

index adbfb95..b7c53a6 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -2614,6 +2614,25 @@ lib/Package/Constants.pm Package::Constants
 lib/Package/Constants/t/01_list.t      Package::Constants tests
 lib/Params/Check.pm    Params::Check
 lib/Params/Check/t/01_Params-Check.t   Params::Check tests
+lib/Parse/CPAN/Meta.pm                                         Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/Changes                                    Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/01_compile.t                             Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/02_basic.t                               Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/03_regression.t                          Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/04_scalar.t                              Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/11_meta_yml.t                            Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/12_plagger.t                             Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/13_perl_smith.t                          Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/14_yaml_org.t                            Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/15_multibyte.t                           Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/16_nullrefs.t                            Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/17_toolbar.t                             Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/HTML-WebDAO.yml                     Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/Template-Provider-Unicode-Japanese.yml      Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/multibyte.yml                       Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/sample.yml                          Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/toolbar.yml                         Parse::CPAN::Meta
+lib/Parse/CPAN/Meta/t/data/vanilla.yml                         Parse::CPAN::Meta
 lib/parent.pm  Establish an ISA relationship with base classes at compile time
 lib/parent/t/compile-time-file.t       tests for parent.pm
 lib/parent/t/compile-time.t    tests for parent.pm
@@ -3900,6 +3919,7 @@ t/lib/Math/BigRat/Test.pm         Math::BigRat test helper
 t/lib/mypragma.pm              An example user pragma
 t/lib/mypragma.t               Test the example user pragma
 t/lib/no_load.t                        Test that some modules don't load others
+t/lib/Parse/CPAN/Meta/Test.pm  Parse::CPAN::Meta testing library
 t/lib/proxy_constant_subs.t    Test that Proxy Constant Subs behave correctly
 t/lib/Sans_mypragma.pm                 Test module for t/lib/mypragma.t
 t/lib/strict/refs              Tests of "use strict 'refs'" for strict.t
diff --git a/lib/Parse/CPAN/Meta.pm b/lib/Parse/CPAN/Meta.pm
new file mode 100644 (file)
index 0000000..8d5e29b
--- /dev/null
@@ -0,0 +1,398 @@
+package Parse::CPAN::Meta;
+
+use strict;
+use Carp 'croak';
+BEGIN {
+       require 5.004;
+       require Exporter;
+       $Parse::CPAN::Meta::VERSION   = '0.04';
+       @Parse::CPAN::Meta::ISA       = qw{ Exporter      };
+       @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
+}
+
+# Prototypes
+sub LoadFile ($);
+sub Load     ($);
+sub _scalar  ($$$);
+sub _array   ($$$);
+sub _hash    ($$$);
+
+# Printable characters for escapes
+my %UNESCAPES = (
+       z => "\x00", a => "\x07", t    => "\x09",
+       n => "\x0a", v => "\x0b", f    => "\x0c",
+       r => "\x0d", e => "\x1b", '\\' => '\\',
+);
+
+
+
+
+
+#####################################################################
+# Implementation
+
+# Create an object from a file
+sub LoadFile ($) {
+       # Check the file
+       my $file = shift;
+       croak('You did not specify a file name')            unless $file;
+       croak( "File '$file' does not exist" )              unless -e $file;
+       croak( "'$file' is a directory, not a file" )       unless -f _;
+       croak( "Insufficient permissions to read '$file'" ) unless -r _;
+
+       # Slurp in the file
+       local $/ = undef;
+       open( CFG, $file ) or croak("Failed to open file '$file': $!");
+       my $yaml = <CFG>;
+       close CFG          or croak("Failed to close file '$file': $!");
+
+       # Hand off to the actual parser
+       Load( $yaml );
+}
+
+# Parse a document from a string.
+# Doing checks on $_[0] prevents us having to do a string copy.
+sub Load ($) {
+       unless ( defined $_[0] ) {
+               croak("Did not provide a string to Load");
+       }
+       return () unless length $_[0];
+       unless ( $_[0] =~ /[\012\015]+$/ ) {
+               croak("Stream does not end with newline character");
+       }
+
+       # Split the file into lines
+       my @lines = grep { ! /^\s*(?:\#.*)?$/ }
+                   split /(?:\015{1,2}\012|\015|\012)/, shift;
+
+       # A nibbling parser
+       my @documents = ();
+       while ( @lines ) {
+               # Do we have a document header?
+               if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?$/ ) {
+                       # Handle scalar documents
+                       shift @lines;
+                       if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML:[\d\.]+)$/ ) {
+                               push @documents, _scalar( "$1", [ undef ], \@lines );
+                               next;
+                       }
+               }
+
+               if ( ! @lines or $lines[0] =~ /^---\s*(?:(.+)\s*)?$/ ) {
+                       # A naked document
+                       push @documents, undef;
+
+               } elsif ( $lines[0] =~ /^\s*\-/ ) {
+                       # An array at the root
+                       my $document = [ ];
+                       push @documents, $document;
+                       _array( $document, [ 0 ], \@lines );
+
+               } elsif ( $lines[0] =~ /^(\s*)\w/ ) {
+                       # A hash at the root
+                       my $document = { };
+                       push @documents, $document;
+                       _hash( $document, [ length($1) ], \@lines );
+
+               } else {
+                       croak("Parse::CPAN::Meta does not support the line '$lines[0]'");
+               }
+       }
+
+       if ( wantarray ) {
+               return @documents;
+       } else {
+               return $documents[-1];
+       }
+}
+
+# Deparse a scalar string to the actual scalar
+sub _scalar ($$$) {
+       my $string = shift;
+       my $indent = shift;
+       my $lines  = shift;
+
+       # Trim trailing whitespace
+       $string =~ s/\s*$//;
+
+       # Explitic null/undef
+       return undef if $string eq '~';
+
+       # Quotes
+       if ( $string =~ /^\'(.*?)\'$/ ) {
+               return '' unless defined $1;
+               my $rv = $1;
+               $rv =~ s/\'\'/\'/g;
+               return $rv;
+       }
+       if ( $string =~ /^\"((?:\\.|[^\"])*)\"$/ ) {
+               my $str = $1;
+               $str =~ s/\\"/"/g;
+               $str =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
+               return $str;
+       }
+       if ( $string =~ /^[\'\"]/ ) {
+               # A quote with folding... we don't support that
+               croak("Parse::CPAN::Meta does not support multi-line quoted scalars");
+       }
+
+       # Null hash and array
+       if ( $string eq '{}' ) {
+               # Null hash
+               return {};              
+       }
+       if ( $string eq '[]' ) {
+               # Null array
+               return [];
+       }
+
+       # Regular unquoted string
+       return $string unless $string =~ /^[>|]/;
+
+       # Error
+       croak("Multi-line scalar content missing") unless @$lines;
+
+       # Check the indent depth
+       $lines->[0] =~ /^(\s*)/;
+       $indent->[-1] = length("$1");
+       if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
+               croak("Illegal line indenting");
+       }
+
+       # Pull the lines
+       my @multiline = ();
+       while ( @$lines ) {
+               $lines->[0] =~ /^(\s*)/;
+               last unless length($1) >= $indent->[-1];
+               push @multiline, substr(shift(@$lines), length($1));
+       }
+
+       my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
+       my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
+       return join( $j, @multiline ) . $t;
+}
+
+# Parse an array
+sub _array ($$$) {
+       my $array  = shift;
+       my $indent = shift;
+       my $lines  = shift;
+
+       while ( @$lines ) {
+               # Check for a new document
+               return 1 if $lines->[0] =~ /^---\s*(?:(.+)\s*)?$/;
+
+               # Check the indent level
+               $lines->[0] =~ /^(\s*)/;
+               if ( length($1) < $indent->[-1] ) {
+                       return 1;
+               } elsif ( length($1) > $indent->[-1] ) {
+                       croak("Hash line over-indented");
+               }
+
+               if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
+                       # Inline nested hash
+                       my $indent2 = length("$1");
+                       $lines->[0] =~ s/-/ /;
+                       push @$array, { };
+                       _hash( $array->[-1], [ @$indent, $indent2 ], $lines );
+
+               } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*$/ ) {
+                       # Array entry with a value
+                       shift @$lines;
+                       push @$array, _scalar( "$2", [ @$indent, undef ], $lines );
+
+               } elsif ( $lines->[0] =~ /^\s*\-\s*$/ ) {
+                       shift @$lines;
+                       unless ( @$lines ) {
+                               push @$array, undef;
+                               return 1;
+                       }
+                       if ( $lines->[0] =~ /^(\s*)\-/ ) {
+                               my $indent2 = length("$1");
+                               if ( $indent->[-1] == $indent2 ) {
+                                       # Null array entry
+                                       push @$array, undef;
+                               } else {
+                                       # Naked indenter
+                                       push @$array, [ ];
+                                       _array( $array->[-1], [ @$indent, $indent2 ], $lines );
+                               }
+
+                       } elsif ( $lines->[0] =~ /^(\s*)\w/ ) {
+                               push @$array, { };
+                               _hash( $array->[-1], [ @$indent, length("$1") ], $lines );
+
+                       } else {
+                               croak("Parse::CPAN::Meta does not support the line '$lines->[0]'");
+                       }
+
+               } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
+                       # This is probably a structure like the following...
+                       # ---
+                       # foo:
+                       # - list
+                       # bar: value
+                       #
+                       # ... so lets return and let the hash parser handle it
+                       return 1;
+
+               } else {
+                       croak("Parse::CPAN::Meta does not support the line '$lines->[0]'");
+               }
+       }
+
+       return 1;
+}
+
+# Parse an array
+sub _hash ($$$) {
+       my $hash   = shift;
+       my $indent = shift;
+       my $lines  = shift;
+
+       while ( @$lines ) {
+               # Check for a new document
+               return 1 if $lines->[0] =~ /^---\s*(?:(.+)\s*)?$/;
+
+               # Check the indent level
+               $lines->[0] =~/^(\s*)/;
+               if ( length($1) < $indent->[-1] ) {
+                       return 1;
+               } elsif ( length($1) > $indent->[-1] ) {
+                       croak("Hash line over-indented");
+               }
+
+               # Get the key
+               unless ( $lines->[0] =~ s/^\s*([^\'\"][^\n]*?)\s*:(\s+|$)// ) {
+                       croak("Bad hash line");
+               }
+               my $key = $1;
+
+               # Do we have a value?
+               if ( length $lines->[0] ) {
+                       # Yes
+                       $hash->{$key} = _scalar( shift(@$lines), [ @$indent, undef ], $lines );
+                       next;
+               }
+
+               # An indent
+               shift @$lines;
+               unless ( @$lines ) {
+                       $hash->{$key} = undef;
+                       return 1;
+               }
+               if ( $lines->[0] =~ /^(\s*)-/ ) {
+                       $hash->{$key} = [];
+                       _array( $hash->{$key}, [ @$indent, length($1) ], $lines );
+               } elsif ( $lines->[0] =~ /^(\s*)./ ) {
+                       my $indent2 = length("$1");
+                       if ( $indent->[-1] >= $indent2 ) {
+                               # Null hash entry
+                               $hash->{$key} = undef;
+                       } else {
+                               $hash->{$key} = {};
+                               _hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
+                       }
+               }
+       }
+
+       return 1;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Parse::CPAN::Meta - Parse META.yml and other similar CPAN metadata files
+
+=head1 SYNOPSIS
+
+    #############################################
+    # In your file
+    
+    ---
+    rootproperty: blah
+    section:
+      one: two
+      three: four
+      Foo: Bar
+      empty: ~
+    
+    
+    
+    #############################################
+    # In your program
+    
+    use Parse::CPAN::Meta;
+    
+    # Create a YAML file
+    my @yaml = Parse::CPAN::Meta::LoadFile( 'Meta.yml' );
+    
+    # Reading properties
+    my $root = $yaml[0]->{rootproperty};
+    my $one  = $yaml[0]->{section}->{one};
+    my $Foo  = $yaml[0]->{section}->{Foo};
+
+=head1 DESCRIPTION
+
+B<Parse::CPAN::Meta> is a parser for META.yml files, based on the
+parser half of L<YAML::Tiny>.
+
+It supports a basic subset of the full YAML specification, enough to
+implement parsing of typical META.yml files, and other similarly simple
+YAML files.
+
+If you need something with more power, move up to a full YAML parser such
+as L<YAML>, L<YAML::Syck> or L<YAML::LibYAML>.
+
+Parse::CPAN::Meta provides a very simply API of only two functions, based
+on the YAML functions of the same name. Wherever possible, identical
+calling semantics are used.
+
+All error reporting is done with exceptions (dieing).
+
+=head1 FUNCTIONS
+
+For maintenance clarity, no functions are exported.
+
+=head2 Load( $string )
+
+  my @documents = Load( $string );
+
+Parses a string containing a valid YAML stream into a list of Perl data
+structures.
+
+=head2 LoadFile( $file_name )
+
+Reads the YAML stream from a file instead of a string.
+
+=head1 SUPPORT
+
+Bugs should be reported via the CPAN bug tracker at
+
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Parse-CPAN-Meta>
+
+=head1 AUTHOR
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+=head1 SEE ALSO
+
+L<YAML::Tiny>, L<YAML>, L<YAML::Syck>
+
+=head1 COPYRIGHT
+
+Copyright 2006 - 2009 Adam Kennedy.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut
diff --git a/lib/Parse/CPAN/Meta/Changes b/lib/Parse/CPAN/Meta/Changes
new file mode 100644 (file)
index 0000000..9bd147c
--- /dev/null
@@ -0,0 +1,20 @@
+Revision history for Perl extension Parse-CPAN-Meta
+
+0.04 Wed  7 Jan 2009
+       - Matching changes in YAML::Tiny 1.36
+       - Fixing missing feature reported by H.Merijn Brand
+       - Changes to make Padre-CPAN-Meta core-compatible (SMUELLER)++
+
+0.03 Thu 20 Mar 2008
+       - Adding YAML.pm compatibility in scalar context
+
+0.02 Thu 10 Jan 2008
+       - Purging some references to YAML::Tiny that I missed
+
+0.01 Tue  8 Jan 2008
+       - Cloned from YAML::Tiny 1.21
+       - Removed all write functionality
+       - Reduced interface to Load and LoadFile
+       - Removed object-orientation
+       - Removed global $errstr, all errors are exceptions
+       - Applied prototypes to all functions
diff --git a/lib/Parse/CPAN/Meta/t/01_compile.t b/lib/Parse/CPAN/Meta/t/01_compile.t
new file mode 100644 (file)
index 0000000..7e64db7
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/perl
+
+# Load testing for YAML::Tiny
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Test::More tests => 3;
+
+# Check their perl version
+ok( $] >= 5.004, "Your perl is new enough" );
+
+# Does the module load
+use_ok( 'Parse::CPAN::Meta' );
+use_ok( 'Parse::CPAN::Meta::Test' );
diff --git a/lib/Parse/CPAN/Meta/t/02_basic.t b/lib/Parse/CPAN/Meta/t/02_basic.t
new file mode 100644 (file)
index 0000000..6b65e02
--- /dev/null
@@ -0,0 +1,248 @@
+#!/usr/bin/perl
+
+# Testing of basic document structures
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(30);
+
+
+
+
+
+#####################################################################
+# Sample Testing
+
+# Test a completely empty document
+yaml_ok(
+       '',
+       [  ],
+       'empty',
+);
+
+# Just a newline
+### YAML.pm has a bug where it dies on a single newline
+yaml_ok(
+       "\n\n",
+       [ ],
+       'only_newlines',
+);
+
+# Just a comment
+yaml_ok(
+       "# comment\n",
+       [ ],
+       'only_comment',
+);
+
+# Empty documents
+yaml_ok(
+       "---\n",
+       [ undef ],
+       'only_header',
+);
+yaml_ok(
+       "---\n---\n",
+       [ undef, undef ],
+       'two_header',
+);
+yaml_ok(
+       "--- ~\n",
+       [ undef ],
+       'one_undef',
+);
+yaml_ok(
+       "---  ~\n",
+       [ undef ],
+       'one_undef2',
+);
+yaml_ok(
+       "--- ~\n---\n",
+       [ undef, undef ],
+       'two_undef',
+);
+
+# Just a scalar
+yaml_ok(
+       "--- foo\n",
+       [ 'foo' ],
+       'one_scalar',
+);
+yaml_ok(
+       "---  foo\n",
+       [ 'foo' ],
+       'one_scalar2',
+);
+yaml_ok(
+       "--- foo\n--- bar\n",
+       [ 'foo', 'bar' ],
+       'two_scalar',
+);
+
+# Simple lists
+yaml_ok(
+       "---\n- foo\n",
+       [ [ 'foo' ] ],
+       'one_list1',
+);
+yaml_ok(
+       "---\n- foo\n- bar\n",
+       [ [ 'foo', 'bar' ] ],
+       'one_list2',
+);
+yaml_ok(
+       "---\n- ~\n- bar\n",
+       [ [ undef, 'bar' ] ],
+       'one_listundef',
+);
+
+# Simple hashs
+yaml_ok(
+       "---\nfoo: bar\n",
+       [ { foo => 'bar' } ],
+       'one_hash1',
+);
+
+yaml_ok(
+       "---\nfoo: bar\nthis: ~\n",
+       [ { this => undef, foo => 'bar' } ],
+       'one_hash2',
+);
+
+# Simple array inside a hash with an undef
+yaml_ok(
+       <<'END_YAML',
+---
+foo:
+  - bar
+  - ~
+  - baz
+END_YAML
+       [ { foo => [ 'bar', undef, 'baz' ] } ],
+       'array_in_hash',
+);
+
+# Simple hash inside a hash with an undef
+yaml_ok(
+       <<'END_YAML',
+---
+foo: ~
+bar:
+  foo: bar
+END_YAML
+       [ { foo => undef, bar => { foo => 'bar' } } ],
+       'hash_in_hash',
+);
+
+# Mixed hash and scalars inside an array
+yaml_ok(
+       <<'END_YAML',
+---
+-
+  foo: ~
+  this: that
+- foo
+- ~
+-
+  foo: bar
+  this: that
+END_YAML
+       [ [
+               { foo => undef, this => 'that' },
+               'foo',
+               undef,
+               { foo => 'bar', this => 'that' },
+       ] ],
+       'hash_in_array',
+);
+
+# Simple single quote
+yaml_ok(
+       "---\n- 'foo'\n",
+       [ [ 'foo' ] ],
+       'single_quote1',
+);
+yaml_ok(
+       "---\n- '  '\n",
+       [ [ '  ' ] ],
+       'single_spaces',
+);
+yaml_ok(
+       "---\n- ''\n",
+       [ [ '' ] ],
+       'single_null',
+);
+
+# Double quotes
+yaml_ok(
+       "--- \"  \"\n",
+       [ '  ' ],
+       "only_spaces",
+);
+
+yaml_ok(
+       "--- \"  foo\"\n--- \"bar  \"\n",
+       [ "  foo", "bar  " ],
+       "leading_trailing_spaces",
+);
+
+# Implicit document start
+yaml_ok(
+       "foo: bar\n",
+       [ { foo => 'bar' } ],
+       'implicit_hash',
+);
+yaml_ok(
+       "- foo\n",
+       [ [ 'foo' ] ],
+       'implicit_array',
+);
+
+# Inline nested hash
+yaml_ok(
+       <<'END_YAML',
+---
+- ~
+- foo: bar
+  this: that
+- baz
+END_YAML
+       [ [ undef, { foo => 'bar', this => 'that' }, 'baz' ] ],
+       'inline_nested_hash',
+);
+
+# Empty comments
+yaml_ok(
+       "---\n- foo\n#\n- bar\n",
+       [ [ 'foo', 'bar' ] ],
+       'empty_comment_in_list',
+);
+
+yaml_ok(
+       "---\nfoo: bar\n# foo\none: two\n",
+       [ { foo => 'bar', one => 'two' } ],
+       'empty_comment_in_hash',
+);
+
+# Complex keys
+yaml_ok(
+       "---\na b: c d\n",
+       [ { 'a b' => 'c d' } ],
+       'key_with_whitespace',
+);
diff --git a/lib/Parse/CPAN/Meta/t/03_regression.t b/lib/Parse/CPAN/Meta/t/03_regression.t
new file mode 100644 (file)
index 0000000..1a1436a
--- /dev/null
@@ -0,0 +1,311 @@
+#!/usr/bin/perl
+
+# Testing of common META.yml examples
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(20);
+
+
+
+
+
+#####################################################################
+# In META.yml files, some hash keys contain module names
+
+# Hash key legally containing a colon
+yaml_ok(
+       "---\nFoo::Bar: 1\n",
+       [ { 'Foo::Bar' => 1 } ],
+       'module_hash_key',
+);
+
+# Hash indented
+yaml_ok(
+         "---\n"
+       . "  foo: bar\n",
+       [ { foo => "bar" } ],
+       'hash_indented',
+);
+
+
+
+
+
+#####################################################################
+# Support for literal multi-line scalars
+
+# Declarative multi-line scalar
+yaml_ok(
+         "---\n"
+       . "  foo: >\n"
+       . "     bar\n"
+       . "     baz\n",
+       [ { foo => "bar baz\n" } ],
+       'simple_multiline',
+);
+
+# Piped multi-line scalar
+yaml_ok( <<'END_YAML', [ [ "foo\nbar\n", 1 ] ], 'indented', nosyck => 1 );
+---
+- |
+  foo
+  bar
+- 1
+END_YAML
+
+# ... with a pointless hyphen
+yaml_ok( <<'END_YAML', [ [ "foo\nbar", 1 ] ], 'indented', nosyck => 1 );
+---
+- |-
+  foo
+  bar
+- 1
+END_YAML
+
+
+
+
+
+
+#####################################################################
+# Support for YAML document version declarations
+
+# Simple case
+yaml_ok(
+       <<'END_YAML',
+--- #YAML:1.0
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'simple_doctype',
+);
+
+# Multiple documents
+yaml_ok(
+       <<'END_YAML',
+--- #YAML:1.0
+foo: bar
+--- #YAML:1.0
+- 1
+--- #YAML:1.0
+foo: bar
+END_YAML
+       [ { foo => 'bar' }, [ 1 ], { foo => 'bar' } ],
+       'multi_doctype',
+);
+
+
+
+
+
+#####################################################################
+# Hitchhiker Scalar
+
+yaml_ok(
+       <<'END_YAML',
+--- 42
+END_YAML
+       [ 42 ],
+       'hitchhiker scalar',
+       serializes => 1,
+);
+
+
+
+
+
+#####################################################################
+# Null HASH/ARRAY
+
+yaml_ok(
+       <<'END_YAML',
+---
+- foo
+- {}
+- bar
+END_YAML
+       [ [ 'foo', {}, 'bar' ] ],
+       'null hash in array',
+);
+
+yaml_ok(
+       <<'END_YAML',
+---
+- foo
+- []
+- bar
+END_YAML
+       [ [ 'foo', [], 'bar' ] ],
+       'null array in array',
+);
+
+yaml_ok(
+       <<'END_YAML',
+---
+foo: {}
+bar: 1
+END_YAML
+       [  { foo => {}, bar => 1 } ],
+       'null hash in hash',
+);
+
+yaml_ok(
+       <<'END_YAML',
+---
+foo: []
+bar: 1
+END_YAML
+       [  { foo => [], bar => 1 } ],
+       'null array in hash',
+);
+
+
+
+
+#####################################################################
+# Trailing Whitespace
+
+yaml_ok(
+       <<'END_YAML',
+---
+abstract: Generate fractal curves 
+foo: ~ 
+arr:
+  - foo 
+  - ~
+  - 'bar'  
+END_YAML
+       [ { abstract => 'Generate fractal curves', foo => undef, arr => [ 'foo', undef, 'bar' ] } ],
+       'trailing whitespace',
+);
+
+
+
+
+
+#####################################################################
+# Quote vs Hash
+
+yaml_ok(
+       <<'END_YAML',
+---
+author:
+  - 'mst: Matt S. Trout <mst@shadowcatsystems.co.uk>'
+END_YAML
+       [ { author => [ 'mst: Matt S. Trout <mst@shadowcatsystems.co.uk>' ] } ],
+       'hash-like quote',
+);
+
+
+
+
+
+#####################################################################
+# Single Quote Idiosyncracy
+
+yaml_ok(
+       <<'END_YAML',
+---
+slash: '\\'
+name: 'O''Reilly'
+END_YAML
+       [ { slash => "\\\\", name => "O'Reilly" } ],
+       'single quote subtleties',
+);
+
+
+
+
+
+#####################################################################
+# Empty Values and Premature EOF
+
+yaml_ok(
+       <<'END_YAML',
+---
+foo:    0
+requires:
+build_requires:
+END_YAML
+       [ { foo => 0, requires => undef, build_requires => undef } ],
+       'empty hash keys',
+);
+
+yaml_ok(
+       <<'END_YAML',
+---
+- foo
+-
+-
+END_YAML
+       [ [ 'foo', undef, undef ] ],
+       'empty array keys',
+);
+
+
+
+
+
+#####################################################################
+# Comment on the Document Line
+
+yaml_ok(
+       <<'END_YAML',
+--- # Comment
+foo: bar
+END_YAML
+       [ { foo => 'bar' } ],
+       'comment header',
+);
+
+
+
+
+
+
+#####################################################################
+# Newlines and tabs
+
+yaml_ok(
+       <<'END_YAML',
+foo: "foo\\\n\tbar"
+END_YAML
+       [ { foo => "foo\\\n\tbar" } ],
+       'special characters',
+);
+
+
+
+
+
+
+######################################################################
+# Non-Indenting Sub-List
+
+yaml_ok(
+       <<'END_YAML',
+---
+foo:
+- list
+bar: value
+END_YAML
+       [ { foo => [ 'list' ], bar => 'value' } ],
+       'Non-indenting sub-list',
+);
diff --git a/lib/Parse/CPAN/Meta/t/04_scalar.t b/lib/Parse/CPAN/Meta/t/04_scalar.t
new file mode 100644 (file)
index 0000000..7a637c9
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+# Testing of basic document structures
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use Test::More tests => 2;
+use Parse::CPAN::Meta ();
+
+my $one = <<'END_YAML';
+---
+- foo
+END_YAML
+
+my $two = <<'END_YAML';
+---
+- foo
+---
+- bar
+END_YAML
+
+my $one_scalar_tiny = Parse::CPAN::Meta::Load( $one );
+my $two_scalar_tiny = Parse::CPAN::Meta::Load( $two );
+
+is_deeply( $one_scalar_tiny, [ 'foo' ], 'one: Parsed correctly' );
+is_deeply( $two_scalar_tiny, [ 'bar' ], 'two: Parsed correctly' );
diff --git a/lib/Parse/CPAN/Meta/t/11_meta_yml.t b/lib/Parse/CPAN/Meta/t/11_meta_yml.t
new file mode 100644 (file)
index 0000000..9b789f0
--- /dev/null
@@ -0,0 +1,318 @@
+#!/usr/bin/perl
+
+# Testing of common META.yml examples
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(8, 2);
+
+
+
+
+
+#####################################################################
+# Testing YAML::Tiny's META.yml file
+
+yaml_ok(
+       <<'END_YAML',
+abstract: Read/Write YAML files with as little code as possible
+author: 'Adam Kennedy <cpan@ali.as>'
+build_requires:
+  File::Spec: 0.80
+  Test::More: 0.47
+distribution_type: module
+generated_by: Module::Install version 0.63
+license: perl
+name: YAML-Tiny
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  perl: 5.005
+version: 0.03
+END_YAML
+       [ {
+               abstract          => 'Read/Write YAML files with as little code as possible',
+               author            => 'Adam Kennedy <cpan@ali.as>',
+               build_requires    => {
+                       'File::Spec' => '0.80',
+                       'Test::More' => '0.47',
+               },
+               distribution_type => 'module',
+               generated_by      => 'Module::Install version 0.63',
+               license           => 'perl',
+               name              => 'YAML-Tiny',
+               no_index          => {
+                       directory    => [ qw{inc t} ],
+               },
+               requires          => {
+                       perl         => '5.005',
+               },
+               version           => '0.03',
+       } ],
+       'YAML::Tiny',
+);
+
+
+
+
+
+
+#####################################################################
+# Testing a META.yml from a commercial project that crashed
+
+yaml_ok(
+       <<'END_YAML',
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         ITS-SIN-FIDS-Content-XML
+version:      0.01
+version_from: lib/ITS/SIN/FIDS/Content/XML.pm
+installdirs:  site
+requires:
+    Test::More:                    0.45
+    XML::Simple:                   2
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.30
+END_YAML
+       [ {
+               name              => 'ITS-SIN-FIDS-Content-XML',
+               version           => 0.01,
+               version_from      => 'lib/ITS/SIN/FIDS/Content/XML.pm',
+               installdirs       => 'site',
+               requires          => {
+                       'Test::More'  => 0.45,
+                       'XML::Simple' => 2,
+                       },
+               distribution_type => 'module',
+               generated_by      => 'ExtUtils::MakeMaker version 6.30',
+       } ],
+       'YAML::Tiny',
+);
+
+
+
+
+
+
+#####################################################################
+# Testing various failing META.yml files from CPAN
+
+yaml_ok(
+       <<'END_YAML',
+---
+abstract: Mii in Nintendo Wii data parser and builder
+author: Toru Yamaguchi <zigorou@cpan.org>
+distribution_type: module
+generated_by: Module::Install version 0.65
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Games-Nintendo-Wii-Mii
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  Carp: 1.03
+  Class::Accessor::Fast: 0.3
+  File::Slurp: 9999.12
+  IO::File: 1.1
+  Readonly: 0
+  Tie::IxHash: 1.21
+  URI: 1.35
+  XML::LibXML: 1.62
+version: 0.02
+END_YAML
+       [ {
+               abstract => 'Mii in Nintendo Wii data parser and builder',
+               author   => 'Toru Yamaguchi <zigorou@cpan.org>',
+               distribution_type => 'module',
+               generated_by => 'Module::Install version 0.65',
+               license => 'perl',
+               'meta-spec' => {
+                       url => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
+                       version => '1.3',
+               },
+               name => 'Games-Nintendo-Wii-Mii',
+               no_index => {
+                       directory => [ qw{ inc t } ],
+               },
+               requires => {
+                       'Carp' => '1.03',
+                       'Class::Accessor::Fast' => '0.3',
+                       'File::Slurp' => '9999.12',
+                       'IO::File'    => '1.1',
+                       'Readonly'    => '0',
+                       'Tie::IxHash' => '1.21',
+                       'URI'         => '1.35',
+                       'XML::LibXML' => '1.62',
+               },
+               version => '0.02',
+       } ],
+       'Games-Nintendo-Wii-Mii',
+);
+
+yaml_ok(
+       <<'END_YAML',
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Acme-Time-Baby
+version:      2.106
+version_from: Baby.pm
+installdirs:  site
+requires:
+    warnings:
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
+END_YAML
+       [ {
+               name => 'Acme-Time-Baby',
+               version => '2.106',
+               version_from => 'Baby.pm',
+               installdirs => 'site',
+               requires => {
+                       warnings => undef,
+               },
+               distribution_type => 'module',
+               generated_by => 'ExtUtils::MakeMaker version 6.17',
+       } ],
+       'Acme-Time-Baby',
+);
+
+
+
+
+
+#####################################################################
+# File with a YAML header
+
+yaml_ok(
+       <<'END_YAML',
+--- %YAML:1.0
+name:     Data-Swap
+version:  0.05
+license:  perl
+distribution_type: module
+requires:
+   perl:  5.6.0
+dynamic_config: 0
+END_YAML
+       [ {
+               name => 'Data-Swap',
+               version => '0.05',
+               license => 'perl',
+               distribution_type => 'module',
+               requires => {
+                       perl => '5.6.0',
+               },
+               dynamic_config => '0',
+       } ],
+       'Data-Swap',
+);
+
+yaml_ok(
+       <<'END_YAML',
+--- #YAML:1.0
+name:     Data-Swap
+version:  0.05
+license:  perl
+distribution_type: module
+requires:
+   perl:  5.6.0
+dynamic_config: 0
+END_YAML
+       [ {
+               name => 'Data-Swap',
+               version => '0.05',
+               license => 'perl',
+               distribution_type => 'module',
+               requires => {
+                       perl => '5.6.0',
+               },
+               dynamic_config => '0',
+       } ],
+       'Data-Swap',
+);
+
+
+
+
+
+#####################################################################
+# Various files that fail for unknown reasons
+
+SCOPE: {
+       my $content = load_ok(
+               'Template-Provider-Unicode-Japanese.yml',
+               catfile( test_data_directory(), 'Template-Provider-Unicode-Japanese.yml' ),
+               100
+       );
+       yaml_ok(
+               $content,
+               [ {
+                       abstract => 'Decode all templates by Unicode::Japanese',
+                       author   => 'Hironori Yoshida C<< <yoshida@cpan.org> >>',
+                       distribution_type => 'module',
+                       generated_by => 'Module::Install version 0.65',
+                       license => 'perl',
+                       'meta-spec' => {
+                               url => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
+                               version => '1.3',
+                       },
+                       name => 'Template-Provider-Unicode-Japanese',
+                       no_index => {
+                               directory => [ qw{ inc t } ],
+                       },
+                       requires => {
+                               'Template::Config' => 0,
+                               'Unicode::Japanese' => 0,
+                               perl => '5.6.0',
+                               version => '0',
+                       },
+                       version => '1.2.1',
+               } ],
+               'Template-Provider-Unicode-Japanese',
+       );
+}
+
+SCOPE: {
+       my $content = load_ok(
+               'HTML-WebDAO.yml',
+               catfile( test_data_directory(), 'HTML-WebDAO.yml' ),
+               100
+       );
+       yaml_ok(
+               $content,
+               [ {
+                       abstract => 'Perl extension for create complex web application',
+                       author   => [
+                               'Zahatski Aliaksandr, E<lt>zagap@users.sourceforge.netE<gt>',
+                       ],
+                       license  => 'perl',
+                       name     => 'HTML-WebDAO',
+                       version  => '0.04',
+               } ],
+               'HTML-WebDAO',
+       );
+}
diff --git a/lib/Parse/CPAN/Meta/t/12_plagger.t b/lib/Parse/CPAN/Meta/t/12_plagger.t
new file mode 100644 (file)
index 0000000..68f3761
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+
+# Testing Plagger config samples from Miyagawa-san's YAPC::NA 2006 talk
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(2);
+
+
+
+
+
+#####################################################################
+# Example Plagger Configuration 1
+
+yaml_ok(
+       <<'END_YAML',
+plugins:
+  - module: Subscription::Bloglines
+    config:
+      username: you@example.pl
+      password: foobar
+      mark_read: 1
+
+  - module: Publish::Gmail
+    config:
+      mailto: example@gmail.com
+      mailfrom: miyagawa@example.com
+      mailroute:
+        via: smtp
+        host: smtp.example.com
+END_YAML
+       [ { plugins => [
+               {
+                       module => 'Subscription::Bloglines',
+                       config => {
+                               username  => 'you@example.pl',
+                               password  => 'foobar',
+                               mark_read => 1,
+                       },
+               },
+               {
+                       module => 'Publish::Gmail',
+                       config => {
+                               mailto    => 'example@gmail.com',
+                               mailfrom  => 'miyagawa@example.com',
+                               mailroute => {
+                                       via  => 'smtp',
+                                       host => 'smtp.example.com',
+                               },
+                       },
+               },
+       ] } ],
+       'Plagger',
+);
+
+
+
+
+
+#####################################################################
+# Example Plagger Configuration 2
+
+yaml_ok(
+       <<'END_YAML',
+plugins:
+ - module: Subscription::Config
+   config:
+     feed:
+        # Trac's feed for changesets
+        - http://plagger.org/.../rss
+
+ # I don't like to be notified of the same items
+ # more than once
+ - module: Filter::Rule
+   rule:
+     module: Fresh
+     mtime:
+       path: /tmp/rssbot.time
+       autoupdate: 1
+
+ - module: Notify::IRC
+   config:
+     daemon_port: 9999
+     nickname: plaggerbot
+     server_host: chat.freenode.net
+     server_channels:
+       - #plagger-ja
+       - #plagger
+
+   
+END_YAML
+       [ { plugins => [ {
+               module => 'Subscription::Config',
+               config => {
+                       feed => [ 'http://plagger.org/.../rss' ],
+               },
+       }, {
+               module => 'Filter::Rule',
+               rule   => {
+                       module => 'Fresh',
+                       mtime  => {
+                               path => '/tmp/rssbot.time',
+                               autoupdate => 1,
+                       },
+               },
+       }, {
+               module => 'Notify::IRC',
+               config => {
+                       daemon_port     => 9999,
+                       nickname        => 'plaggerbot',
+                       server_host     => 'chat.freenode.net',
+                       server_channels => [
+                               '#plagger-ja',
+                               '#plagger',
+                       ],
+               },
+       } ] } ],
+       'plagger2',
+);                     
diff --git a/lib/Parse/CPAN/Meta/t/13_perl_smith.t b/lib/Parse/CPAN/Meta/t/13_perl_smith.t
new file mode 100644 (file)
index 0000000..557f9bb
--- /dev/null
@@ -0,0 +1,156 @@
+#!/usr/bin/perl
+
+# Testing of common META.yml examples
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(1, 1);
+
+
+
+
+
+#####################################################################
+# Testing that Perl::Smith config files work
+
+my $vanilla_file = catfile( test_data_directory(), 'vanilla.yml' );
+my $vanilla      = load_ok( 'yanilla.yml', $vanilla_file, 1000 );
+
+yaml_ok(
+       $vanilla,
+       [ {
+               package_name    => 'VanillaPerl',
+               package_version => 5,
+               download_dir    => 'c:\temp\vp_sources',
+               build_dir       => 'c:\temp\vp_build',
+               image_dir       => 'c:\vanilla-perl',
+               binary          => [
+                       {
+                               name    => 'dmake',
+                               url     => 'http://search.cpan.org/CPAN/authors/id/S/SH/SHAY/dmake-4.5-20060619-SHAY.zip',
+                               license => {
+                                       'dmake/COPYING' => 'dmake/COPYING',
+                                       'dmake/readme/license.txt' => 'dmake/license.txt',
+                               },
+                               install_to => {
+                                       'dmake/dmake.exe' => 'dmake/bin/dmake.exe',
+                                       'dmake/startup' => 'dmake/bin/startup',
+                               },
+                       },
+                       {
+                               name       => 'gcc-core',
+                               url        => 'http://umn.dl.sourceforge.net/mingw/gcc-core-3.4.5-20060117-1.tar.gz',
+                               license    => {
+                                       'COPYING'     => 'gcc/COPYING',
+                                       'COPYING.lib' => 'gcc/COPYING.lib',
+                               },
+                               install_to => 'mingw',
+                       },
+                       {
+                               name       => 'gcc-g++',
+                               url        => 'http://umn.dl.sourceforge.net/mingw/gcc-g++-3.4.5-20060117-1.tar.gz',
+                               license    => undef,
+                               install_to => 'mingw',
+                       },
+                       {
+                               name       => 'binutils',
+                               url        => 'http://umn.dl.sourceforge.net/mingw/binutils-2.16.91-20060119-1.tar.gz',
+                               license    => {
+                                       'Copying'     => 'binutils/Copying',
+                                       'Copying.lib' => 'binutils/Copying.lib',
+                               },
+                               install_to => 'mingw',
+                       },
+                       {
+                               name       => 'mingw-runtime',
+                               url        => 'http://umn.dl.sourceforge.net/mingw/mingw-runtime-3.10.tar.gz',
+                               license    => {
+                                       'doc/mingw-runtime/Contributors' => 'mingw/Contributors',
+                                       'doc/mingw-runtime/Disclaimer'   => 'mingw/Disclaimer',
+                               },
+                               install_to => 'mingw',
+                       },
+                       {
+                               name       => 'w32api',
+                               url        => 'http://umn.dl.sourceforge.net/mingw/w32api-3.6.tar.gz',
+                               license    => undef,
+                               install_to => 'mingw',
+                               extra      => {
+                                       'extra\README.w32api' => 'licenses\win32api\README.w32api',
+                               },
+                       }
+               ],
+               source => [
+                       {
+                               name       => 'perl',
+                               url        => 'http://mirrors.kernel.org/CPAN/src/perl-5.8.8.tar.gz',
+                               license    => {
+                                       'perl-5.8.8/Readme'   => 'perl/Readme',
+                                       'perl-5.8.8/Artistic' => 'perl/Artistic',
+                                       'perl-5.8.8/Copying'  => 'perl/Copying',
+                               },
+                               unpack_to  => 'perl',
+                               install_to => 'perl',
+                               after      => {
+                                       'extra\Config.pm' => 'lib\CPAN\Config.pm',
+                               },
+                       }
+               ],
+               modules => [
+                       {
+                               name      => 'Win32::Job',
+                               unpack_to => {
+                                       APIFile => 'Win32API-File',
+                               },
+                       },
+                       {
+                               name  => 'IO',
+                               force => 1,
+                       },
+                       {
+                               name => 'Compress::Zlib',
+                       },
+                       {
+                               name => 'IO::Zlib',
+                       },
+                       {
+                               name => 'Archive::Tar',
+                       },
+                       {
+                               name  => 'Net::FTP',
+                               extra => {
+                                       'extra\libnet.cfg' => 'libnet.cfg',
+                               },
+                       },
+               ],
+               extra => {
+                       'README' => 'README.txt',
+                       'LICENSE.txt' => 'LICENSE.txt',
+                       'Changes' => 'Release-Notes.txt',
+                       'extra\Config.pm' => 'perl\lib\CPAN\Config.pm',
+                       'extra\links\Perl-Documentation.url' => 'links\Perl Documentation.url',
+                       'extra\links\Perl-Homepage.url' => 'links\Perl Homepage.url',
+                       'extra\links\Perl-Mailing-Lists.url' => 'links\Perl Mailing Lists.url',
+                       'extra\links\Perlmonks-Community-Forum.url' => 'links\Perlmonks Community Forum.url',
+                       'extra\links\Search-CPAN-Modules.url' => 'links\Search CPAN Modules.url',
+                       'extra\links\Vanilla-Perl-Homepage.url' => 'links\Vanilla Perl Homepage.url',
+               },
+       } ],
+       'vanilla.yml',
+);
diff --git a/lib/Parse/CPAN/Meta/t/14_yaml_org.t b/lib/Parse/CPAN/Meta/t/14_yaml_org.t
new file mode 100644 (file)
index 0000000..5f01274
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+# Testing of common META.yml examples
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(1, 1);
+
+
+
+
+
+#####################################################################
+# Testing that Perl::Smith config files work
+
+my $sample_file = catfile( test_data_directory(), 'sample.yml' );
+my $sample      = load_ok( 'sample.yml', $sample_file, 500 );
+
+yaml_ok(
+       $sample,
+       [ {
+               invoice   => 34843,
+               date      => '2001-01-23',
+               'bill-to' => {
+                       given  => 'Chris',      
+                       family => 'Dumars',
+                       address => {
+                               lines  => "458 Walkman Dr.\nSuite #292\n",
+                               city   => 'Royal Oak',
+                               state  => 'MI',
+                               postal => 48046,
+                       },
+               },
+               product => [
+                       {
+                               sku         => 'BL394D',
+                               quantity    => '4',
+                               description => 'Basketball',
+                               price       => '450.00',
+                       },
+                       {
+                               sku         => 'BL4438H',
+                               quantity    => '1',
+                               description => 'Super Hoop',
+                               price       => '2392.00',
+                       },
+               ],
+               tax      => '251.42',
+               total    => '4443.52',
+               comments => <<'END_TEXT',
+Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.
+END_TEXT
+       } ],
+       'sample.yml',
+
+);
diff --git a/lib/Parse/CPAN/Meta/t/15_multibyte.t b/lib/Parse/CPAN/Meta/t/15_multibyte.t
new file mode 100644 (file)
index 0000000..c5e0d67
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+
+# Testing of META.yml containing AVAR's name
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(0, 1, 3);
+
+
+
+
+
+#####################################################################
+# Testing that Perl::Smith config files work
+
+my $sample_file = catfile( test_data_directory(), 'multibyte.yml' );
+my $sample      = load_ok( 'multibyte.yml', $sample_file, 450 );
+
+# Does the string parse to the structure
+my $name      = "multibyte";
+my $yaml_copy = $sample;
+my @yaml      = eval { Parse::CPAN::Meta::Load( $yaml_copy ); };
+is( $@, '', "$name: Parse::CPAN::Meta::Load parses without error" );
+is( $yaml_copy, $sample, "$name: Parse::CPAN::Meta::Load does not modify the input string" );
+SKIP: {
+       skip( "Shortcutting after failure", 1 ) if $@;
+       is_deeply( $yaml[0]->{build_requires}, {
+               'Config'     => 0,
+               'Test::More' => 0,
+               'XSLoader'   => 0,
+       }, 'build_requires ok' );
+}
diff --git a/lib/Parse/CPAN/Meta/t/16_nullrefs.t b/lib/Parse/CPAN/Meta/t/16_nullrefs.t
new file mode 100644 (file)
index 0000000..fa1c3e9
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+# Testing for null references
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+use Test::More tests(1);
+
+
+
+
+
+#####################################################################
+# Example Empty References
+
+yaml_ok(
+       <<'END_YAML',
+--- []
+--- {}
+END_YAML
+       [ [], {} ],
+       'Empty references',
+);
diff --git a/lib/Parse/CPAN/Meta/t/17_toolbar.t b/lib/Parse/CPAN/Meta/t/17_toolbar.t
new file mode 100644 (file)
index 0000000..2b2e06e
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+# Testing of a known-bad file from an editor
+
+BEGIN {
+       if( $ENV{PERL_CORE} ) {
+               chdir 't';
+               @INC = ('../lib', 'lib');
+       }
+       else {
+               unshift @INC, 't/lib/';
+       }
+}
+
+use strict;
+BEGIN {
+       $|  = 1;
+       $^W = 1;
+}
+
+use File::Spec::Functions ':ALL';
+use Parse::CPAN::Meta::Test;
+# use Test::More skip_all => 'Temporarily ignoring failing test';
+use Test::More tests(1, 1);
+
+
+
+
+
+#####################################################################
+# Testing that Perl::Smith config files work
+
+my $toolbar_file = catfile( test_data_directory(), 'toolbar.yml' );
+my $toolbar      = load_ok( 'toolbar.yml', $toolbar_file, 100 );
+
+yaml_ok(
+       $toolbar,
+       [ {
+               main_toolbar => [
+                       'item file-new',
+                       'item file-open',
+                       'item file-print#',
+                       'item file-close#',
+                       'item file-save-all',
+                       'item file-save',
+                       undef,
+                       'item edit-changes-undo',
+                       'item edit-changes-redo',
+                       undef,
+                       'item edit-cut',
+                       'item edit-copy',
+                       'item edit-paste',
+                       'item edit-replace',
+                       'item edit-delete',
+               ]
+       } ],
+       'toolbar.yml',
+);
diff --git a/lib/Parse/CPAN/Meta/t/data/HTML-WebDAO.yml b/lib/Parse/CPAN/Meta/t/data/HTML-WebDAO.yml
new file mode 100644 (file)
index 0000000..c5262ff
--- /dev/null
@@ -0,0 +1,8 @@
+--- #YAML:1.0
+name: HTML-WebDAO
+version: 0.04
+author:
+  - |-
+    Zahatski Aliaksandr, E<lt>zagap@users.sourceforge.netE<gt>
+abstract: Perl extension for create complex web application
+license: perl
diff --git a/lib/Parse/CPAN/Meta/t/data/Template-Provider-Unicode-Japanese.yml b/lib/Parse/CPAN/Meta/t/data/Template-Provider-Unicode-Japanese.yml
new file mode 100644 (file)
index 0000000..66dae89
--- /dev/null
@@ -0,0 +1,20 @@
+--- 
+abstract: Decode all templates by Unicode::Japanese
+author: Hironori Yoshida C<< <yoshida@cpan.org> >>
+distribution_type: module
+generated_by: Module::Install version 0.65
+license: perl
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: Template-Provider-Unicode-Japanese
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  Template::Config: 0
+  Unicode::Japanese: 0
+  perl: 5.6.0
+  version: 0
+version: 1.2.1
diff --git a/lib/Parse/CPAN/Meta/t/data/multibyte.yml b/lib/Parse/CPAN/Meta/t/data/multibyte.yml
new file mode 100644 (file)
index 0000000..91f3459
--- /dev/null
@@ -0,0 +1,22 @@
+--- 
+abstract: Perl-compatible regular expression engine
+author: "Ævar Arnfjörð Bjarmason <avar@cpan.org>"
+build_requires: 
+  Config: 0
+  Test::More: 0
+  XSLoader: 0
+distribution_type: module
+generated_by: Module::Install version 0.65
+license: perl
+meta-spec: 
+  url: http://module-build.sourceforge.net/META-spec-v1.3.html
+  version: 1.3
+name: re-engine-PCRE
+no_index: 
+  directory: 
+    - inc
+    - t
+requires: 
+  perl: 5.9.5
+tests: t/*.t t/*/*.t
+version: 0.10
diff --git a/lib/Parse/CPAN/Meta/t/data/sample.yml b/lib/Parse/CPAN/Meta/t/data/sample.yml
new file mode 100644 (file)
index 0000000..bea4f8a
--- /dev/null
@@ -0,0 +1,28 @@
+---
+invoice: 34843
+date   : 2001-01-23
+bill-to:
+    given  : Chris
+    family : Dumars
+    address:
+        lines: |
+            458 Walkman Dr.
+            Suite #292
+        city    : Royal Oak
+        state   : MI
+        postal  : 48046
+product:
+    - sku         : BL394D
+      quantity    : 4
+      description : Basketball
+      price       : 450.00
+    - sku         : BL4438H
+      quantity    : 1
+      description : Super Hoop
+      price       : 2392.00
+tax  : 251.42
+total: 4443.52
+comments: >
+    Late afternoon is best.
+    Backup contact is Nancy
+    Billsmer @ 338-4338.
diff --git a/lib/Parse/CPAN/Meta/t/data/toolbar.yml b/lib/Parse/CPAN/Meta/t/data/toolbar.yml
new file mode 100644 (file)
index 0000000..5219248
--- /dev/null
@@ -0,0 +1,16 @@
+main_toolbar:
+  - item file-new
+  - item file-open
+  - item file-print#
+  - item file-close#
+  - item file-save-all
+  - item file-save
+  -
+  - item edit-changes-undo
+  - item edit-changes-redo
+  -
+  - item edit-cut
+  - item edit-copy
+  - item edit-paste
+  - item edit-replace
+  - item edit-delete
diff --git a/lib/Parse/CPAN/Meta/t/data/vanilla.yml b/lib/Parse/CPAN/Meta/t/data/vanilla.yml
new file mode 100644 (file)
index 0000000..dc757e1
--- /dev/null
@@ -0,0 +1,98 @@
+# VanillaPerl YAML config file
+---
+# package info
+package_name:       VanillaPerl
+package_version:    5
+
+# directories
+download_dir:   c:\temp\vp_sources
+build_dir:      c:\temp\vp_build
+image_dir:      c:\vanilla-perl
+
+# Binary components
+binary:
+    -   name: dmake
+        url: http://search.cpan.org/CPAN/authors/id/S/SH/SHAY/dmake-4.5-20060619-SHAY.zip
+        license:
+            dmake/COPYING : dmake/COPYING
+            dmake/readme/license.txt: dmake/license.txt
+        install_to:
+            dmake/dmake.exe: dmake/bin/dmake.exe
+            dmake/startup: dmake/bin/startup
+
+    -   name: gcc-core
+        url: http://umn.dl.sourceforge.net/mingw/gcc-core-3.4.5-20060117-1.tar.gz
+        license:
+            COPYING: gcc/COPYING
+            COPYING.lib: gcc/COPYING.lib
+        install_to: mingw
+    
+    -   name: gcc-g++
+        url: http://umn.dl.sourceforge.net/mingw/gcc-g++-3.4.5-20060117-1.tar.gz
+        license:
+        install_to: mingw
+
+    -   name: binutils
+        url: http://umn.dl.sourceforge.net/mingw/binutils-2.16.91-20060119-1.tar.gz
+        license:
+            Copying: binutils/Copying
+            Copying.lib: binutils/Copying.lib
+        install_to: mingw
+
+    -   name: mingw-runtime
+        url: http://umn.dl.sourceforge.net/mingw/mingw-runtime-3.10.tar.gz
+        license:
+            doc/mingw-runtime/Contributors: mingw/Contributors
+            doc/mingw-runtime/Disclaimer: mingw/Disclaimer
+        install_to: mingw
+
+    -   name: w32api
+        url: http://umn.dl.sourceforge.net/mingw/w32api-3.6.tar.gz
+        license:
+        install_to: mingw
+        extra:
+            extra\README.w32api:  licenses\win32api\README.w32api
+
+# Source components
+source:
+    -   name: perl
+        url: http://mirrors.kernel.org/CPAN/src/perl-5.8.8.tar.gz
+        license: 
+            perl-5.8.8/Readme: perl/Readme
+            perl-5.8.8/Artistic: perl/Artistic
+            perl-5.8.8/Copying: perl/Copying
+        unpack_to: perl
+        install_to: perl
+        after:
+            extra\Config.pm:  lib\CPAN\Config.pm
+
+# Additional modules to bundle in site\lib
+modules:
+        # i.e. not used, but gets us the libwin32 dist
+    -   name: Win32::Job
+        unpack_to:
+            APIFile: Win32API-File
+    -   name: IO
+        force: 1
+    -   name: Compress::Zlib
+    -   name: IO::Zlib
+    -   name: Archive::Tar
+    -   name: Net::FTP
+        extra: 
+            extra\libnet.cfg: libnet.cfg
+
+# Extra files to be placed
+#    Signature.pm: perl\site\lib\Module\Signature.pm
+extra:
+    README:  README.txt
+    LICENSE.txt: LICENSE.txt
+    Changes: Release-Notes.txt
+    extra\Config.pm:  perl\lib\CPAN\Config.pm
+                      # reset this again
+
+    extra\links\Perl-Documentation.url: links\Perl Documentation.url
+    extra\links\Perl-Homepage.url: links\Perl Homepage.url
+    extra\links\Perl-Mailing-Lists.url: links\Perl Mailing Lists.url
+    extra\links\Perlmonks-Community-Forum.url: links\Perlmonks Community Forum.url
+    extra\links\Search-CPAN-Modules.url: links\Search CPAN Modules.url
+    extra\links\Vanilla-Perl-Homepage.url: links\Vanilla Perl Homepage.url
diff --git a/t/lib/Parse/CPAN/Meta/Test.pm b/t/lib/Parse/CPAN/Meta/Test.pm
new file mode 100644 (file)
index 0000000..319317a
--- /dev/null
@@ -0,0 +1,77 @@
+package Parse::CPAN::Meta::Test;
+
+use strict;
+use Test::More ();
+use Parse::CPAN::Meta;
+use File::Spec;
+
+use vars qw{@ISA @EXPORT};
+BEGIN {
+       require Exporter;
+       @ISA    = qw{ Exporter };
+       @EXPORT = qw{ tests  yaml_ok  slurp  load_ok  test_data_directory };
+}
+
+sub test_data_directory {
+       return(
+               $ENV{PERL_CORE}
+               ? File::Spec->catdir(File::Spec->updir, qw(lib Parse CPAN Meta t data))
+               : File::Spec->catdir(qw(t data))
+       );
+}
+
+# 22 tests per call to yaml_ok
+# 4  tests per call to load_ok
+sub tests {
+       return ( tests => count(@_) );
+}
+
+sub count {
+       my $yaml_ok = shift || 0;
+       my $load_ok = shift || 0;
+       my $single  = shift || 0;
+       my $count   = $yaml_ok * 3 + $load_ok * 4 + $single;
+       return $count;
+}
+
+sub yaml_ok {
+       my $string  = shift;
+       my $array   = shift;
+       my $name    = shift || 'unnamed';
+
+       # Does the string parse to the structure
+       my $yaml_copy = $string;
+       my @yaml      = eval { Parse::CPAN::Meta::Load( $yaml_copy ); };
+       Test::More::is( $@, '', "$name: Parse::CPAN::Meta parses without error" );
+       Test::More::is( $yaml_copy, $string, "$name: Parse::CPAN::Meta does not modify the input string" );
+       SKIP: {
+               Test::More::skip( "Shortcutting after failure", 1 ) if $@;
+               Test::More::is_deeply( \@yaml, $array, "$name: Parse::CPAN::Meta parses correctly" );
+       }
+
+       # Return true as a convenience
+       return 1;
+}
+
+sub slurp {
+       my $file = shift;
+       local $/ = undef;
+       open( FILE, " $file" ) or die "open($file) failed: $!";
+       my $source = <FILE>;
+       close( FILE ) or die "close($file) failed: $!";
+       $source;
+}
+
+sub load_ok {
+       my $name = shift;
+       my $file = shift;
+       my $size = shift;
+       Test::More::ok( -f $file, "Found $name" ) or Test::More::diag("Searched at '$file'");
+       Test::More::ok( -r $file, "Can read $name" );
+       my $content = slurp( $file );
+       Test::More::ok( (defined $content and ! ref $content), "Loaded $name" );
+       Test::More::ok( ($size < length $content), "Content of $name larger than $size bytes" );
+       return $content;
+}
+
+1;