package t::lib::PPI; use File::Spec::Functions ':ALL'; use Test::More; use Test::Object; use Params::Util qw{_STRING _INSTANCE}; use List::MoreUtils 'any'; use PPI::Dumper; use vars qw{$VERSION}; BEGIN { $VERSION = '1.215'; } ##################################################################### # PPI::Document Testing Test::Object->register( class => 'PPI::Document', tests => 1, code => \&document_ok, ); sub document_ok { my $doc = shift; # A document should have zero or more children that are either # a statement or a non-significant child. my @children = $doc->children; my $good = grep { _INSTANCE($_, 'PPI::Statement') or ( _INSTANCE($_, 'PPI::Token') and ! $_->significant ) } @children; is( $good, scalar(@children), 'Document contains only statements and non-significant tokens' ); 1; } ##################################################################### # Are there an unknowns Test::Object->register( class => 'PPI::Document', tests => 3, code => \&unknown_objects, ); sub unknown_objects { my $doc = shift; is( $doc->find_any('Token::Unknown'), '', "Contains no PPI::Token::Unknown elements", ); is( $doc->find_any('Structure::Unknown'), '', "Contains no PPI::Structure::Unknown elements", ); is( $doc->find_any('Statement::Unknown'), '', "Contains no PPI::Statement::Unknown elements", ); 1; } ##################################################################### # Are there any invalid nestings? Test::Object->register( class => 'PPI::Document', tests => 1, code => \&nested_statements, ); sub nested_statements { my $doc = shift; ok( ! $doc->find_any( sub { _INSTANCE($_[1], 'PPI::Statement') and any { _INSTANCE($_, 'PPI::Statement') } $_[1]->children } ), 'Document contains no nested statements', ); } Test::Object->register( class => 'PPI::Document', tests => 1, code => \&nested_structures, ); sub nested_structures { my $doc = shift; ok( ! $doc->find_any( sub { _INSTANCE($_[1], 'PPI::Structure') and any { _INSTANCE($_, 'PPI::Structure') } $_[1]->children } ), 'Document contains no nested structures', ); } Test::Object->register( class => 'PPI::Document', tests => 1, code => \&no_attribute_in_attribute, ); sub no_attribute_in_attribute { my $doc = shift; ok( ! $doc->find_any( sub { _INSTANCE($_[1], 'PPI::Token::Attribute') and ! exists $_[1]->{_attribute} } ), 'No ->{_attribute} in PPI::Token::Attributes', ); } ##################################################################### # PPI::Statement Tests Test::Object->register( class => 'PPI::Document', tests => 1, code => \&valid_compound_type, ); sub valid_compound_type { my $document = shift; my $compound = $document->find('PPI::Statement::Compound'); is( scalar( grep { not defined $_->type } @$compound ), 0, 'All compound statements have defined ->type', ); } ##################################################################### # Does ->location work properly # As an aside, fixes #23788: PPI::Statement::location() returns undef for C<({})>. Test::Object->register( class => 'PPI::Document', tests => 1, code => \&defined_location, ); sub defined_location { my $document = shift; my $bad = $document->find( sub { not defined $_[1]->location } ); is( $bad, '', '->location always defined' ); } ##################################################################### # Process a .code/.dump file pair # plan: 2 + 14 * npairs sub run_testdir { my $pkg = shift; my $testdir = catdir(@_); # Does the test directory exist? ok( (-e $testdir and -d $testdir and -r $testdir), "Test directory $testdir found" ); # Find the .code test files local *TESTDIR; opendir( TESTDIR, $testdir ) or die "opendir: $!"; my @code = map { catfile( $testdir, $_ ) } sort grep { /\.code$/ } readdir(TESTDIR); closedir( TESTDIR ) or die "closedir: $!"; ok( scalar @code, 'Found at least one code file' ); foreach my $codefile ( @code ) { # Does the .code file have a matching .dump file my $dumpfile = $codefile; $dumpfile =~ s/\.code$/\.dump/; my $codename = $codefile; $codename =~ s/\.code$//; ok( (-f $dumpfile and -r $dumpfile), "$codename: Found matching .dump file" ); # Create the lexer and get the Document object my $document = PPI::Document->new( $codefile ); ok( $document, "$codename: Lexer->Document returns true" ); ok( _INSTANCE($document, 'PPI::Document'), "$codename: Object isa PPI::Document" ); my $rv; local *CODEFILE; SKIP: { skip "No Document to test", 12 unless $document; # Index locations ok( $document->index_locations, "$codename: ->index_locations ok" ); # Check standard things object_ok( $document ); # 7 tests contained within # Get the dump array ref for the Document object my $Dumper = PPI::Dumper->new( $document ); ok( _INSTANCE($Dumper, 'PPI::Dumper'), "$codename: Object isa PPI::Dumper" ); my @dump_list = $Dumper->list; ok( scalar @dump_list, "$codename: Got dump content from dumper" ); # Try to get the .dump file array local *DUMP; open( DUMP, '<', $dumpfile ) or die "open: $!"; my @content = ; close( DUMP ) or die "close: $!"; chomp @content; # Compare the two is_deeply( \@dump_list, \@content, "$codename: Generated dump matches stored dump" ); # Also, do a round-trip check $rv = open( CODEFILE, '<', $codefile ); ok( $rv, "$codename: Opened file" ); } SKIP: { unless ( $document and $rv ) { skip "Missing file", 1; } my $source = do { local $/ = undef; }; close CODEFILE; $source =~ s/(?:\015{1,2}\012|\015|\012)/\n/g; is( $document->serialize, $source, "$codename: Round-trip back to source was ok" ); } } } ##################################################################### # Process a .code/.dump file pair # plan: 2 + 14 * npairs sub increment_testdir { my $pkg = shift; my $testdir = catdir(@_); # Does the test directory exist? ok( (-e $testdir and -d $testdir and -r $testdir), "Test directory $testdir found" ); # Find the .code test files local *TESTDIR; opendir( TESTDIR, $testdir ) or die "opendir: $!"; my @code = map { catfile( $testdir, $_ ) } sort grep { /\.code$/ } readdir(TESTDIR); closedir( TESTDIR ) or die "closedir: $!"; ok( scalar @code, 'Found at least one code file' ); foreach my $codefile ( @code ) { # Does the .code file have a matching .dump file my $codename = $codefile; $codename =~ s/\.code$//; # Load the file local *CODEFILE; local $/ = undef; open( CODEFILE, $codefile ) or die "open: $!"; my $buffer = ; close( CODEFILE ) or die "close: $!"; # Cover every possible transitional state in # the regression test code fragments. foreach my $chars ( 1 .. length($buffer) ) { my $string = substr( $buffer, 0, $chars ); my $document = eval { PPI::Document->new( \$string ); }; is( $@ => '', "$codename: $chars chars ok", ); is( ref($document) => 'PPI::Document', "$codename: $chars chars document", ); is( $document->serialize => $string, "$codename: $chars char roundtrip", ); } } } 1;