File Coverage

blib/lib/Devel/Mutator/Command/Test.pm
Criterion Covered Total %
statement 85 94 90.4
branch 15 20 75.0
condition 6 10 60.0
subroutine 12 13 92.3
pod 2 2 100.0
total 120 139 86.3


line stmt bran cond sub pod time code
1             package Devel::Mutator::Command::Test;
2              
3 5     5   151345 use strict;
  5         10  
  5         120  
4 5     5   20 use warnings;
  5         10  
  5         115  
5              
6 5     5   2035 use Capture::Tiny qw(capture);
  5         20690  
  5         235  
7 5     5   1990 use Text::Diff;
  5         29745  
  5         395  
8 5     5   35 use File::Path qw(remove_tree);
  5         5  
  5         215  
9 5     5   2565 use File::Copy qw(copy move);
  5         7845  
  5         220  
10 5     5   20 use File::Spec;
  5         5  
  5         2565  
11              
12             sub new {
13 14     14 1 48115 my $class = shift;
14 14         53 my (%params) = @_;
15              
16 14         28 my $self = {};
17 14         38 bless $self, $class;
18              
19 14   50     128 $self->{verbose} = $params{verbose} || 0;
20 14   100     65 $self->{remove} = $params{remove} || 0;
21 14   50     67 $self->{timeout} = $params{timeout} || 10;
22 14   50     54 $self->{root} = $params{root} || '.';
23 14   50     172 $self->{command} = $params{command} || 'prove -l t';
24              
25 14         43 return $self;
26             }
27              
28             sub run {
29 14     14 1 54 my $self = shift;
30              
31 14         141 my $mutants_dir = File::Spec->catfile($self->{root}, 'mutants');
32 14         145 my @mutants = $self->_read_dir($mutants_dir);
33              
34 14         14 my $total = @mutants;
35 14         19 my $current = 1;
36 14         19 my $failed = 0;
37 14         28 foreach my $mutant (@mutants) {
38 14         331 print "($current/$total) $mutant ... ";
39 14         23 $current++;
40              
41 14         401 my ($mutant_id, $orig_file) = $mutant =~ m{^\Q$mutants_dir\E/(.*?)/(.*$)};
42 14         129 $orig_file = File::Spec->catfile($self->{root}, $orig_file);
43 14         150 move($orig_file, "$orig_file.bak");
44              
45 14         1031 copy($mutant, $orig_file);
46              
47 14         2949 my $rv = $self->_run_command;
48              
49 10 100       54 if ($rv == 0) {
    50          
50 3         18 $failed++;
51 3         54 print "not ok\n";
52              
53 3         60 print diff($mutant, "$orig_file.bak");
54             }
55             elsif ($rv == -1) {
56 0         0 print "n/a (timeout $self->{timeout}s)\n";
57              
58 0         0 print diff($mutant, "$orig_file.bak");
59             }
60             else {
61 7 100       34 if ($self->{remove}) {
62 1         541 remove_tree("$mutants_dir/$mutant_id");
63             }
64              
65 7         602 print "ok\n";
66             }
67              
68 10         2012 move("$orig_file.bak", $orig_file);
69             }
70              
71 10 100       1611 if ($failed) {
72 3         21 print "Result: FAIL ($failed/$total)\n";
73              
74 3         30 return 255;
75             }
76             else {
77 7         284 print "Result: PASS\n";
78              
79 7         80 return 0;
80             }
81             }
82              
83             sub _run_command {
84 14     14   89 my $self = shift;
85              
86 14         14 my $ALARM_EXCEPTION = "alarm timeout";
87              
88 14         7443 my $pid = fork;
89 14 100       306 if ($pid == 0) {
90 4         298 setpgrp(0, 0);
91              
92             capture {
93 4     4   0 exec $self->{command};
94 4         1349 };
95              
96 0         0 exit 0;
97             }
98              
99 10         203 eval {
100 10     0   759 local $SIG{ALRM} = sub { die $ALARM_EXCEPTION };
  0         0  
101 10         88 alarm $self->{timeout};
102              
103 10         6556727 waitpid($pid, 0);
104              
105 10         258 alarm 0;
106             };
107              
108 10         72 my $rv = $?;
109              
110 10 50       59 if ($@) {
111 0         0 alarm 0;
112              
113 0 0       0 if ($@ =~ quotemeta($ALARM_EXCEPTION)) {
114 0         0 kill -9, $pid;
115 0         0 $rv = -1;
116             }
117 0         0 else { die; }
118             }
119              
120 10         132 return $rv;
121             }
122              
123             sub _read_dir {
124 42     42   52 my $self = shift;
125 42         45 my ($dir) = @_;
126              
127 42 50       832 opendir(my $dh, $dir) || die "Can't open directory '$dir'";
128 42         40 my @files;
129 42         538 while ($_ = readdir $dh) { #using without explicit assign requires perl 5.12
130 126 100       408 next if /^\./;
131              
132 42         81 my $file = "$dir/$_";
133              
134 42 100       422 if (-d $file) {
135 28         81 push @files, $self->_read_dir($file);
136             }
137             else {
138 14         43 push @files, $file;
139             }
140             }
141 42         186 closedir $dh;
142              
143 42         134 return @files;
144             }
145              
146             1;
147             __END__