File Coverage

blib/lib/TAP/Harness/Async.pm
Criterion Covered Total %
statement 15 86 17.4
branch 0 22 0.0
condition 0 6 0.0
subroutine 5 15 33.3
pod 2 3 66.6
total 22 132 16.6


line stmt bran cond sub pod time code
1             package TAP::Harness::Async;
2             # ABSTRACT: Asynchronous subclass for TAP::Harness
3 1     1   751 use strict;
  1         2  
  1         31  
4 1     1   5 use warnings;
  1         1  
  1         32  
5 1     1   663 use parent qw(TAP::Harness);
  1         253  
  1         8  
6              
7             our $VERSION = '0.001';
8              
9             =head1 NAME
10              
11             TAP::Harness::Async - Run tests in a subprocess through L
12              
13             =head1 VERSION
14              
15             version 0.001
16              
17             =head1 SYNOPSIS
18              
19             use TAP::Harness::Async;
20             use IO::Async::Loop;
21             my $loop = IO::Async::Loop->new;
22             my $harness = TAP::Harness::Async->new({
23             loop => $loop,
24             });
25             $harness->runtests(@ARGV);
26             $harness->on_complete(sub { $loop->later(sub { $loop->loop_stop }) });
27             $loop->loop_forever;
28              
29             =head1 DESCRIPTION
30              
31             This is a simple test harness which does the bare minimum required to
32             run the test process under L.
33              
34             WARNING: This is an early proof-of-concept version, see examples/tickit.pl
35             for a simple demonstration and please note that the API is not stable
36             and may change significantly in the next version.
37              
38             =cut
39              
40 1     1   29391 use Carp;
  1         4  
  1         86  
41 1     1   6 use TAP::Base;
  1         2  
  1         1176  
42              
43             ##############################################################################
44              
45             =head1 METHODS
46              
47             =cut
48              
49             sub _initialize {
50 0     0     my ($self, $args) = @_;
51 0 0         my $loop = delete $args->{loop} or die 'loop?';
52 0           $self->{loop} = $loop;
53 0           $self->SUPER::_initialize($args);
54             }
55              
56             sub _aggregate_parallel {
57 0     0     my ( $self, $aggregate, $scheduler ) = @_;
58              
59 0           my $jobs = $self->jobs;
60 0           my $mux = $self->_construct( $self->multiplexer_class );
61              
62             RESULT: {
63              
64             # Keep multiplexer topped up
65 0           FILL:
66 0           while ( $mux->parsers < $jobs ) {
67 0           my $job = $scheduler->get_job;
68              
69             # If we hit a spinner stop filling and start running.
70 0 0 0       last FILL if !defined $job || $job->is_spinner;
71              
72 0           my ( $parser, $session ) = $self->make_parser($job);
73 0           $mux->add( $parser, [ $session, $job ] );
74             }
75              
76 0 0         if(my ($parser, $stash, $result) = $mux->next) {
77 0           my ($session, $job) = @$stash;
78 0 0         if(defined $result) {
79 0           $session->result($result);
80 0 0         $self->_bailout($result) if $result->is_bailout;
81             } else {
82              
83             # End of parser. Automatically removed from the mux.
84 0           $self->finish_parser( $parser, $session );
85 0           $self->_after_test( $aggregate, $job, $parser );
86 0           $job->finish;
87             }
88 0           redo RESULT;
89             }
90             }
91              
92 0           return;
93             }
94              
95 0     0 0   sub loop { shift->{loop} }
96              
97             sub _aggregate_single {
98 0     0     my ( $self, $aggregate, $scheduler ) = @_;
99              
100 0           my $code;
101             $code = sub {
102 0 0   0     if(my $job = $scheduler->get_job ) {
103 0 0         return $code->() if $job->is_spinner;
104              
105 0           my ( $parser, $session ) = $self->make_parser($job);
106 0           my $it = $parser->_iterator;
107             $it->{on_line} = sub {
108 0           my ($line) = @_;
109 0   0       while ($it->lines && defined( my $result = $parser->next ) ) {
110 0           $session->result($result);
111 0 0         if ( $result->is_bailout ) {
112 0           $self->_bailout($result);
113             }
114             }
115 0           };
116             $it->{on_finished} = sub {
117 0           $self->finish_parser( $parser, $session );
118 0           $self->_after_test( $aggregate, $job, $parser );
119 0           $job->finish;
120 0           $self->loop->later($code);
121             },
122 0           } else {
123 0 0         $self->{on_tests_complete}->($self, $aggregate) if exists $self->{on_tests_complete};
124             }
125 0           };
126 0           $code->();
127 0           return;
128             }
129              
130             sub runtests {
131 0     0 1   my ( $self, @tests ) = @_;
132              
133 0           my $aggregate = $self->_construct( $self->aggregator_class );
134              
135 0           $self->_make_callback( 'before_runtests', $aggregate );
136 0           $aggregate->start;
137             my $finish = sub {
138 0     0     my $interrupted = shift;
139 0           $aggregate->stop;
140 0           $self->summary( $aggregate, $interrupted );
141 0           $self->_make_callback( 'after_runtests', $aggregate );
142 0           };
143             my $run = sub {
144 0     0     $self->{on_tests_complete} = sub { $finish->(0) };
  0            
145 0           $self->aggregate_tests( $aggregate, @tests );
146 0           };
147              
148 0 0         if ( $self->trap ) {
149             local $SIG{INT} = sub {
150 0     0     print "\n";
151 0           $finish->(1);
152 0           exit;
153 0           };
154 0           $run->();
155             }
156             else {
157 0           $run->();
158             }
159              
160 0           return $aggregate;
161             }
162              
163             =head2 on_complete
164              
165             Accessor for code to run on test completion.
166              
167             =cut
168              
169             sub on_complete {
170 0     0 1   my ($self, $code) = @_;
171 0 0         if($code) {
172 0           $self->{on_complete} = $code;
173 0           return $self;
174             }
175 0           return $self->{on_complete};
176             }
177              
178             1;
179              
180             __END__