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   143885 use strict;
  5         10  
  5         105  
4 5     5   15 use warnings;
  5         5  
  5         105  
5              
6 5     5   2460 use Capture::Tiny qw(capture);
  5         20565  
  5         235  
7 5     5   2125 use Text::Diff;
  5         29285  
  5         230  
8 5     5   25 use File::Path qw(remove_tree);
  5         5  
  5         195  
9 5     5   2010 use File::Copy qw(copy move);
  5         9325  
  5         235  
10 5     5   15 use File::Spec;
  5         5  
  5         2575  
11              
12             sub new {
13 14     14 1 47723 my $class = shift;
14 14         50 my (%params) = @_;
15              
16 14         28 my $self = {};
17 14         23 bless $self, $class;
18              
19 14   50     97 $self->{verbose} = $params{verbose} || 0;
20 14   100     65 $self->{remove} = $params{remove} || 0;
21 14   50     66 $self->{timeout} = $params{timeout} || 10;
22 14   50     42 $self->{root} = $params{root} || '.';
23 14   50     161 $self->{command} = $params{command} || 'prove -l t';
24              
25 14         35 return $self;
26             }
27              
28             sub run {
29 14     14 1 46 my $self = shift;
30              
31 14         118 my $mutants_dir = File::Spec->catfile($self->{root}, 'mutants');
32 14         136 my @mutants = $self->_read_dir($mutants_dir);
33              
34 14         22 my $total = @mutants;
35 14         14 my $current = 1;
36 14         14 my $failed = 0;
37 14         29 foreach my $mutant (@mutants) {
38 14         572 print "($current/$total) $mutant ... ";
39 14         29 $current++;
40              
41 14         417 my ($mutant_id, $orig_file) = $mutant =~ m{^\Q$mutants_dir\E/(.*?)/(.*$)};
42 14         145 $orig_file = File::Spec->catfile($self->{root}, $orig_file);
43 14         150 move($orig_file, "$orig_file.bak");
44              
45 14         962 copy($mutant, $orig_file);
46              
47 14         2900 my $rv = $self->_run_command;
48              
49 10 100       65 if ($rv == 0) {
    50          
50 3         24 $failed++;
51 3         591 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       41 if ($self->{remove}) {
62 1         566 remove_tree("$mutants_dir/$mutant_id");
63             }
64              
65 7         1404 print "ok\n";
66             }
67              
68 10         3262 move("$orig_file.bak", $orig_file);
69             }
70              
71 10 100       1459 if ($failed) {
72 3         333 print "Result: FAIL ($failed/$total)\n";
73              
74 3         48 return 255;
75             }
76             else {
77 7         656 print "Result: PASS\n";
78              
79 7         94 return 0;
80             }
81             }
82              
83             sub _run_command {
84 14     14   81 my $self = shift;
85              
86 14         19 my $ALARM_EXCEPTION = "alarm timeout";
87              
88 14         8258 my $pid = fork;
89 14 100       261 if ($pid == 0) {
90 4         197 setpgrp(0, 0);
91              
92             capture {
93 4     4   0 exec $self->{command};
94 4         922 };
95              
96 0         0 exit 0;
97             }
98              
99 10         196 eval {
100 10     0   622 local $SIG{ALRM} = sub { die $ALARM_EXCEPTION };
  0         0  
101 10         63 alarm $self->{timeout};
102              
103 10         4789347 waitpid($pid, 0);
104              
105 10         231 alarm 0;
106             };
107              
108 10         67 my $rv = $?;
109              
110 10 50       63 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         70 return $rv;
121             }
122              
123             sub _read_dir {
124 42     42   49 my $self = shift;
125 42         38 my ($dir) = @_;
126              
127 42 50       977 opendir(my $dh, $dir) || die "Can't open directory '$dir'";
128 42         39 my @files;
129 42         522 while ($_ = readdir $dh) { #using without explicit assign requires perl 5.12
130 126 100       400 next if /^\./;
131              
132 42         79 my $file = "$dir/$_";
133              
134 42 100       397 if (-d $file) {
135 28         84 push @files, $self->_read_dir($file);
136             }
137             else {
138 14         41 push @files, $file;
139             }
140             }
141 42         201 closedir $dh;
142              
143 42         159 return @files;
144             }
145              
146             1;
147             __END__