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   6377 use strict;
  109         183  
  109         2675  
3 109     109   511 use warnings;
  109         179  
  109         3041  
4              
5 109     109   527 use Carp qw/confess croak/;
  109         245  
  109         5897  
6 109     109   569 use Scalar::Util qw/reftype blessed/;
  109         297  
  109         6816  
7              
8 109     109   54215 use Test::Stream::Capabilities qw/CAN_FORK/;
  109         247  
  109         703  
9 109     109   61767 use Test::Stream::Util qw/get_tid USE_THREADS pkg_to_file/;
  109         257  
  109         663  
10              
11 109     109   59599 use Test::Stream::DebugInfo;
  109         266  
  109         3274  
12 109     109   56943 use Test::Stream::Stack;
  109         283  
  109         159587  
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   71 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   92 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 983 sub pid { $PID }
73 10     10 0 48 sub tid { $TID }
74              
75 11     11 0 626 sub hooks { scalar @HOOKS }
76 3     3 0 186 sub post_loads { scalar @POST_LOAD }
77              
78 177 100   177 1 10744 sub init_done { $INIT ? 1 : 0 }
79              
80             sub post_load {
81 125     125 1 349 my $class = shift;
82 125         264 my ($code) = @_;
83 125 100       578 return $code->() if $LOADED;
84 103         588 push @POST_LOAD => $code;
85             }
86              
87             sub loaded {
88 166     166 1 444 my $class = shift;
89              
90 166 100 100     4534 return $LOADED if $LOADED || !@_;
91              
92 106 100       430 if ($_[0]) {
93 103         213 $LOADED = 1;
94 103         551 $_->() for @POST_LOAD;
95             }
96              
97 106         1371 return $LOADED
98             }
99              
100             sub _init {
101 175     175   1820 $INIT = [caller(1)];
102 175         1417 $STACK = Test::Stream::Stack->new;
103              
104 175 100       607 unless ($FORMAT) {
105 169         308 my ($name, $source);
106 169 100       661 if ($ENV{TS_FORMATTER}) {
107 15         35 $name = $ENV{TS_FORMATTER};
108 15         20 $source = "set by the 'TS_FORMATTER' environment variable";
109             }
110             else {
111 154         344 $name = 'TAP';
112 154         345 $source = 'default formatter';
113             }
114              
115 169         347 my $mod = $name;
116 169 100       886 $mod = "Test::Stream::Formatter::$mod"
117             unless $mod =~ s/^\+//;
118              
119 169         834 my $file = pkg_to_file($mod);
120 169 100       439 unless (eval { require $file; 1 }) {
  169         6131  
  164         684  
121 5         10 my $err = $@;
122 5         50 my $line = "* COULD NOT LOAD FORMATTER '$name' ($source) *";
123 5         40 my $border = '*' x length($line);
124 5         35 die "\n\n $border\n $line\n $border\n\n$err";
125             }
126              
127 164         412 $FORMAT = $mod;
128             }
129              
130 170 100       889 return unless $INC{'Test/Stream/IPC.pm'};
131 151         1089 $IPC = Test::Stream::IPC->init;
132             }
133              
134             sub add_hook {
135 125     125 1 378 my $class = shift;
136 125         299 my ($code) = @_;
137 125   100     907 my $rtype = reftype($code) || "";
138 125 100 100     4111 confess "End hooks must be coderefs"
139             unless $code && $rtype eq 'CODE';
140 110         623 push @HOOKS => $code;
141             }
142              
143             sub stack {
144 2511 100   2511 1 18623 return $STACK if $INIT;
145 130         505 _init();
146 130         634 $STACK;
147             }
148              
149             sub ipc {
150 146 100   146 1 751 return $IPC if $INIT;
151 5         15 _init();
152 5         25 $IPC;
153             }
154              
155             sub set_formatter {
156 17     17 1 42 my $self = shift;
157 17 100       935 croak "Global Formatter already set" if $FORMAT;
158 11 100       2067 $FORMAT = pop or croak "No formatter specified";
159             }
160              
161             sub formatter {
162 161 100   161 1 1857 return $FORMAT if $INIT;
163 25         55 _init();
164 20         85 $FORMAT;
165             }
166              
167             sub no_wait {
168 56     56 1 192 my $class = shift;
169 56 100       135 ($NO_WAIT) = @_ if @_;
170 56         189 $NO_WAIT;
171             }
172              
173             sub _ipc_wait {
174 109     109   297 my $fail = 0;
175              
176 109         234 while (CAN_FORK) {
177 121         30318161 my $pid = CORE::wait();
178 121         639 my $err = $?;
179 121 100       668 last if $pid == -1;
180 12 100       89 next unless $err;
181 6         25 $fail++;
182 6         20 $err = $err >> 8;
183 6         369 warn "Process $pid did not exit cleanly (status: $err)\n";
184             }
185              
186 109         220 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       588 return 0 unless $fail;
201 6         68 return 255;
202             }
203              
204             # Set the exit status
205 109     109   587 END { _set_exit() }
206             sub _set_exit {
207 136     136   697 my $exit = $?;
208 136         347 my $new_exit = $exit;
209              
210 136 100 100     1364 if ($PID != $$ or $TID != get_tid()) {
211 11         27 $? = $exit;
212 11         12 return;
213             }
214              
215 125 100       1054 my @hubs = $STACK ? $STACK->all : ();
216              
217 125 100 100     1596 if (@hubs and $IPC and !$NO_WAIT) {
      100        
218 102         337 local $?;
219 102         291 my %seen;
220 102         318 for my $hub (reverse @hubs) {
221 111 100       781 my $ipc = $hub->ipc or next;
222 105 100       1202 next if $seen{$ipc}++;
223 102         684 $ipc->waiting();
224             }
225              
226 102         451 my $ipc_exit = _ipc_wait();
227 102   100     898 $new_exit ||= $ipc_exit;
228             }
229              
230             # None of this is necessary if we never got a root hub
231 125 100       598 if(my $root = shift @hubs) {
232 119         1286 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         867 my $ctx = Test::Stream::Context->new(
237             debug => $dbg,
238             hub => $root,
239             );
240              
241 119 100       496 if (@hubs) {
242 6         27 $ctx->diag("Test ended with extra hubs on the stack!");
243 6         15 $new_exit = 255;
244             }
245              
246 119 100       658 unless ($root->no_ending) {
247 116         915 local $?;
248 116 100       594 $root->finalize($dbg) unless $root->state->ended;
249 116         1501 $_->($ctx, $exit, \$new_exit) for @HOOKS;
250 116   100     1009 $new_exit ||= $root->state->failed;
251             }
252             }
253              
254 125 100       593 $new_exit = 255 if $new_exit > 255;
255              
256 125         83 $? = $new_exit;
257             }
258              
259             1;
260              
261             __END__