File Coverage

blib/lib/Test/SerialFork.pm
Criterion Covered Total %
statement 50 54 92.5
branch 8 16 50.0
condition 2 5 40.0
subroutine 8 8 100.0
pod 1 1 100.0
total 69 84 82.1


line stmt bran cond sub pod time code
1             # $Id: SerialFork.pm,v 1.1 2005/07/27 15:08:13 pmh Exp $
2              
3             =head1 NAME
4              
5             Test::SerialFork - Run iterations of a test in different processes
6              
7             =head1 SYNOPSIS
8              
9             use Test::More plan => 42;
10             use Test::SerialFork;
11              
12             my $abc=serial_fork('abc','def','ghi');
13              
14             ok(do_my_test($abc),'It works');
15              
16             =head1 DESCRIPTION
17              
18             This module allows you to run multiple copies of the same test, specifying a
19             different label for each copy.
20             The label is made available to the test run, and will
21             generally be used to modify its behaviour in some way.
22             Each copy of the test will also be run in a
23             seperate sub-process, to isolate each test run from system-wide changes made
24             by previous runs.
25              
26             The module exports a single function, serial_fork(), whose arguments are
27             the labels which will be passed to the subsequent runs of the test.
28             serial_fork() iterates over these labels, forking into two processes each
29             time.
30              
31             In the child process, serial_fork() essentially just returns the current
32             label, and the remainder of the test program is run as normal. However, the
33             parent process collects all of the child's output, and integrates it into the
34             main status report generated by the test (test names have the current label
35             prepended to make it clear which run produced each message).
36              
37             serial_fork() generates a few test cases of its own:
38              
39             =over
40              
41             =item serial_fork() parameters good
42              
43             This test of serial_fork()'s parameters ensures that each one is defined
44             and contains at least one non-whitespace character. This is to make sure that
45             when these labels are useful when prepended to the test. If this test fails,
46             the remainder of the test is aborted.
47              
48             =item $label: Create pipe
49              
50             =item $label: Fork
51              
52             These tests are performed at the beginning of each test run, to check that the
53             mechanics of setting up the subprocess are working. If either of them fails,
54             the test run and any subsequent test runs are aborted.
55              
56             =item $label: Clean exit
57              
58             This test is performed after each test run, to check that the subprocess
59             exited with a successful status.
60              
61             =back
62              
63             =head1 INTEGRATION WITH TEST::*
64              
65             This module only works properly with other modules derived from
66             L. However, the way it currently works loses some of the
67             distinction between the three output channels that Test::Builder provides. If
68             you stick to the ok(), is() and diag() variants, you should be OK.
69              
70             =head1 BUGS AND CAVEATS
71              
72             Due to their use as test name labels, the values passed to serial_fork()
73             should really only be plain strings (though have a look at L
74             to see what resulted when Damian Conway said something similar). A future
75             version of this module will probably allow some way of specifying label/data
76             pairs, but in the meantime, you can easily work around it like this:
77              
78             my %label_data=(
79             abc => { complex data ... },
80             def => { more complex data ... },
81             );
82             my $label=serial_fork(keys %label_data);
83             my $data=$label_data{$label};
84              
85             =head1 SEE ALSO
86              
87             L forks several copies of a test and runs them all
88             concurrently, through the magic of source filters. It also provides a way for
89             the subprocesses to communicate with each other. In my view, the diagnostics
90             aren't as clear as C's, but YMMV.
91              
92             =head1 AUTHOR
93              
94             Copyright 2005 by Peter Haworth Epmh@cpan.orgE
95              
96             =cut
97              
98              
99             package Test::SerialFork;
100             our $VERSION=0.01;
101              
102 4     4   83031 use 5.006;
  4         12  
  4         132  
103 4     4   21 use Exporter;
  4         7  
  4         145  
104 4     4   3885 use IO::Handle;
  4         47411  
  4         190  
105 4     4   27 use Test::Builder;
  4         8  
  4         85  
106 4     4   19 use strict;
  4         4  
  4         117  
107 4     4   19 use warnings;
  4         4  
  4         107  
108              
109             {
110 4     4   19 no warnings 'once';
  4         5  
  4         2369  
111             *import=\&Exporter::import;
112             }
113             our @EXPORT=qw(serial_fork);
114              
115             sub serial_fork(@){
116 3     3 1 2058 my $Test=Test::Builder->new;
117              
118 3 50 33     60 $Test->ok(@_==grep(defined() && /\S/,@_), 'serial_fork() parameters good')
119             or $Test->BAILOUT;
120              
121 3         1107 for my $label(@_){
122 5 50       1885 $Test->ok(pipe(my $stdout_in,my $stdout_out),"$label: Create pipe")
123             or $Test->BAILOUT;
124 5         1960 $stdout_out->autoflush(1);
125              
126 5 100       6452 if(my $pid=fork){
    50          
127             # Parent
128 3         158 close $stdout_out;
129 3         261 $Test->ok(1,"$label: Fork");
130              
131 3         8529 while(my $line=<$stdout_in>){
132 3         64 $line=~s/\s+\z//;
133 3 50       44 if($line=~/\A((not\s+)?ok)(?:\s+-)(?:\s+(.*))\z/){
    0          
134 3         73 my($status,$not,$text)=($1,$2,$3);
135 3   50     37 $text||='';
136              
137             # We don't just call ok(!$not), because that generates diagnostics of
138             # its own for failures. We only want the diagnostics from the child.
139 3         55 my $num=$Test->current_test;
140 3         41 $Test->current_test(++$num);
141 3         106 $Test->_print("$status $num - $label: $text\n");
142             }elsif($line=~s/\A#\s?//){
143 0         0 $Test->diag($line);
144             }else{
145 0         0 $Test->_print_diag("$label: $line (unrecognised)\n");
146             }
147             }
148 3         552154 my $wpid=waitpid($pid,0);
149 3         78 $Test->is_num($?,0,"$label: Clean exit");
150             }elsif(defined $pid){
151             # Child
152 2         437 $Test->use_numbers(0);
153 2         167 $Test->no_ending(1);
154              
155 2         101 close $stdout_in;
156 2         21 my $fileno=fileno $stdout_out;
157 2 50       476 open STDERR,">&=$fileno"
158             or die "$label: Can't redirect STDERR";
159 2 50       95 open STDOUT,">&=$fileno"
160             or die "$label: Can't redirect STDOUT";
161              
162 2         53 $Test->output($stdout_out);
163 2         274 $Test->failure_output($stdout_out);
164 2         56 $Test->todo_output($stdout_out);
165              
166 2         249 return $label;
167             }else{
168 0         0 $Test->ok(0,"$label: Fork");
169 0         0 $Test->BAILOUT;
170             }
171             }
172 1         1160 exit 0;
173             }
174              
175             # Return true to require
176             1;
177