File Coverage

blib/lib/Test2/Harness/Job.pm
Criterion Covered Total %
statement 100 106 94.3
branch 36 44 81.8
condition 10 19 52.6
subroutine 21 21 100.0
pod 4 7 57.1
total 171 197 86.8


line stmt bran cond sub pod time code
1             package Test2::Harness::Job;
2 25     25   248245 use strict;
  25         25  
  25         541  
3 25     25   55 use warnings;
  25         130  
  25         831  
4              
5             our $VERSION = '0.000013';
6              
7 25     25   75 use Carp qw/croak/;
  25         25  
  25         826  
8 25     25   942 use Time::HiRes qw/time/;
  25         1886  
  25         76  
9 25         103 use Test2::Util::HashBase qw{
10             id file listeners parser proc result
11             event_timeout
12             _done _timeout _timeout_notified
13 25     25   1992 };
  25         25  
14              
15 25     25   13211 use Test2::Event::ParseError;
  25         26  
  25         447  
16 25     25   7582 use Test2::Event::ProcessStart;
  25         27  
  25         395  
17 25     25   6844 use Test2::Event::ProcessFinish;
  25         26  
  25         597  
18 25     25   7405 use Test2::Event::Subtest;
  25         38771  
  25         441  
19 25     25   7475 use Test2::Event::TimeoutReset;
  25         36  
  25         590  
20 25     25   8364 use Test2::Event::UnexpectedProcessExit;
  25         47  
  25         423  
21 25     25   7377 use Test2::Harness::Result;
  25         25  
  25         16536  
