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   153618 use strict;
  137         551  
  137         5710  
3 137     137   1031 use warnings;
  137         423  
  137         5966  
4              
5 137     137   67806 BEGIN { require Fennec::Runner }
6              
7 137     137   98687 use Fennec::Test;
  137         529  
  137         10157  
8 137     137   838 use Fennec::Util qw/inject_sub require_module verbose_message/;
  137         286  
  137         3328  
9 137     137   154211 use Carp qw/croak carp/;
  137         342  
  137         51173  
10             our $VERSION = '2.017';
11              
12             sub defaults {
13             (
14 274 50 50 274 0 11274 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             debug => $ENV{'FENNEC_DEBUG'} || 0,
27             );
28             }
29              
30             sub _setup_class {
31 137     137   362 my $class = shift;
32 137         551 my ( $runner, $importer, $load ) = @_;
33 137 100       989 return unless $load;
34              
35 2         184 require_module $load;
36              
37 137     137   11205 no strict 'refs';
  137         327  
  137         121414  
38 2         29054 *{"$importer\::CLASS"} = \$load;
  2         18  
39 2     1   16 *{"$importer\::class"} = sub { $load };
  2         16  
  1         72  
40             }
41              
42             sub import {
43 137     137   158487 my $class = shift;
44 137         451 my $importer = caller;
45              
46 137         828 my %defaults = $class->defaults;
47 137   50     2597 $defaults{runner_class} ||= 'Fennec::Runner';
48 137         870 my %params = ( %defaults, @_ );
49              
50 137 50 0     945 $ENV{FENNEC_SEED} ||= $params{seed} if $params{seed};
51 137 50 0     623 $ENV{FENNEC_DEBUG} ||= $params{debug} if $params{debug};
52              
53 137         1139 my ( $runner, $runner_init ) = $class->_get_runner(
54             $importer,
55             $defaults{runner_class},
56             $defaults{runner_params},
57             );
58              
59 137         3253 verbose_message("Entering build stage: $importer\n");
60              
61 137         371 push @{$runner->test_classes} => $importer;
  137         1002  
62              
63 137         1415 my $meta = $class->_init_meta( $importer, %params );
64              
65 137         1066 $class->_setup_class( $runner, $importer, $params{class} );
66 137         1050 $class->_process_deps( $runner, $params{skip_without} );
67 135         1003 $class->_set_isa( $importer, 'Fennec::Test', $meta->base );
68 135         925 $class->_load_utils( $importer, %params );
69              
70             # Intercept Mock::Quick mocks
71 135         1340 my $wfmeta = $importer->TEST_WORKFLOW;
72 135 50 33     1593 if ( $wfmeta && grep { $_ eq 'Mock::Quick' } @{$defaults{utils} || []}) {
  813 50       2470  
  135         872  
73             my $intercept = sub {
74 20     20   46 my ($code) = @_;
75 20         164 my @caller = caller;
76              
77 20         144 my $store = $wfmeta->control_store;
78 20 100       115 return push @$store => $code->() if $store;
79              
80 10   33     45 my $layer = $wfmeta->peek_layer || $wfmeta->root_layer;
81 10         60 $layer->add_control($code);
82 135         1209 };
83 137     137   1053 no strict 'refs';
  137         278  
  137         41861  
84 135     20   610 *{"$importer\::QINTERCEPT"} = sub{ $intercept };
  135         913  
  20         2468  
85             }
86              
87 135         1518 $class->_with_tests( $importer, $params{with_tests} );
88 135         1060 $class->init( %params, importer => $importer, meta => $meta );
89              
90 135 50 33     1387 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   900 no strict 'refs';
  137         340  
  137         35106  
108 0         0 *{"$importer\::fennec_debug"} = $debug;
  0         0  
109             }
110              
111             $class->_export_done_testing(
112 135         765 $importer,
113             $runner,
114             $runner_init,
115             );
116              
117 135 100 33     1742 $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         1030 verbose_message("Entering primary stage: $importer\n");
126             }
127              
128             sub init {
129 135     135 0 330 my $class = shift;
130 135         1010 my %params = @_;
131 135         387 my $importer = $params{importer};
132 135         359 my $meta = $params{meta};
133              
134 135         578 my $wfmeta = $importer->TEST_WORKFLOW;
135 135 100       869 $wfmeta->test_sort( $meta->test_sort )
136             if $meta->test_sort;
137              
138 137     137   1038 no strict 'refs';
  137         310  
  137         128338  
139 135         287 my $stash = \%{"$importer\::"};
  135         745  
140 135         1456 delete $stash->{$_} for qw/run_tests done_testing/;
141             }
142              
143             sub _get_runner {
144 137     137   335 my $class = shift;
145 137         705 my ( $importer, $runner_class, $runner_params ) = @_;
146              
147 137         819 require_module $runner_class;
148 137         1368 my $runner_init = $runner_class->is_initialized;
149              
150 137 50 66     5359 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       537 if ($runner_init) {
154 48         359 my $runner = $runner_class->new;
155 48 50       884 carp "Runner is already initialized, but it is not a $runner_class"
156             unless $runner->isa($runner_class);
157              
158 48 50       180 carp "Runner is already initialized, ignoring 'runner_params'"
159             if $runner_params;
160              
161 48         211 return ( $runner, $runner_init );
162             }
163              
164 89 50       784 my $runner = $runner_class->new(
165             parallel => 0,
166             $runner_params ? (%$runner_params) : (),
167             );
168              
169 89         53498 require Fennec::EndRunner;
170 89         707 Fennec::EndRunner->set_pid($$);
171 89         365 Fennec::EndRunner->set_runner($runner);
172              
173 89         437 return ( $runner, $runner_init );
174             }
175              
176             sub _process_deps {
177 137     137   329 my $class = shift;
178 137         2160 my ( $runner, $deps ) = @_;
179              
180 137 100 66     984 return unless $deps && @$deps;
181              
182 2         9 for my $require (@$deps) {
183 2 50       4 unless ( eval { require_module $require; 1 } ) {
  2         9  
  0         0  
184 2         32 $runner->_skip_all(1);
185 2         16 $runner->collector->skip("'$require' is not installed");
186 2         10 $runner->collector->finish;
187 2         1568 exit 0;
188             }
189             }
190             }
191              
192             sub _init_meta {
193 137     137   371 my $class = shift;
194 137         845 my ( $importer, %params ) = @_;
195              
196 137         89278 require Fennec::Meta;
197              
198 137         1364 my $meta = Fennec::Meta->new(
199             %params,
200              
201             # Well, this is confusing.
202             fennec => $class,
203             class => $importer,
204             );
205              
206 137     1495   1800 inject_sub( $importer, 'FENNEC', sub { $meta } );
  1495         15977  
207              
208 137         529 return $meta;
209             }
210              
211             sub _set_isa {
212 135     135   397 my $class = shift;
213 135         454 my ( $importer, @bases ) = @_;
214              
215 135         420 for my $base (@bases) {
216 270 100       1333 next unless $base;
217 137     137   989 no strict 'refs';
  137         362  
  137         57309  
218 135         608 require_module $base;
219 135         2852 push @{"$importer\::ISA"} => $base
  0         0  
220 135 50       247 unless grep { $_ eq $base } @{"$importer\::ISA"};
  135         2025  
221             }
222             }
223              
224             sub _load_utils {
225 135     135   330 my $class = shift;
226 135         577 my ( $importer, %params ) = @_;
227              
228 135         320 my $utils = $params{utils};
229 135 50 33     1433 return unless $utils && @$utils;
230              
231 135         375 for my $util (@$utils) {
232 813         4197 require_module $util;
233 813   100     4949016 my $args = $params{$util} || [];
234 813         3179 my $code = "package $importer; $util\->import(\@\$args); 1";
235 813 50       80162 eval $code || die $@;
236             }
237             }
238              
239             sub _with_tests {
240 135     135   345 my $class = shift;
241 135         376 my ( $importer, $classes ) = @_;
242              
243 135 100 66     1309 return unless $classes && @$classes;
244              
245 2         10 $importer->TEST_WORKFLOW->root_layer->merge_in( undef, @$classes );
246             }
247              
248             sub _export_done_testing {
249 135     135   337 my $class = shift;
250 135         1213 my ( $importer, $runner, $runner_init ) = @_;
251              
252 135 100       513 if ($runner_init) {
253 137     137   900 no strict 'refs';
  137         286  
  137         5369  
254 137     137   833 no warnings 'redefine';
  137         281  
  137         48554  
255 47         568 *{"$importer\::done_testing"} = sub {
256 47 100   47   511 $importer->FENNEC->post(@_) if @_;
257 47         621 return 1;
258 47         289 };
259             }
260             else {
261 137     137   887 no strict 'refs';
  137         1255  
  137         5425  
262 137     137   1087 no warnings 'redefine';
  137         371  
  137         25394  
263 88         203 my $has_run = 0;
264 88         578 *{"$importer\::done_testing"} = sub {
265 77 50   77   2418 croak "done_testing() called more than once!"
266             if $has_run++;
267              
268 77         852 Fennec::EndRunner->set_runner(undef);
269              
270 77 100       429 $importer->FENNEC->post(@_) if @_;
271 77         593 $runner->run();
272              
273 18         3058 1;
274 88         476 };
275             }
276             }
277              
278             1;
279              
280             __END__