File Coverage

blib/lib/Test/Stream/Sync.pm
Criterion Covered Total %
statement 124 124 100.0
branch 58 58 100.0
condition 23 23 100.0
subroutine 27 27 100.0
pod 9 13 69.2
total 241 245 98.3


line stmt bran cond sub pod time code
1             package Test::Stream::Sync;
2 109     109   4255 use strict;
  109         110  
  109         2498  
3 109     109   327 use warnings;
  109         101  
  109         2292  
4              
5 109     109   332 use Carp qw/confess croak/;
  109         114  
  109         4728  
6 109     109   354 use Scalar::Util qw/reftype blessed/;
  109         124  
  109         4932  
7              
8 109     109   33407 use Test::Stream::Capabilities qw/CAN_FORK/;
  109         162  
  109         511  
9 109     109   38472 use Test::Stream::Util qw/get_tid USE_THREADS pkg_to_file/;
  109         148  
  109         500  
10              
11 109     109   38033 use Test::Stream::DebugInfo();
  109         171  
  109         2324  
12 109     109   36653 use Test::Stream::Stack();
  109         185  
  109         101866  
13              
14             # This package is NOT an object. It is global in nature and I don't want people
15             # fscking with it. It is small, with only the following variables. These are
16             # lexicals on purpose to prevent anyone from touching them directly.
17             # I know this may seem awful, but thats why this package is so small, this is
18             # the only place I need to lock down. This is to prevent people from doing some
19             # of the awful things they did with Test::Builder.
20             my $PID = $$;
21             my $TID = get_tid();
22             my $NO_WAIT = 0;
23             my $INIT = undef;
24             my $IPC = undef;
25             my $STACK = undef;
26             my $FORMAT = undef;
27             my @HOOKS = ();
28             my $LOADED = 0;
29             my @POST_LOAD = ();
30              
31             # The only valid reason to touch these internals is to test them. As such the
32             # internals can be exposed if the package is loaded from itself, and even then
33             # it warns in-case someone tries to do it for the wrong reasons.
34             # This must ONLY be used in the unit tests for this package.
35             {
36             my $caller = caller || '';
37             if ($caller eq __PACKAGE__) {
38             warn "Enabling Test::Stream::Sync debug features, this is normally not desired!";
39              
40             *GUTS = sub {
41             return {
42 8     8   53 PID => \$PID,
43             TID => \$TID,
44             NO_WAIT => \$NO_WAIT,
45             INIT => \$INIT,
46             IPC => \$IPC,
47             STACK => \$STACK,
48             FORMAT => \$FORMAT,
49             HOOKS => \@HOOKS,
50             LOADED => \$LOADED,
51             POST_LOAD => \@POST_LOAD,
52             };
53             };
54              
55             *GUTS_SNAPSHOT = sub {
56             return {
57 8     8   73 PID => $PID,
58             TID => $TID,
59             NO_WAIT => $NO_WAIT,
60             INIT => $INIT,
61             IPC => $IPC,
62             STACK => $STACK,
63             FORMAT => $FORMAT,
64             HOOKS => [@HOOKS],
65             LOADED => $LOADED,
66             POST_LOAD => [@POST_LOAD],
67             };
68             };
69             }
70             }
71              
72 14     14 0 106 sub pid { $PID }
73 10     10 0 35 sub tid { $TID }
74              
75 11     11 0 434 sub hooks { scalar @HOOKS }
76 3     3 0 9 sub post_loads { scalar @POST_LOAD }
77              
78 178 100   178 1 7045 sub init_done { $INIT ? 1 : 0 }
79              
80             sub post_load {
81 126     126 1 212 my $class = shift;
82 126         177 my ($code) = @_;
83 126 100       414 return $code->() if $LOADED;
84 103         414 push @POST_LOAD => $code;
85             }
86              
87             sub loaded {
88 167     167 1 455 my $class = shift;
89              
90 167 100 100     2364 return $LOADED if $LOADED || !@_;
91              
92 106 100       545 if ($_[0]) {
93 103         153 $LOADED = 1;
94 103         478 $_->() for @POST_LOAD;
95             }
96              
97 106         954 return $LOADED
98             }
99              
100             sub _init {
101 175     175   1272 $INIT = [caller(1)];
102 175         1043 $STACK = Test::Stream::Stack->new;
103              
104 175 100       442 unless ($FORMAT) {
105 169         177 my ($name, $source);
106 169 100       439 if ($ENV{TS_FORMATTER}) {
107 15         20 $name = $ENV{TS_FORMATTER};
108 15         10 $source = "set by the 'TS_FORMATTER' environment variable";
109             }
110             else {
111 154         211 $name = 'TAP';
112 154         210 $source = 'default formatter';
113             }
114              
115 169         187 my $mod = $name;
116 169 100       677 $mod = "Test::Stream::Formatter::$mod"
117             unless $mod =~ s/^\+//;
118              
119 169         592 my $file = pkg_to_file($mod);
120 169 100       243 unless (eval { require $file; 1 }) {
  169         3350  
  164         460  
121 5         10 my $err = $@;
122 5         15 my $line = "* COULD NOT LOAD FORMATTER '$name' ($source) *";
123 5         10 my $border = '*' x length($line);
124 5         25 die "\n\n $border\n $line\n $border\n\n$err";
125             }
126              
127 164         272 $FORMAT = $mod;
128             }
129              
130 170 100       436 return unless $INC{'Test/Stream/IPC.pm'};
131 151         710 $IPC = Test::Stream::IPC->init;
132             }
133              
134             sub add_hook {
135 125     125 1 265 my $class = shift;
136 125         207 my ($code) = @_;
137 125   100     575 my $rtype = reftype($code) || "";
138 125 100 100     3126 confess "End hooks must be coderefs"
139             unless $code && $rtype eq 'CODE';
140 110         415 push @HOOKS => $code;
141             }
142              
143             sub stack {
144 2518 100   2518 1 12398 return $STACK if $INIT;
145 130         321 _init();
146 130         418 $STACK;
147             }
148              
149             sub ipc {
150 146 100   146 1 831 return $IPC if $INIT;
151 5         10 _init();
152 5         15 $IPC;
153             }
154              
155             sub set_formatter {
156 17     17 1 26 my $self = shift;
157 17 100       746 croak "Global Formatter already set" if $FORMAT;
158 11 100       1340 $FORMAT = pop or croak "No formatter specified";
159             }
160              
161             sub formatter {
162 161 100   161 1 1305 return $FORMAT if $INIT;
163 25         40 _init();
164 20         55 $FORMAT;
165             }
166              
167             sub no_wait {
168 56     56 1 150 my $class = shift;
169 56 100       90 ($NO_WAIT) = @_ if @_;
170 56         103 $NO_WAIT;
171             }
172              
173             sub _ipc_wait {
174 109     109   187 my $fail = 0;
175              
176 109         161 while (CAN_FORK) {
177 121         22795840 my $pid = CORE::wait();
178 121         568 my $err = $?;
179 121 100       468 last if $pid == -1;
180 12 100       104 next unless $err;
181 6         31 $fail++;
182 6         23 $err = $err >> 8;
183 6         259 warn "Process $pid did not exit cleanly (status: $err)\n";
184             }
185              
186 109         130 if (USE_THREADS) {
187             for my $t (threads->list()) {
188             $t->join;
189             # In older threads we cannot check if a thread had an error unless
190             # we control it and its return.
191             my $err = $t->can('error') ? $t->error : undef;
192             next unless $err;
193             my $tid = $t->tid();
194             $fail++;
195             chomp($err);
196             warn "Thread $tid did not end cleanly: $err\n";
197             }
198             }
199              
200 109 100       420 return 0 unless $fail;
201 6         51 return 255;
202             }
203              
204             # Set the exit status
205 109     109   403 END { _set_exit() }
206             sub _set_exit {
207 136     136   540 my $exit = $?;
208 136         195 my $new_exit = $exit;
209              
210 136 100 100     945 if ($PID != $$ or $TID != get_tid()) {
211 11         26 $? = $exit;
212 11         6 return;
213             }
214              
215 125 100       739 my @hubs = $STACK ? $STACK->all : ();
216              
217 125 100 100     1103 if (@hubs and $IPC and !$NO_WAIT) {
      100        
218 102         223 local $?;
219 102         128 my %seen;
220 102         238 for my $hub (reverse @hubs) {
221 111 100       405 my $ipc = $hub->ipc or next;
222 105 100       825 next if $seen{$ipc}++;
223 102         439 $ipc->waiting();
224             }
225              
226 102         324 my $ipc_exit = _ipc_wait();
227 102   100     682 $new_exit ||= $ipc_exit;
228             }
229              
230             # None of this is necessary if we never got a root hub
231 125 100       430 if(my $root = shift @hubs) {
232 119         864 my $dbg = Test::Stream::DebugInfo->new(
233             frame => [__PACKAGE__, __FILE__, 0, 'Test::Stream::Context::END'],
234             detail => 'Test::Stream::Context END Block finalization',
235             );
236 119         636 my $ctx = Test::Stream::Context->new(
237             debug => $dbg,
238             hub => $root,
239             );
240              
241 119 100       344 if (@hubs) {
242 6         15 $ctx->diag("Test ended with extra hubs on the stack!");
243 6         9 $new_exit = 255;
244             }
245              
246 119 100       451 unless ($root->no_ending) {
247 116         626 local $?;
248 116 100       390 $root->finalize($dbg) unless $root->state->ended;
249 116         984 $_->($ctx, $exit, \$new_exit) for @HOOKS;
250 116   100     757 $new_exit ||= $root->state->failed;
251             }
252             }
253              
254 125 100       400 $new_exit = 255 if $new_exit > 255;
255              
256 125         58 $? = $new_exit;
257             }
258              
259             1;
260              
261             __END__