File Coverage

blib/lib/OOP/Perlish/Class/AutoTest.pm
Criterion Covered Total %
statement 92 97 94.8
branch 16 30 53.3
condition 4 18 22.2
subroutine 18 18 100.0
pod 1 1 100.0
total 131 164 79.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2 5     5   4486 use warnings;
  5         9  
  5         154  
3 5     5   25 use strict;
  5         6  
  5         237  
4             {
5              
6             package OOP::Perlish::Class::AutoTest;
7 5     5   34 use warnings;
  5         10  
  5         124  
8 5     5   23 use strict;
  5         9  
  5         147  
9             ############################################################################################
10             ## Simple, somewhat hacky way of running unit-tests in batch to make testing framework
11             ## easier to manage.
12             ############################################################################################
13              
14 5     5   6404 use Data::Dumper;
  5         64644  
  5         403  
15 5     5   53 use B;
  5         9  
  5         278  
16 5     5   27 use File::Basename qw(dirname fileparse basename);
  5         10  
  5         597  
17 5     5   105 use File::Find;
  5         9  
  5         290  
18 5     5   6431 use Test::Class;
  5         238023  
  5         2603  
19              
20             ############################################################################################
21             ## Provide an import method to handle testing parameters.
22             ############################################################################################
23             sub import
24             {
25 5     5   77 my ( $self, @tags ) = @_;
26 5 50       48 my %test_opts = @tags if( scalar @tags % 2 == 0 );
27 5         10 my @packages;
28             my @test_directories;
29 0         0 my @exclude;
30              
31 5 50       33 return unless( scalar keys %test_opts );
32              
33 5 50       32 if( exists $test_opts{package} ) {
34 5 50 33     87 if( $test_opts{package} && ref( $test_opts{package} ) eq 'ARRAY' ) {
    50 33        
35 0         0 @packages = @{ $test_opts{package} };
  0         0  
36             }
37             elsif( $test_opts{package} && !ref( $test_opts{package} ) ) {
38 5         14 @packages = ( $test_opts{package} );
39             }
40             }
41 5 50       26 if( exists $test_opts{tests} ) {
42 5 50 33     49 if( $test_opts{tests} && ref( $test_opts{tests} ) eq 'ARRAY' ) {
    0 0        
43 5         11 @test_directories = @{ $test_opts{tests} };
  5         21  
44             }
45             elsif( $test_opts{tests} && !ref( $test_opts{tests} ) ) {
46 0         0 @test_directories = ( $test_opts{tests} );
47             }
48             }
49 5 50       19 if( exists $test_opts{exclude} ) {
50 5 50 33     42 if( $test_opts{exclude} && ref( $test_opts{exclude} ) eq 'ARRAY' ) {
    0 0        
51 5         7 @exclude = @{ $test_opts{exclude} };
  5         16  
52             }
53             elsif( $test_opts{exclude} && !ref( $test_opts{exclude} ) ) {
54 0         0 @exclude = ( $test_opts{exclude} );
55             }
56             }
57              
58 5         22 for my $package ( $self->_find_test_modules( \@packages, \@test_directories, \@exclude ) ) {
59 17 50       2007 eval "require $package" || die "$@";
60             }
61             }
62              
63             ############################################################################################
64             ## Divine information from whence a subroutine is loaded
65             ############################################################################################
66             sub _introspect_sub
67             {
68 6     6   17 my ( $self, $sub ) = @_;
69 6         83 my $cv = B::svref_2object($sub);
70              
71 6         336 return ( ( $cv->STASH()->NAME(), $cv->FILE() ) );
72             }
73              
74             ############################################################################################
75             ## using introspect_sub, find any subroutine, introspect it to learn which package_name and
76             ## file_name it comes from.
77             ############################################################################################
78             sub _find_package_filename
79             {
80 5     5   19 my ( $self, $package ) = @_;
81              
82 5         8 my ( $package_name, $file_name );
83              
84 5         454 eval "require $package";
85              
86 5     5   153 no strict 'refs';
  5         17  
  5         289  
87 5         21 my %sym = %{ *{ '::' . $package . '::' } };
  5         8  
  5         231  
88 5     5   24 use strict;
  5         9  
  5         3396  
89              
90 5         54 while( my ( $k, $v ) = each %sym ) {
91 8 100       14 if( defined *{$v}{CODE} ) {
  8         56  
92 6         12 ( $package_name, $file_name ) = $self->_introspect_sub( *{$v}{CODE} );
  6         57  
93 5 100       158 last if( $package_name eq $package );
94             }
95             }
96 4         106 return $file_name;
97             }
98              
99             ############################################################################################
100             ## Search the path of a module for subdirectories matching paths in 'tests => []', yet not
101             ## matching $exclude_re
102             ############################################################################################
103             sub _find_test_modules
104             {
105 5     5   12 my ( $self, $packages, $test_directories, $exclude ) = @_;
106 5         7 my @packages = @{$packages};
  5         13  
107 5         8 my @test_directories = @{$test_directories};
  5         14  
108 5         7 my @exclude = @{$exclude};
  5         13  
109              
110 5         12 my @test_packages;
111              
112 5         12 for my $package (@packages) {
113 5         17 my $package_path = $self->_find_package_filename($package);
114 4         351 my $search_path = join( '/', ( fileparse( $package_path, qr/\.pm/ ) )[ 1, 0 ] );
115              
116             File::Find::find(
117             {
118             follow => 0,
119             no_chdir => 1,
120             bydepth => 1,
121             untaint => 1,
122             untaint_exclude => 1,
123             dangling_symlinks => undef,
124             preprocess => sub {
125 4     4   22 my $exclude_re = '(?:' . join( '|', @exclude ) . ')';
126 4         8 return grep { !/$exclude_re/ } @_;
  27         229  
127             },
128             wanted => sub {
129 21 100   21   193 m/\.pm$/ && do {
130 17         149 ( my $module = $_ ) =~ s/^\Q$search_path\E//;
131 17         80 $module =~ s,/+,/,g;
132 17         53 $module =~ s,/,::,g;
133 17         55 $module =~ s/\.pm$//;
134 17         43 $module = $package . $module;
135 17         253 push @test_packages, $module;
136             };
137             },
138             },
139 4         96 map { $search_path . '/' . $_ } @test_directories,
  4         1396  
140             );
141             }
142 4         29 return @test_packages;
143             } ## end sub _find_test_modules
144              
145             ############################################################################################
146             ## convenience function to invoke Test::Class->runtests() indirectly
147             ############################################################################################
148             sub runtests
149             {
150 4     4 1 533 my ($self) = @_;
151 4         42 Test::Class->runtests();
152             }
153             }
154             1;
155             __END__