File Coverage

blib/lib/Test/Stream/IPC/Files.pm
Criterion Covered Total %
statement 161 161 100.0
branch 73 88 82.9
condition 6 6 100.0
subroutine 22 22 100.0
pod 6 10 60.0
total 268 287 93.3


line stmt bran cond sub pod time code
1             package Test::Stream::IPC::Files;
2 99     99   1449 use strict;
  99         184  
  99         2465  
3 99     99   470 use warnings;
  99         174  
  99         2562  
4              
5 99     99   480 use base 'Test::Stream::IPC';
  99         171  
  99         10834  
6              
7             use Test::Stream::HashBase(
8 99         812 accessors => [qw/tempdir event_id tid pid globals/],
9 99     99   558 );
  99         201  
10              
11 99     99   556 use Scalar::Util qw/blessed/;
  99         181  
  99         4785  
12 99     99   117995 use File::Temp;
  99         2434480  
  99         8908  
13 99     99   95142 use Storable;
  99         345717  
  99         6526  
14 99     99   728 use File::Spec;
  99         220  
  99         3082  
15              
16 99     99   1221 use Test::Stream::Util qw/try get_tid pkg_to_file/;
  99         282  
  99         1070  
17              
18 161     161 1 757 sub is_viable { 1 }
19              
20             sub init {
21 304     304 0 581 my $self = shift;
22              
23 304         1539 my $tmpdir = File::Temp::tempdir(CLEANUP => 0);
24              
25 304 50       435402 $self->abort_trace("Could not get a temp dir") unless $tmpdir;
26              
27 304         2230 $self->{+TEMPDIR} = File::Spec->canonpath($tmpdir);
28              
29             print STDERR "\nIPC Temp Dir: $tmpdir\n\n"
30 304 100       1217 if $ENV{TS_KEEP_TEMPDIR};
31              
32 304         758 $self->{+EVENT_ID} = 1;
33              
34 304         727 $self->{+TID} = get_tid();
35 304         1022 $self->{+PID} = $$;
36              
37 304         763 $self->{+GLOBALS} = {};
38              
39 304         1032 return $self;
40             }
41              
42             sub hub_file {
43 1015     1015 0 1530 my $self = shift;
44 1015         1604 my ($hid) = @_;
45 1015         1942 my $tdir = $self->{+TEMPDIR};
46 1015         8426 return File::Spec->canonpath("$tdir/HUB-$hid");
47             }
48              
49             sub event_file {
50 141     141 0 334 my $self = shift;
51 141         341 my ($hid, $e) = @_;
52              
53 141         387 my $tempdir = $self->{+TEMPDIR};
54 141 100       1234 my $type = blessed($e) or $self->abort("'$e' is not a blessed object!");
55              
56 140 100       1206 $self->abort("'$e' is not an event object!")
57             unless $type->isa('Test::Stream::Event');
58              
59 139         830 my @type = split '::', $type;
60 139         884 my $name = join('-', $hid, $$, get_tid(), $self->{+EVENT_ID}++, @type);
61              
62 139         1014 return File::Spec->canonpath("$tempdir/$name");
63             }
64              
65             sub add_hub {
66 488     488 1 1578 my $self = shift;
67 488         918 my ($hid) = @_;
68              
69 488         1577 my $hfile = $self->hub_file($hid);
70              
71 488 100       14265 $self->abort_trace("File for hub '$hid' already exists")
72             if -e $hfile;
73              
74 487 50       338393 open(my $fh, '>', $hfile) or $self->abort_trace("Could not create hub file '$hid': $!");
75 487         6065 print $fh "$$\n" . get_tid() . "\n";
76 487         37674 close($fh);
77             }
78              
79             sub drop_hub {
80 384     384 1 630 my $self = shift;
81 384         714 my ($hid) = @_;
82              
83 384         856 my $tdir = $self->{+TEMPDIR};
84 384         1077 my $hfile = $self->hub_file($hid);
85              
86 384 100       8378 $self->abort_trace("File for hub '$hid' does not exist")
87             unless -e $hfile;
88              
89 383 50       13371 open(my $fh, '<', $hfile) or $self->abort_trace("Could not open hub file '$hid': $!");
90 383         7647 my ($pid, $tid) = <$fh>;
91 383         3152 close($fh);
92              
93 383 50       1663 $self->abort_trace("A hub file can only be closed by the process that started it\nExpected $pid, got $$")
94             unless $pid == $$;
95              
96 383 50       1185 $self->abort_trace("A hub file can only be closed by the thread that started it\nExpected $tid, got " . get_tid())
97             unless get_tid() == $tid;
98              
99 383 100       1114 if ($ENV{TS_KEEP_TEMPDIR}) {
100 1 50       56 rename($hfile, File::Spec->canonpath("$hfile.complete")) or $self->abort_trace("Could not rename file '$hfile' -> '$hfile.complete'");
101             }
102             else {
103 382 50       70696 unlink($hfile) or $self->abort_trace("Could not remove file for hub '$hid'");
104             }
105              
106 383 50       9445 opendir(my $dh, $tdir) or $self->abort_trace("Could not open temp dir!");
107 383         7140 for my $file (readdir($dh)) {
108 1123 100       2715 next if $file =~ m{\.complete$};
109 1121 100       8617 next unless $file =~ m{^$hid};
110 1         7 $self->abort_trace("Not all files from hub '$hid' have been collected!");
111             }
112 382         11157 closedir($dh);
113             }
114              
115             sub send {
116 143     143 1 556 my $self = shift;
117 143         431 my ($hid, $e) = @_;
118              
119 143         531 my $tempdir = $self->{+TEMPDIR};
120 143         472 my $global = $hid eq 'GLOBAL';
121 143         713 my $hfile = $self->hub_file($hid);
122              
123 143 100 100     1450 $self->abort("hub '$hid' is not available! Failed to send event!\n")
124             unless $global || -f $hfile;
125              
126 141         692 my $file = $self->event_file($hid, $e);
127 139         718 my $ready = File::Spec->canonpath("$file.ready");
128              
129 139 100       637 if ($global) {
130 129         281 my $name = $ready;
131 129         1047 $name =~ s{^.*(GLOBAL)}{GLOBAL};
132 129         835 $self->globals->{$name}++;
133             }
134              
135             my ($ok, $err) = try {
136 139     139   954 Storable::store($e, $file);
137 138 50       96140 rename($file, $ready) or $self->abort("Could not rename file '$file' -> '$ready'");
138 139         2091 };
139 139 100       1159 if (!$ok) {
140 1         2 my $src_file = __FILE__;
141 1         22 $err =~ s{ at \Q$src_file\E.*$}{};
142 1         4 chomp($err);
143 1         2 my $tid = get_tid();
144 1         9 my $trace = $e->debug->trace;
145 1         5 my $type = blessed($e);
146              
147 1         9 $self->abort(<<" EOT");
148              
149             *******************************************************************************
150             There was an error writing an event:
151             Destination: $hid
152             Origin PID: $$
153             Origin TID: $tid
154             Event Type: $type
155             Event Trace: $trace
156             File Name: $file
157             Ready Name: $ready
158             Error: $err
159             *******************************************************************************
160              
161             EOT
162             }
163              
164 138         505 return 1;
165             }
166              
167             sub cull {
168 797     797 1 1295 my $self = shift;
169 797         1320 my ($hid) = @_;
170              
171 797         1503 my $tempdir = $self->{+TEMPDIR};
172              
173 797 50       30355 opendir(my $dh, $tempdir) or $self->abort("could not open IPC temp dir ($tempdir)!");
174              
175 797         1636 my @out;
176 797         16690 my @files = sort readdir($dh);
177 797         2290 for my $file (@files) {
178 2995 100       10428 next if $file =~ m/^\.+$/;
179 1401 100       29225 next unless $file =~ m/^(\Q$hid\E|GLOBAL)-.*\.ready$/;
180 55         315 my $global = $1 eq 'GLOBAL';
181 55 100 100     353 next if $global && $self->globals->{$file}++;
182              
183             # Untaint the path.
184 10         114 my $full = File::Spec->canonpath("$tempdir/$file");
185 10         61 ($full) = ($full =~ m/^(.*)$/gs);
186              
187 10         53 my $obj = $self->read_event_file($full);
188              
189             # Do not remove global events
190 10 100       40 unless ($global) {
191 8         65 my $complete = File::Spec->canonpath("$full.complete");
192 8 100       42 if ($ENV{TS_KEEP_TEMPDIR}) {
193 1 50       54 rename($full, $complete) or $self->abort("Could not rename IPC file '$full', '$complete'");
194             }
195             else {
196 7 50       762 unlink($full) or $self->abort("Could not unlink IPC file: $file");
197             }
198             }
199              
200 10         38 push @out => $obj;
201             }
202              
203 797         10605 closedir($dh);
204 797         5909 return @out;
205             }
206              
207             sub read_event_file {
208 14     14 0 57 my $self = shift;
209 14         34 my ($file) = @_;
210              
211 14         75 my $obj = Storable::retrieve($file);
212 14 100       1284 $self->abort("Got an unblessed object: '$obj'")
213             unless blessed($obj);
214              
215 13 100       123 unless ($obj->isa('Test::Stream::Event')) {
216 3         10 my $pkg = blessed($obj);
217 3         13 my $mod_file = pkg_to_file($pkg);
218 3     3   19 my ($ok, $err) = try { require $mod_file };
  3         905  
219              
220 3 100       23 $self->abort("Event has unknown type ($pkg), tried to load '$mod_file' but failed: $err")
221             unless $ok;
222              
223 2 100       17 $self->abort("'$obj' is not a 'Test::Stream::Event' object")
224             unless $obj->isa('Test::Stream::Event');
225             }
226              
227 11         32 return $obj;
228             }
229              
230             sub waiting {
231 102     102 1 239 my $self = shift;
232 102         932 require Test::Stream::Event::Waiting;
233 102         1258 $self->send(
234             GLOBAL => Test::Stream::Event::Waiting->new(
235             debug => Test::Stream::DebugInfo->new(frame => [caller()]),
236             )
237             );
238 102         1330 return;
239             }
240              
241             sub DESTROY {
242 206     206   881 my $self = shift;
243              
244 206 100       956 return unless defined $self->pid;
245 205 100       1655 return unless defined $self->tid;
246              
247 204 100       1430 return unless $$ == $self->pid;
248 202 100       1307 return unless get_tid() == $self->tid;
249              
250 201         1062 my $tempdir = $self->{+TEMPDIR};
251              
252 201 50       5560 opendir(my $dh, $tempdir) or $self->abort("Could not open temp dir! ($tempdir)");
253 201         2035 while(my $file = readdir($dh)) {
254 441 100       2744 next if $file =~ m/^\.+$/;
255 41 100       132 next if $file =~ m/\.complete$/;
256 39         264 my $full = File::Spec->canonpath("$tempdir/$file");
257              
258 39 100       210 if ($file =~ m/^(GLOBAL|HUB-)/) {
259 38         123 $full =~ m/^(.*)$/;
260 38         112 $full = $1; # Untaint it
261 38 100       127 next if $ENV{TS_KEEP_TEMPDIR};
262 37 50       2624 unlink($full) or $self->abort("Could not unlink IPC file: $full");
263 37         174 next;
264             }
265              
266 1         6 $self->abort("Leftover files in the directory ($full)!\n");
267             }
268 200         1444 closedir($dh);
269              
270 200 100       593 if ($ENV{TS_KEEP_TEMPDIR}) {
271 1         5 print STDERR "# Not removing temp dir: $tempdir\n";
272 1         11 return;
273             }
274              
275 199 50       22117 rmdir($tempdir) or warn "Could not remove IPC temp dir ($tempdir)";
276             }
277              
278             1;
279              
280             __END__