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   1402 use strict;
  99         187  
  99         2453  
3 99     99   485 use warnings;
  99         168  
  99         2561  
4              
5 99     99   483 use base 'Test::Stream::IPC';
  99         176  
  99         10861  
6              
7             use Test::Stream::HashBase(
8 99         836 accessors => [qw/tempdir event_id tid pid globals/],
9 99     99   548 );
  99         204  
10              
11 99     99   554 use Scalar::Util qw/blessed/;
  99         184  
  99         4910  
12 99     99   118155 use File::Temp;
  99         2566498  
  99         8925  
13 99     99   96929 use Storable;
  99         806048  
  99         6412  
14 99     99   722 use File::Spec;
  99         229  
  99         3220  
15              
16 99     99   1183 use Test::Stream::Util qw/try get_tid pkg_to_file/;
  99         231  
  99         975  
17              
18 161     161 1 847 sub is_viable { 1 }
19              
20             sub init {
21 304     304 0 591 my $self = shift;
22              
23 304         1330 my $tmpdir = File::Temp::tempdir(CLEANUP => 0);
24              
25 304 50       809709 $self->abort_trace("Could not get a temp dir") unless $tmpdir;
26              
27 304         2156 $self->{+TEMPDIR} = File::Spec->canonpath($tmpdir);
28              
29             print STDERR "\nIPC Temp Dir: $tmpdir\n\n"
30 304 100       1136 if $ENV{TS_KEEP_TEMPDIR};
31              
32 304         773 $self->{+EVENT_ID} = 1;
33              
34 304         772 $self->{+TID} = get_tid();
35 304         945 $self->{+PID} = $$;
36              
37 304         780 $self->{+GLOBALS} = {};
38              
39 304         974 return $self;
40             }
41              
42             sub hub_file {
43 1015     1015 0 1581 my $self = shift;
44 1015         1735 my ($hid) = @_;
45 1015         1921 my $tdir = $self->{+TEMPDIR};
46 1015         8798 return File::Spec->canonpath("$tdir/HUB-$hid");
47             }
48              
49             sub event_file {
50 141     141 0 336 my $self = shift;
51 141         353 my ($hid, $e) = @_;
52              
53 141         382 my $tempdir = $self->{+TEMPDIR};
54 141 100       1259 my $type = blessed($e) or $self->abort("'$e' is not a blessed object!");
55              
56 140 100       1212 $self->abort("'$e' is not an event object!")
57             unless $type->isa('Test::Stream::Event');
58              
59 139         866 my @type = split '::', $type;
60 139         912 my $name = join('-', $hid, $$, get_tid(), $self->{+EVENT_ID}++, @type);
61              
62 139         1125 return File::Spec->canonpath("$tempdir/$name");
63             }
64              
65             sub add_hub {
66 488     488 1 1644 my $self = shift;
67 488         908 my ($hid) = @_;
68              
69 488         1636 my $hfile = $self->hub_file($hid);
70              
71 488 100       14623 $self->abort_trace("File for hub '$hid' already exists")
72             if -e $hfile;
73              
74 487 50       971801 open(my $fh, '>', $hfile) or $self->abort_trace("Could not create hub file '$hid': $!");
75 487         6071 print $fh "$$\n" . get_tid() . "\n";
76 487         190724 close($fh);
77             }
78              
79             sub drop_hub {
80 384     384 1 681 my $self = shift;
81 384         714 my ($hid) = @_;
82              
83 384         813 my $tdir = $self->{+TEMPDIR};
84 384         1122 my $hfile = $self->hub_file($hid);
85              
86 384 100       9409 $self->abort_trace("File for hub '$hid' does not exist")
87             unless -e $hfile;
88              
89 383 50       14198 open(my $fh, '<', $hfile) or $self->abort_trace("Could not open hub file '$hid': $!");
90 383         8116 my ($pid, $tid) = <$fh>;
91 383         3389 close($fh);
92              
93 383 50       1668 $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       1089 $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       1152 if ($ENV{TS_KEEP_TEMPDIR}) {
100 1 50       57 rename($hfile, File::Spec->canonpath("$hfile.complete")) or $self->abort_trace("Could not rename file '$hfile' -> '$hfile.complete'");
101             }
102             else {
103 382 50       432285 unlink($hfile) or $self->abort_trace("Could not remove file for hub '$hid'");
104             }
105              
106 383 50       10065 opendir(my $dh, $tdir) or $self->abort_trace("Could not open temp dir!");
107 383         7616 for my $file (readdir($dh)) {
108 1123 100       2915 next if $file =~ m{\.complete$};
109 1121 100       9404 next unless $file =~ m{^$hid};
110 1         8 $self->abort_trace("Not all files from hub '$hid' have been collected!");
111             }
112 382         12866 closedir($dh);
113             }
114              
115             sub send {
116 143     143 1 548 my $self = shift;
117 143         479 my ($hid, $e) = @_;
118              
119 143         476 my $tempdir = $self->{+TEMPDIR};
120 143         498 my $global = $hid eq 'GLOBAL';
121 143         672 my $hfile = $self->hub_file($hid);
122              
123 143 100 100     1753 $self->abort("hub '$hid' is not available! Failed to send event!\n")
124             unless $global || -f $hfile;
125              
126 141         706 my $file = $self->event_file($hid, $e);
127 139         765 my $ready = File::Spec->canonpath("$file.ready");
128              
129 139 100       663 if ($global) {
130 129         261 my $name = $ready;
131 129         1088 $name =~ s{^.*(GLOBAL)}{GLOBAL};
132 129         819 $self->globals->{$name}++;
133             }
134              
135             my ($ok, $err) = try {
136 139     139   980 Storable::store($e, $file);
137 138 50       56831 rename($file, $ready) or $self->abort("Could not rename file '$file' -> '$ready'");
138 139         2223 };
139 139 100       1173 if (!$ok) {
140 1         3 my $src_file = __FILE__;
141 1         22 $err =~ s{ at \Q$src_file\E.*$}{};
142 1         4 chomp($err);
143 1         3 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         538 return 1;
165             }
166              
167             sub cull {
168 797     797 1 1230 my $self = shift;
169 797         1367 my ($hid) = @_;
170              
171 797         1683 my $tempdir = $self->{+TEMPDIR};
172              
173 797 50       31859 opendir(my $dh, $tempdir) or $self->abort("could not open IPC temp dir ($tempdir)!");
174              
175 797         1686 my @out;
176 797         17724 my @files = sort readdir($dh);
177 797         2428 for my $file (@files) {
178 2995 100       10499 next if $file =~ m/^\.+$/;
179 1401 100       29916 next unless $file =~ m/^(\Q$hid\E|GLOBAL)-.*\.ready$/;
180 55         249 my $global = $1 eq 'GLOBAL';
181 55 100 100     344 next if $global && $self->globals->{$file}++;
182              
183             # Untaint the path.
184 10         119 my $full = File::Spec->canonpath("$tempdir/$file");
185 10         57 ($full) = ($full =~ m/^(.*)$/gs);
186              
187 10         51 my $obj = $self->read_event_file($full);
188              
189             # Do not remove global events
190 10 100       37 unless ($global) {
191 8         66 my $complete = File::Spec->canonpath("$full.complete");
192 8 100       41 if ($ENV{TS_KEEP_TEMPDIR}) {
193 1 50       55 rename($full, $complete) or $self->abort("Could not rename IPC file '$full', '$complete'");
194             }
195             else {
196 7 50       252168 unlink($full) or $self->abort("Could not unlink IPC file: $file");
197             }
198             }
199              
200 10         62 push @out => $obj;
201             }
202              
203 797         8769 closedir($dh);
204 797         6252 return @out;
205             }
206              
207             sub read_event_file {
208 14     14 0 69 my $self = shift;
209 14         31 my ($file) = @_;
210              
211 14         77 my $obj = Storable::retrieve($file);
212 14 100       1542 $self->abort("Got an unblessed object: '$obj'")
213             unless blessed($obj);
214              
215 13 100       197 unless ($obj->isa('Test::Stream::Event')) {
216 3         16 my $pkg = blessed($obj);
217 3         18 my $mod_file = pkg_to_file($pkg);
218 3     3   30 my ($ok, $err) = try { require $mod_file };
  3         1484  
219              
220 3 100       37 $self->abort("Event has unknown type ($pkg), tried to load '$mod_file' but failed: $err")
221             unless $ok;
222              
223 2 100       30 $self->abort("'$obj' is not a 'Test::Stream::Event' object")
224             unless $obj->isa('Test::Stream::Event');
225             }
226              
227 11         44 return $obj;
228             }
229              
230             sub waiting {
231 102     102 1 274 my $self = shift;
232 102         937 require Test::Stream::Event::Waiting;
233 102         1239 $self->send(
234             GLOBAL => Test::Stream::Event::Waiting->new(
235             debug => Test::Stream::DebugInfo->new(frame => [caller()]),
236             )
237             );
238 102         1309 return;
239             }
240              
241             sub DESTROY {
242 206     206   879 my $self = shift;
243              
244 206 100       977 return unless defined $self->pid;
245 205 100       1655 return unless defined $self->tid;
246              
247 204 100       1419 return unless $$ == $self->pid;
248 202 100       1312 return unless get_tid() == $self->tid;
249              
250 201         1044 my $tempdir = $self->{+TEMPDIR};
251              
252 201 50       5307 opendir(my $dh, $tempdir) or $self->abort("Could not open temp dir! ($tempdir)");
253 201         2036 while(my $file = readdir($dh)) {
254 441 100       2963 next if $file =~ m/^\.+$/;
255 41 100       134 next if $file =~ m/\.complete$/;
256 39         265 my $full = File::Spec->canonpath("$tempdir/$file");
257              
258 39 100       211 if ($file =~ m/^(GLOBAL|HUB-)/) {
259 38         120 $full =~ m/^(.*)$/;
260 38         108 $full = $1; # Untaint it
261 38 100       134 next if $ENV{TS_KEEP_TEMPDIR};
262 37 50       2590 unlink($full) or $self->abort("Could not unlink IPC file: $full");
263 37         176 next;
264             }
265              
266 1         6 $self->abort("Leftover files in the directory ($full)!\n");
267             }
268 200         1528 closedir($dh);
269              
270 200 100       648 if ($ENV{TS_KEEP_TEMPDIR}) {
271 1         4 print STDERR "# Not removing temp dir: $tempdir\n";
272 1         11 return;
273             }
274              
275 199 50       22090 rmdir($tempdir) or warn "Could not remove IPC temp dir ($tempdir)";
276             }
277              
278             1;
279              
280             __END__