File Coverage

blib/lib/App/autotest.pm
Criterion Covered Total %
statement 81 95 85.2
branch 11 20 55.0
condition 2 3 66.6
subroutine 19 22 86.3
pod 0 10 0.0
total 113 150 75.3


line stmt bran cond sub pod time code
1 3     3   216896 use strict;
  3         5  
  3         71  
2 3     3   9 use warnings;
  3         3  
  3         92  
3              
4             package App::autotest;
5             $App::autotest::VERSION = '0.006';
6             # ABSTRACT: main package for the autotest tool
7              
8 3     3   1253 use Moose;
  3         876552  
  3         21  
9 3     3   14761 use File::Find;
  3         5  
  3         198  
10 3     3   13 use File::Spec;
  3         5  
  3         88  
11 3     3   13 use Cwd;
  3         3  
  3         142  
12 3     3   1699 use File::ChangeNotify;
  3         488091  
  3         72  
13 3     3   19 use List::MoreUtils;
  3         4  
  3         26  
14              
15 3     3   1617 use App::autotest::Test::Runner;
  3         7  
  3         93  
16 3     3   1423 use App::autotest::Test::Runner::Result::History;
  3         7  
  3         2392  
17              
18             has test_directory => ( is => 'rw', isa => 'Str', default => 't' );
19              
20             has watcher => (
21             is => 'rw',
22             isa => 'File::ChangeNotify::Watcher',
23             default => sub {
24             File::ChangeNotify->instantiate_watcher(
25             directories => ['t', 'lib'],
26             filter => qr/(?:\.t|\.pm)$/,
27             );
28             }
29             );
30              
31             has after_change_or_new_hook => (
32             is => 'rw',
33             isa => 'CodeRef',
34             default => sub {
35             sub { 0 }
36             }
37             );
38              
39             has history => ( is => 'rw',
40             default => sub { App::autotest::Test::Runner::Result::History->new } );
41              
42             has test_runner => ( is => 'rw',
43             default => sub { App::autotest::Test::Runner->new });
44              
45             sub run {
46 0     0 0 0 my ($self) = @_;
47              
48 0         0 $self->run_tests_upon_startup;
49 0         0 $self->run_tests_upon_change_or_creation;
50             }
51              
52             sub run_tests_upon_startup {
53 2     2 0 1188 my ($self) = @_;
54              
55 2         49 my $all_test_programs = $self->all_test_programs( $self->test_directory );
56              
57 2         36 $self->run_tests(@$all_test_programs);
58             }
59              
60             sub run_tests_upon_change_or_creation {
61 0     0 0 0 my ($self) = @_;
62              
63 0         0 while (1) {
64              
65 0         0 my $test_files = $self->pm_to_t( $self->changed_and_new_files );
66 0 0       0 next unless @$test_files;
67              
68 0         0 $self->run_tests( @$test_files );
69              
70 0 0       0 last if $self->after_change_or_new_hook->();
71             }
72 0         0 return 1;
73             }
74              
75             sub pm_to_t {
76 2     2 0 1669 my ($self, $change_files) = @_;
77            
78 2         51 my $all_test_programs = $self->all_test_programs( $self->test_directory );
79 2         11 my @all_test_parts_map = map { +{ path => $_, parts => $self->break_path($_) } } @$all_test_programs;
  7         8  
80              
81 2         33 my @test_paths;
82              
83 2         3 foreach ( @$change_files ) {
84              
85 3 50       6 if ( $_ =~ /\.t$/ ) {
86 0         0 push @test_paths, $_;
87 0         0 next;
88             }
89              
90 3         5 my $test_path = $self->find_max_rate_of_concordance($self->break_path($_), \@all_test_parts_map);
91              
92 3 50       9 if (defined $test_path) {
93 3         4 push @test_paths, $test_path;
94             }
95             }
96              
97 2         13 return \@test_paths;
98             }
99              
100             sub find_max_rate_of_concordance {
101 3     3 0 2 my ($self, $file_parts, $all_test_programs) = @_;
102              
103 12         14 my @sorted_test_data = sort { $a->{rate} <=> $b->{rate} }
  11         15  
104             map {
105 3         4 +{
106             path => $_->{path},
107             rate => $self->calc_rate_of_concordance($file_parts, $_->{parts})
108             }
109             }
110             @$all_test_programs;
111              
112 3 50       5 return unless @sorted_test_data;
113              
114 3         2 my $max_data = $sorted_test_data[-1];
115              
116 3 50       10 return $max_data->{rate} == 0 ? undef : $max_data->{path};
117             }
118              
119             sub calc_rate_of_concordance {
120 14     14 0 2427 my ($self, $target_parts, $cmp_parts) = @_;
121              
122 14 100       18 return 0 unless ( @$target_parts );
123              
124 13         10 my %target_map = map { $_ => 1 } @$target_parts;
  59         62  
125              
126 13         13 foreach ( @$cmp_parts ) {
127 36         36 $target_map{$_}++;
128             }
129              
130 13         16 return scalar( grep { $_ >= 2 } values %target_map ) / scalar @$target_parts;
  88         89  
131             }
132              
133             sub break_path {
134 10     10 0 11 my ($self, $path) = @_;
135              
136 10         17 (my $lc_path = $path) =~ s/([A-Z])/_\l$1/g;
137 10         77 return [ List::MoreUtils::uniq( split(qr/[\\\/\.\-_]/, $lc_path) ) ];
138             }
139              
140             sub changed_and_new_files {
141 2     2 0 3 my ($self) = @_;
142              
143 2         2 my @files;
144 2         44 for my $event ( $self->watcher->wait_for_events() ) {
145 2         55 my $type = $event->type();
146 2   66     41 my $file_changed = $type eq 'create' || $type eq 'modify';
147 2 50       5 push @files, $event->path() if $file_changed;
148             }
149              
150 2         36 return \@files;
151             }
152              
153             {
154             my @files;
155              
156             sub all_test_programs {
157 5     5 0 841 my ($self) = @_;
158              
159 5         8 @files = (); # throw away result of last call
160 5         104 find( { wanted => \&_wanted, no_chdir => 1 },
161             './' . $self->test_directory );
162              
163 5         21 return \@files;
164             }
165              
166             sub _wanted {
167 57     57   139 my $cwd = getcwd();
168 57         39 my $name = $File::Find::name;
169              
170 57 100       851 push @files, File::Spec->catfile( $cwd, $name ) if $name =~ m{\.t$};
171             }
172              
173             }
174              
175             sub run_tests {
176 13     13 0 29158 my ($self, @tests)=@_;
177              
178 13         408 my $result=$self->test_runner->run(@tests);
179 13         516 $self->history->perpetuate($result);
180              
181 13 100       379 if ($self->history->things_just_got_better) {
182 2         83 $self->print("Things just got better.\n");
183             }
184             }
185              
186             sub print {
187 0     0     my ($self, @rest)=@_;
188 0           print @rest;
189             }
190              
191             1;
192              
193             __END__
194              
195             =pod
196              
197             =encoding UTF-8
198              
199             =head1 NAME
200              
201             App::autotest - main package for the autotest tool
202              
203             =head1 VERSION
204              
205             version 0.006
206              
207             =head1 AUTHOR
208              
209             Gregor Goldbach <glauschwuffel@nomaden.org>
210              
211             =head1 COPYRIGHT AND LICENSE
212              
213             This software is copyright (c) 2015 by Gregor Goldbach.
214              
215             This is free software; you can redistribute it and/or modify it under
216             the same terms as the Perl 5 programming language system itself.
217              
218             =cut