22              
23             sub init {
24 645     645 0 311913 my $self = shift;
25              
26             croak "job 'id' is required"
27 645 100       2193 unless $self->{+ID};
28              
29             croak "job 'file' is required"
30 643 100       1709 unless $self->{+FILE};
31              
32 641   100     1733 $self->{+LISTENERS} ||= [];
33              
34             $self->{+RESULT} ||= Test2::Harness::Result->new(
35             file => $self->{+FILE},
36             name => $self->{+FILE},
37 641   33     8120 job => $self->{+ID},
38             );
39             }
40              
41             sub start {
42 627     627 1 1760 my $self = shift;
43 627         3314 my %params = @_;
44              
45 627         1108 my $id = $self->{+ID};
46 627         1099 my ($runner, $start_args, $parser_class) = @params{qw/runner start_args parser_class/};
47              
48             my ($proc, @events) = $runner->start(
49 627         5765 $self->{+FILE},
50             %$start_args,
51             job => $id,
52             );
53              
54 607 50       36760 die "Failed to get a proc object" unless $proc;
55              
56 607         4984 my $parser = $parser_class->new(
57             job => $id,
58             proc => $proc,
59             );
60              
61 607 50       2154 die "Failed to get a parser object" unless $parser;
62              
63 607         1373 $self->{+PROC} = $proc;
64 607         1166 $self->{+PARSER} = $parser;
65              
66 607         6231 my $start = Test2::Event::ProcessStart->new(file => $self->{+FILE});
67 607         3619 $self->notify($start, @events);
68             }
69              
70             sub notify {
71 36077     36077 1 34619 my $self = shift;
72 36077         37088 my (@events) = @_;
73              
74 36077 100       58758 return unless @events;
75              
76 15648         18553 for my $e (@events) {
77 19913         13445 $_->($self, $e) for @{$self->{+LISTENERS}};
  19913         57917  
78             }
79             # The ProcessFinish event contains a reference to the result, so if we add
80             # that event to the result we end up with a circular ref.
81 15648         19409 $self->{+RESULT}->add_events(grep { !$_->isa('Test2::Event::ProcessFinish') } @events);
  19913         80630  
82             }
83              
84             sub step {
85 34793     34793 1 47411 my $self = shift;
86 34793         105860 my @events = $self->{+PARSER}->step;
87 34793         77577 $self->notify(@events);
88 34793 100 66     143198 if (@events && $self->{+_TIMEOUT}) {
89 48         179 delete $self->{+_TIMEOUT};
90             $self->notify(Test2::Event::TimeoutReset->new(file => $self->{+FILE}))
91 48 50       389 if $self->{+_TIMEOUT_NOTIFIED};
92             }
93 34793 100       113528 return @events ? 1 : 0;
94             }
95              
96             sub timeout {
97 2339     2339 0 7620 my $self = shift;
98              
99             # No timeout if the process exits badly
100 2339 100       9233 return 0 if $self->{+PROC}->exit;
101              
102 2337         10676 my $r = $self->{+RESULT};
103 2337         8216 my $plans = $r->plans;
104              
105 2337 100 66     20281 if ($plans && @$plans) {
106 613         1192 my $plan = $plans->[0];
107 613         2319 my $max = ($plan->sets_plan)[0];
108              
109 613 100       4366 return 0 unless $max;
110 573 100       2995 return 0 if $max == $r->total;
111             }
112              
113             # 60 seconds if all else fails.
114 1726   50     11797 return $self->{+EVENT_TIMEOUT} || 60;
115             }
116              
117             sub is_done {
118 29531     29531 1 25783 my $self = shift;
119              
120 29531 100       45360 return 1 if $self->{+_DONE};
121              
122 29529 100       57057 return $self->_incomplete_timeout
123             if $self->proc->is_done;
124              
125 24247         38637 return $self->_event_timeout;
126             }
127              
128             sub _event_timeout {
129 24247     24247   18367 my $self = shift;
130              
131 24247 50       82095 my $timeout = $self->{+EVENT_TIMEOUT}
132             or return 0;
133              
134 0 0       0 return 0 if $self->step;
135 0   0     0 $self->{+_TIMEOUT} ||= time;
136              
137 0 0       0 return 0 if $timeout > (time - $self->{+_TIMEOUT});
138              
139             # We timed out!
140             $self->notify(
141             Test2::Event::UnexpectedProcessExit->new(
142             error => "Process timed out after $timeout seconds with no events...",
143 0         0 file => $self->{+FILE},
144             ),
145             );
146              
147 0         0 $self->{+PROC}->force_kill;
148              
149 0         0 return $self->finish;
150             }
151              
152             sub _incomplete_timeout {
153 5282     5282   5631 my $self = shift;
154              
155             # If the process finished but forked a subprocess that is still producing
156             # output, then we might see something when we call ->step. This is fairly
157             # pathological, but we try to handle it.
158 5282 100       8634 return 0 if $self->step;
159              
160 2337         4710 my $proc = $self->{+PROC};
161              
162 2337 100       6505 if (my $timeout = $self->timeout) {
163 1730 100       4056 unless ($self->{+_TIMEOUT}) {
164 50         356 $self->{+_TIMEOUT} = time;
165              
166             $self->notify(
167             Test2::Event::UnexpectedProcessExit->new(
168             error => "Process has exited but the event stream does not appear complete. Waiting $timeout seconds...",
169             file => $self->{+FILE},
170             ),
171 50 100 66     988 ) if $timeout >= 1 && !$self->{+_TIMEOUT_NOTIFIED}++;
172              
173 50         631 return 0;
174             }
175              
176 1680 100       14909 return 0 if $timeout > (time - $self->{+_TIMEOUT});
177             }
178              
179 609         5677 return $self->finish;
180             }
181              
182             sub finish {
183 609     609 0 794 my $self = shift;
184              
185 609         2570 $self->{+_DONE} = 1;
186              
187 609         1019 my $proc = $self->{+PROC};
188 609         1630 $self->{+RESULT}->stop($proc->exit);
189              
190 609         3173 $self->notify(Test2::Event::ProcessFinish->new(file => $proc->file, result => $self->{+RESULT}));
191              
192 609         7328 return 1;
193             }
194              
195             1;
196              
197             __END__