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   6493 use strict;
  109         183  
  109         2759  
3 109     109   507 use warnings;
  109         174  
  109         2934  
4              
5 109     109   542 use Carp qw/confess croak/;
  109         181  
  109         5920  
6 109     109   557 use Scalar::Util qw/reftype blessed/;
  109         188  
  109         6673  
7              
8 109     109   53842 use Test::Stream::Capabilities qw/CAN_FORK/;
  109         257  
  109         705  
9 109     109   61931 use Test::Stream::Util qw/get_tid USE_THREADS pkg_to_file/;
  109         260  
  109         662  
10              
11 109     109   60684 use Test::Stream::DebugInfo;
  109         286  
  109         3227  
12 109     109   57454 use Test::Stream::Stack;
  109         286  
  109         160437  
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   82 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   89 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 931 sub pid { $PID }
73 10     10 0 45 sub tid { $TID }
74              
75 11     11 0 845 sub hooks { scalar @HOOKS }
76 3     3 0 198 sub post_loads { scalar @POST_LOAD }
77              
78 177 100   177 1 11119 sub init_done { $INIT ? 1 : 0 }
79              
80             sub post_load {
81 125     125 1 354 my $class = shift;
82 125         260 my ($code) = @_;
83 125 100       577 return $code->() if $LOADED;
84 103         585 push @POST_LOAD => $code;
85             }
86              
87             sub loaded {
88 166     166 1 432 my $class = shift;
89              
90 166 100 100     3254 return $LOADED if $LOADED || !@_;
91              
92 106 100       417 if ($_[0]) {
93 103         232 $LOADED = 1;
94 103         568 $_->() for @POST_LOAD;
95             }
96              
97 106         1344 return $LOADED
98             }
99              
100             sub _init {
101 175     175   1804 $INIT = [caller(1)];
102 175         1462 $STACK = Test::Stream::Stack->new;
103              
104 175 100       616 unless ($FORMAT) {
105 169         286 my ($name, $source);
106 169 100       633 if ($ENV{TS_FORMATTER}) {
107 15         25 $name = $ENV{TS_FORMATTER};
108 15         30 $source = "set by the 'TS_FORMATTER' environment variable";
109             }
110             else {
111 154         338 $name = 'TAP';
112 154         315 $source = 'default formatter';
113             }
114              
115 169         343 my $mod = $name;
116 169 100       906 $mod = "Test::Stream::Formatter::$mod"
117             unless $mod =~ s/^\+//;
118              
119 169         869 my $file = pkg_to_file($mod);
120 169 100       392 unless (eval { require $file; 1 }) {
  169         5870  
  164         717  
121 5         10 my $err = $@;
122 5         50 my $line = "* COULD NOT LOAD FORMATTER '$name' ($source) *";
123 5         45 my $border = '*' x length($line);
124 5         35 die "\n\n $border\n $line\n $border\n\n$err";
125             }
126              
127 164         396 $FORMAT = $mod;
128             }
129              
130 170 100       926 return unless $INC{'Test/Stream/IPC.pm'};
131 151         1045 $IPC = Test::Stream::IPC->init;
132             }
133              
134             sub add_hook {
135 125     125 1 386 my $class = shift;
136 125         337 my ($code) = @_;
137 125   100     775 my $rtype = reftype($code) || "";
138 125 100 100     5247 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 18334 return $STACK if $INIT;
145 130         510 _init();
146 130         626 $STACK;
147             }
148              
149             sub ipc {
150 146 100   146 1 754 return $IPC if $INIT;
151 5         20 _init();
152 5         25 $IPC;
153             }
154              
155             sub set_formatter {
156 17     17 1 38 my $self = shift;
157 17 100       943 croak "Global Formatter already set" if $FORMAT;
158 11 100       2123 $FORMAT = pop or croak "No formatter specified";
159             }
160              
161             sub formatter {
162 161 100   161 1 1991 return $FORMAT if $INIT;
163 25         45 _init();
164 20         80 $FORMAT;
165             }
166              
167             sub no_wait {
168 56     56 1 180 my $class = shift;
169 56 100       135 ($NO_WAIT) = @_ if @_;
170 56         194 $NO_WAIT;
171             }
172              
173             sub _ipc_wait {
174 109     109   389 my $fail = 0;
175              
176 109         218 while (CAN_FORK) {
177 121         30512769 my $pid = CORE::wait();
178 121         462 my $err = $?;
179 121 100       744 last if $pid == -1;
180 12 100       87 next unless $err;
181 6         33 $fail++;
182 6         20 $err = $err >> 8;
183 6         402 warn "Process $pid did not exit cleanly (status: $err)\n";
184             }
185              
186 109         245 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       639 return 0 unless $fail;
201 6         69 return 255;
202             }
203              
204             # Set the exit status
205 109     109   600 END { _set_exit() }
206             sub _set_exit {
207 136     136   726 my $exit = $?;
208 136         336 my $new_exit = $exit;
209              
210 136 100 100     1322 if ($PID != $$ or $TID != get_tid()) {
211 11         42 $? = $exit;
212 11         9 return;
213             }
214              
215 125 100       1106 my @hubs = $STACK ? $STACK->all : ();
216              
217 125 100 100     1638 if (@hubs and $IPC and !$NO_WAIT) {
      100        
218 102         345 local $?;
219 102         233 my %seen;
220 102         371 for my $hub (reverse @hubs) {
221 111 100       609 my $ipc = $hub->ipc or next;
222 105 100       1237 next if $seen{$ipc}++;
223 102         748 $ipc->waiting();
224             }
225              
226 102         465 my $ipc_exit = _ipc_wait();
227 102   100     890 $new_exit ||= $ipc_exit;
228             }
229              
230             # None of this is necessary if we never got a root hub
231 125 100       608 if(my $root = shift @hubs) {
232 119         1204 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         870 my $ctx = Test::Stream::Context->new(
237             debug => $dbg,
238             hub => $root,
239             );
240              
241 119 100       520 if (@hubs) {
242 6         24 $ctx->diag("Test ended with extra hubs on the stack!");
243 6         18 $new_exit = 255;
244             }
245              
246 119 100       650 unless ($root->no_ending) {
247 116         892 local $?;
248 116 100       589 $root->finalize($dbg) unless $root->state->ended;
249 116         1532 $_->($ctx, $exit, \$new_exit) for @HOOKS;
250 116   100     1084 $new_exit ||= $root->state->failed;
251             }
252             }
253              
254 125 100       585 $new_exit = 255 if $new_exit > 255;
255              
256 125         77 $? = $new_exit;
257             }
258              
259             1;
260              
261             __END__