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   250215 use strict;
  5         15  
  5         190  
4 5     5   25 use warnings;
  5         10  
  5         190  
5              
6 5     5   3285 use Capture::Tiny qw(capture);
  5         38425  
  5         620  
7 5     5   4390 use Text::Diff;
  5         74915  
  5         550  
8 5     5   60 use File::Path qw(remove_tree);
  5         15  
  5         390  
9 5     5   5395 use File::Copy qw(copy move);
  5         13700  
  5         415  
10 5     5   35 use File::Spec;
  5         5  
  5         4125  
11              
12             sub new {
13 14     14 1 77075 my $class = shift;
14 14         71 my (%params) = @_;
15              
16 14         38 my $self = {};
17 14         50 bless $self, $class;
18              
19 14   50     177 $self->{verbose} = $params{verbose} || 0;
20 14   100     86 $self->{remove} = $params{remove} || 0;
21 14   50     185 $self->{timeout} = $params{timeout} || 10;
22 14   50     71 $self->{root} = $params{root} || '.';
23 14   50     236 $self->{command} = $params{command} || 'prove -l t';
24              
25 14         58 return $self;
26             }
27              
28             sub run {
29 14     14 1 84 my $self = shift;
30              
31 14         215 my $mutants_dir = File::Spec->catfile($self->{root}, 'mutants');
32 14         224 my @mutants = $self->_read_dir($mutants_dir);
33              
34 14         26 my $total = @mutants;
35 14         25 my $current = 1;
36 14         28 my $failed = 0;
37 14         44 foreach my $mutant (@mutants) {
38 14         1322 print "($current/$total) $mutant ... ";
39 14         64 $current++;
40              
41 14         607 my ($mutant_id, $orig_file) = $mutant =~ m{^$mutants_dir/(.*?)/(.*$)};
42 14         208 $orig_file = File::Spec->catfile($self->{root}, $orig_file);
43 14         248 move($orig_file, "$orig_file.bak");
44              
45 14         1544 copy($mutant, $orig_file);
46              
47 14         16308 my $rv = $self->_run_command;
48              
49 10 100       92 if ($rv == 0) {
    50          
50 3         24 $failed++;
51 3         1062 print "not ok\n";
52              
53 3         84 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       53 if ($self->{remove}) {
62 1         1065 remove_tree("$mutants_dir/$mutant_id");
63             }
64              
65 7         1044 print "ok\n";
66             }
67              
68 10         3770 move("$orig_file.bak", $orig_file);
69             }
70              
71 10 100       2326 if ($failed) {
72 3         315 print "Result: FAIL ($failed/$total)\n";
73              
74 3         54 return 255;
75             }
76             else {
77 7         501 print "Result: PASS\n";
78              
79 7         122 return 0;
80             }
81             }
82              
83             sub _run_command {
84 14     14   136 my $self = shift;
85              
86 14         27 my $ALARM_EXCEPTION = "alarm timeout";
87              
88 14         26122 my $pid = fork;
89 14 100       490 if ($pid == 0) {
90 4         319 setpgrp(0, 0);
91              
92             capture {
93 4     4   0 exec $self->{command};
94 4         1544 };
95              
96 0         0 exit 0;
97             }
98              
99 10         348 eval {
100 10     0   1226 local $SIG{ALRM} = sub { die $ALARM_EXCEPTION };
  0         0  
101 10         103 alarm $self->{timeout};
102              
103 10         8258151 waitpid($pid, 0);
104              
105 10         434 alarm 0;
106             };
107              
108 10         132 my $rv = $?;
109              
110 10 50       96 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         129 return $rv;
121             }
122              
123             sub _read_dir {
124 42     42   61 my $self = shift;
125 42         88 my ($dir) = @_;
126              
127 42 50       1227 opendir(my $dh, $dir) || die "Can't open directory '$dir'";
128 42         55 my @files;
129 42         750 while (readdir $dh) {
130 126 100       565 next if /^\./;
131              
132 42         99 my $file = "$dir/$_";
133              
134 42 100       609 if (-d $file) {
135 28         120 push @files, $self->_read_dir($file);
136             }
137             else {
138 14         56 push @files, $file;
139             }
140             }
141 42         444 closedir $dh;
142              
143 42         233 return @files;
144             }
145              
146             1;
147             __END__