File Coverage

blib/lib/Fennec.pm
Criterion Covered Total %
statement 166 178 93.2
branch 37 52 71.1
condition 15 39 38.4
subroutine 32 33 96.9
pod 0 2 0.0
total 250 304 82.2


line stmt bran cond sub pod time code
1             package Fennec;
2 137     137   81321 use strict;
  137         966  
  137         3531  
3 137     137   842 use warnings;
  137         483  
  137         3886  
4              
5 137     137   29113 BEGIN { require Fennec::Runner }
6              
7 137     137   50978 use Fennec::Test;
  137         347  
  137         5731  
8 137     137   844 use Fennec::Util qw/inject_sub require_module verbose_message/;
  137         247  
  137         2736  
9 137     137   94454 use Carp qw/croak carp/;
  137         244  
  137         25630  
10             our $VERSION = '2.018';
11              
12             sub defaults {
13             (
14             utils => [
15             'Test::More',
16             'Test::Warn',
17             'Test::Exception',
18             'Test::Workflow',
19             'Mock::Quick',
20             'Child',
21             ],
22             parallel => defined $ENV{'FENNEC_PARALLEL'} ? $ENV{'FENNEC_PARALLEL'} : 3,
23             runner_class => 'Fennec::Runner',
24             with_tests => [],
25             Child => ['child'],
26 274 50 50 274 0 5178 debug => $ENV{'FENNEC_DEBUG'} || 0,
27             );
28             }
29              
30             sub _setup_class {
31 137     137   334 my $class = shift;
32 137         512 my ( $runner, $importer, $load ) = @_;
33 137 100       534 return unless $load;
34              
35 2         6 require_module $load;
36              
37 137     137   928 no strict 'refs';
  137         349  
  137         45543  
38 2         9380 *{"$importer\::CLASS"} = \$load;
  2         10  
39 2     1   8 *{"$importer\::class"} = sub { $load };
  2         8  
  1         37  
40             }
41              
42             sub import {
43 137     137   80648 my $class = shift;
44 137         389 my $importer = caller;
45              
46 137         383 my %defaults = $class->defaults;
47 137   50     838 $defaults{runner_class} ||= 'Fennec::Runner';
48 137         611 my %params = ( %defaults, @_ );
49              
50 137 50 0     572 $ENV{FENNEC_SEED} ||= $params{seed} if $params{seed};
51 137 50 0     539 $ENV{FENNEC_DEBUG} ||= $params{debug} if $params{debug};
52              
53             my ( $runner, $runner_init ) = $class->_get_runner(
54             $importer,
55             $defaults{runner_class},
56             $defaults{runner_params},
57 137         993 );
58              
59 137         1011 verbose_message("Entering build stage: $importer\n");
60              
61 137         264 push @{$runner->test_classes} => $importer;
  137         1262  
62              
63 137         1192 my $meta = $class->_init_meta( $importer, %params );
64              
65 137         842 $class->_setup_class( $runner, $importer, $params{class} );
66 137         653 $class->_process_deps( $runner, $params{skip_without} );
67 135         520 $class->_set_isa( $importer, 'Fennec::Test', $meta->base );
68 135         5018 $class->_load_utils( $importer, %params );
69              
70             # Intercept Mock::Quick mocks
71 135         789 my $wfmeta = $importer->TEST_WORKFLOW;
72 135 50 33     1037 if ( $wfmeta && grep { $_ eq 'Mock::Quick' } @{$defaults{utils} || []}) {
  813 50       1670  
  135         701  
73             my $intercept = sub {
74 24     24   68 my ($code) = @_;
75 24         91 my @caller = caller;
76              
77 24         304 my $store = $wfmeta->control_store;
78 24 100       165 return push @$store => $code->() if $store;
79              
80 10   33     60 my $layer = $wfmeta->peek_layer || $wfmeta->root_layer;
81 10         40 $layer->add_control($code);
82 135         798 };
83 137     137   1060 no strict 'refs';
  137         369  
  137         27204  
84 135     24   535 *{"$importer\::QINTERCEPT"} = sub{ $intercept };
  135         691  
  24         2190  
85             }
86              
87 135         1103 $class->_with_tests( $importer, $params{with_tests} );
88 135         977 $class->init( %params, importer => $importer, meta => $meta );
89              
90 135 50 33     951 if ($ENV{FENNEC_DEBUG} || $params{debug}) {
91 0         0 require Time::HiRes;
92 0         0 my $collector;
93             my $debug = sub {
94 0     0   0 my $msg = pop;
95              
96 0         0 my ($sec, $ms) = Time::HiRes::gettimeofday();
97 0         0 my $line = sprintf(
98             "FENNEC_DEBUG_CUSTOM:PID:%d\0SEC:%d\0MSEC:%d\0MESSAGE:%s\n",
99             $$,
100             $sec,
101             $ms,
102             $msg
103             );
104 0   0     0 $collector ||= Fennec::Runner->new->collector;
105 0         0 $collector->diag($line);
106 0         0 };
107 137     137   1232 no strict 'refs';
  137         532  
  137         24967  
108 0         0 *{"$importer\::fennec_debug"} = $debug;
  0         0  
109             }
110              
111             $class->_export_done_testing(
112 135         724 $importer,
113             $runner,
114             $runner_init,
115             );
116              
117 135 100 33     1264 $class->after_import({
118             importer => $importer,
119             runner => $runner,
120             meta => $meta,
121             wf_meta => $wfmeta,
122             layer => $wfmeta->peek_layer || $wfmeta->root_layer,
123             }) if $class->can('after_import');
124              
125 135         1115 verbose_message("Entering primary stage: $importer\n");
126             }
127              
128             sub init {
129 135     135 0 299 my $class = shift;
130 135         784 my %params = @_;
131 135         332 my $importer = $params{importer};
132 135         288 my $meta = $params{meta};
133              
134 135         446 my $wfmeta = $importer->TEST_WORKFLOW;
135 135 100       629 $wfmeta->test_sort( $meta->test_sort )
136             if $meta->test_sort;
137              
138 137     137   866 no strict 'refs';
  137         229  
  137         59769  
139 135         280 my $stash = \%{"$importer\::"};
  135         533  
140 135         1018 delete $stash->{$_} for qw/run_tests done_testing/;
141             }
142              
143             sub _get_runner {
144 137     137   273 my $class = shift;
145 137         711 my ( $importer, $runner_class, $runner_params ) = @_;
146              
147 137         662 require_module $runner_class;
148 137         738 my $runner_init = $runner_class->is_initialized;
149              
150 137 50 66     1101 croak "Fennec cannot be used in package 'main' when the test is used with Fennec::Finder"
151             if $runner_init && $importer eq 'main';
152              
153 137 100       494 if ($runner_init) {
154 48         624 my $runner = $runner_class->new;
155 48 50       794 carp "Runner is already initialized, but it is not a $runner_class"
156             unless $runner->isa($runner_class);
157              
158 48 50       863 carp "Runner is already initialized, ignoring 'runner_params'"
159             if $runner_params;
160              
161 48         221 return ( $runner, $runner_init );
162             }
163              
164 89 50       301 my $runner = $runner_class->new(
165             parallel => 0,
166             $runner_params ? (%$runner_params) : (),
167             );
168              
169 89         32798 require Fennec::EndRunner;
170 89         602 Fennec::EndRunner->set_pid($$);
171 89         286 Fennec::EndRunner->set_runner($runner);
172              
173 89         322 return ( $runner, $runner_init );
174             }
175              
176             sub _process_deps {
177 137     137   252 my $class = shift;
178 137         443 my ( $runner, $deps ) = @_;
179              
180 137 100 66     591 return unless $deps && @$deps;
181              
182 2         3 for my $require (@$deps) {
183 2 50       2 unless ( eval { require_module $require; 1 } ) {
  2         7  
  0         0  
184 2         13 $runner->_skip_all(1);
185 2         5 $runner->collector->skip("'$require' is not installed");
186 2         6 $runner->collector->finish;
187 2         654 exit 0;
188             }
189             }
190             }
191              
192             sub _init_meta {
193 137     137   347 my $class = shift;
194 137         709 my ( $importer, %params ) = @_;
195              
196 137         42741 require Fennec::Meta;
197              
198 137         1032 my $meta = Fennec::Meta->new(
199             %params,
200              
201             # Well, this is confusing.
202             fennec => $class,
203             class => $importer,
204             );
205              
206 137     1503   1023 inject_sub( $importer, 'FENNEC', sub { $meta } );
  1503         9728  
207              
208 137         495 return $meta;
209             }
210              
211             sub _set_isa {
212 135     135   387 my $class = shift;
213 135         418 my ( $importer, @bases ) = @_;
214              
215 135         381 for my $base (@bases) {
216 270 100       926 next unless $base;
217 137     137   852 no strict 'refs';
  137         302  
  137         33600  
218 135         599 require_module $base;
219 135         1904 push @{"$importer\::ISA"} => $base
220 135 50       400 unless grep { $_ eq $base } @{"$importer\::ISA"};
  0         0  
  135         1228  
221             }
222             }
223              
224             sub _load_utils {
225 135     135   2656 my $class = shift;
226 135         873 my ( $importer, %params ) = @_;
227              
228 135         424 my $utils = $params{utils};
229 135 50 33     1156 return unless $utils && @$utils;
230              
231 135         358 for my $util (@$utils) {
232 813         2960 require_module $util;
233 813   100     2464102 my $args = $params{$util} || [];
234 813         2446 my $code = "package $importer; $util\->import(\@\$args); 1";
235 813 50       46873 eval $code || die $@;
236             }
237             }
238              
239             sub _with_tests {
240 135     135   326 my $class = shift;
241 135         406 my ( $importer, $classes ) = @_;
242              
243 135 100 66     828 return unless $classes && @$classes;
244              
245 2         7 $importer->TEST_WORKFLOW->root_layer->merge_in( undef, @$classes );
246             }
247              
248             sub _export_done_testing {
249 135     135   396 my $class = shift;
250 135         382 my ( $importer, $runner, $runner_init ) = @_;
251              
252 135 100       549 if ($runner_init) {
253 137     137   18350 no strict 'refs';
  137         271  
  137         6871  
254 137     137   835 no warnings 'redefine';
  137         427  
  137         14414  
255 47         325 *{"$importer\::done_testing"} = sub {
256 47 100   47   505 $importer->FENNEC->post(@_) if @_;
257 47         600 return 1;
258 47         216 };
259             }
260             else {
261 137     137   901 no strict 'refs';
  137         272  
  137         4681  
262 137     137   725 no warnings 'redefine';
  137         247  
  137         17019  
263 88         195 my $has_run = 0;
264 88         472 *{"$importer\::done_testing"} = sub {
265 77 50   77   568 croak "done_testing() called more than once!"
266             if $has_run++;
267              
268 77         604 Fennec::EndRunner->set_runner(undef);
269              
270 77 100       287 $importer->FENNEC->post(@_) if @_;
271 77         453 $runner->run();
272              
273 18         3012 1;
274 88         408 };
275             }
276             }
277              
278             1;
279              
280             __END__