File Coverage

lib/Git/Background/Future.pm
Criterion Covered Total %
statement 80 84 95.2
branch 21 22 95.4
condition 12 14 85.7
subroutine 15 16 93.7
pod 9 9 100.0
total 137 145 94.4


line stmt bran cond sub pod time code
1             # vim: ts=4 sts=4 sw=4 et: syntax=perl
2             #
3             # Copyright (c) 2021-2023 Sven Kirmess
4             #
5             # Permission to use, copy, modify, and distribute this software for any
6             # purpose with or without fee is hereby granted, provided that the above
7             # copyright notice and this permission notice appear in all copies.
8             #
9             # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10             # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11             # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12             # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13             # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14             # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15             # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16              
17 13     13   104516 use 5.010;
  13         65  
18 13     13   88 use strict;
  13         31  
  13         286  
19 13     13   73 use warnings;
  13         39  
  13         869  
20              
21             package Git::Background::Future;
22              
23             our $VERSION = '0.007_01';
24              
25 13     13   679 use Future 0.49;
  13         14320  
  13         374  
26              
27 13     13   482 use parent 'Future';
  13         322  
  13         92  
28              
29 13     13   1804 use File::Temp qw(:seekable);
  13         18602  
  13         13172  
30              
31             sub new {
32 31     31 1 2382 my ( $class, $run ) = @_;
33              
34 31         753 my $self = $class->SUPER::new;
35 31         1332 $self->set_udata( '_run', $run );
36 31         2070 return $self;
37             }
38              
39             sub await {
40 28     28 1 31178 my ($self) = @_;
41              
42 28         274 my $run = $self->udata('_run');
43              
44 28 100       563 return $self if !defined $run;
45              
46 25         131 $self->set_udata( '_run', undef );
47              
48 25         233 my $e;
49 25         60 my $ok = 1;
50             {
51 25         74 local $@; ## no critic (Variables::RequireInitializationForLocalVars)
  25         79  
52 25         88 $ok = eval {
53              
54             # prevent the "(in cleanup) process is already terminated" message
55 25         124 $run->{_proc}->autoterminate(0);
56              
57             # wait for the process to finish
58 25         664 $run->{_proc}->wait;
59              
60 23         437053 1;
61             };
62              
63 25 100       236 if ( !$ok ) {
64              
65             # The Future->fail exception must be true
66 2   100     42 $e = qq{$@} || 'Failed to wait on Git process with Proc::Background';
67             }
68             }
69 25 100       137 return $self->fail( $e, 'Proc::Background' ) if !$ok;
70              
71             # slurp back stdout
72 23         78 my $stdout_fh = $run->{_stdout};
73 23 100       767 $stdout_fh->seek( 0, SEEK_SET ) or return $self->fail( "Cannot seek stdout: $stdout_fh: $!", 'seek' );
74 22         1361 my @stdout = split /\r?\n/m, do { ## no critic (RegularExpressions::RequireDotMatchAnything, RegularExpressions::RequireExtendedFormatting])
75 22         356 local $/; ## no critic (Variables::RequireInitializationForLocalVars)
76 22         2112 scalar <$stdout_fh>;
77             };
78 22 100       1536 return $self->fail( "Cannot read stdout: $stdout_fh: $!", 'readline' ) if $stdout_fh->error;
79              
80             # slurp back stderr
81 21         457 my $stderr_fh = $run->{_stderr};
82 21 100       136 $stderr_fh->seek( 0, SEEK_SET ) or return $self->fail( "Cannot seek stderr: $stderr_fh: $!", 'seek' );
83 20         427 my @stderr = split /\r?\n/m, do { ## no critic (RegularExpressions::RequireDotMatchAnything, RegularExpressions::RequireExtendedFormatting])
84 20         102 local $/; ## no critic (Variables::RequireInitializationForLocalVars)
85 20         1125 scalar <$stderr_fh>;
86             };
87 20 100       648 return $self->fail( "Cannot read stderr: $stderr_fh: $!", 'readline' ) if $stderr_fh->error;
88              
89             # get exit code and signal from git process
90 19         156 my $exit_code = $run->{_proc}->exit_code;
91              
92 19         270 my @result = (
93             \@stdout,
94             \@stderr,
95             $exit_code,
96             );
97              
98             # git died by a signal
99 19 100       155 return $self->fail( 'Git was terminated by a signal', 'Proc::Background', @result ) if $run->{_proc}->exit_signal;
100              
101 18 100 100     520 if (
      100        
      100        
102             # fatal error
103             ( $exit_code == 128 ) ||
104              
105             # usage error
106             ( $exit_code == 129 ) ||
107              
108             # non-zero return code
109             ( $exit_code && $run->{_fatal} )
110             )
111             {
112              
113 8         87 my $stderr = join "\n", @stderr;
114 8 100       96 my $message = length $stderr ? $stderr : "git exited with fatal exit code $exit_code but had no output to stderr";
115              
116             # $run goes out of scope and the file handles and the proc object are freed
117 8         131 return $self->fail( $message, 'git', @result );
118             }
119              
120             # $run goes out of scope and the file handles and the proc object are freed
121 10         121 return $self->done(@result);
122             }
123              
124             sub exit_code {
125 1     1 1 583 my ($self) = @_;
126              
127 1         40 my @result = $self->get;
128 1         22 return $result[2];
129             }
130              
131             sub is_done {
132 19     19 1 26401 my ($self) = @_;
133 19         142 $self->_await_if_git_is_done;
134 19         181 return $self->SUPER::is_done;
135             }
136              
137             sub is_failed {
138 19     19 1 7243 my ($self) = @_;
139 19         69 $self->_await_if_git_is_done;
140 19         166 return $self->SUPER::is_failed;
141             }
142              
143             sub is_ready {
144 19     19 1 8048 my ($self) = @_;
145 19         105 $self->_await_if_git_is_done;
146 19         173 return $self->SUPER::is_ready;
147             }
148              
149             sub state { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
150 0     0 1 0 my ($self) = @_;
151 0         0 $self->_await_if_git_is_done;
152 0         0 return $self->SUPER::state;
153             }
154              
155             sub stderr {
156 1     1 1 979 my ($self) = @_;
157              
158 1         14 my @result = $self->get;
159 1         101 return @{ $result[1] };
  1         11  
160             }
161              
162             sub stdout {
163 5     5 1 1109 my ($self) = @_;
164              
165 5         61 my @result = $self->get;
166 5         1303 return @{ $result[0] };
  5         65  
167             }
168              
169             sub _await_if_git_is_done {
170 57     57   146 my ($self) = @_;
171              
172 57         296 my $run = $self->udata('_run');
173 57 50 33     613 if ( defined $run && !$run->{_proc}->alive ) {
174 0         0 $self->await;
175             }
176              
177 57         112 return;
178             }
179              
180             1;
181              
182             __END__