File Coverage

blib/lib/Test/Mini/Runner.pm
Criterion Covered Total %
statement 86 88 97.7
branch 10 14 71.4
condition 4 9 44.4
subroutine 23 24 95.8
pod 14 14 100.0
total 137 149 91.9


line stmt bran cond sub pod time code
1             # Default Test Runner.
2             #
3             # The Test::Mini::Runner is responsible for finding and running the
4             # appropriate tests, setting up output logging, and returning an appropriate
5             # status code. For those looking to write tests with this framework, the
6             # points of note are as follows:
7             #
8             # * Tests are run automatically at process exit.
9             # * All test cases (subclasses of {Test::Mini::TestCase}) that have been
10             # loaded at that time will be considered. This includes indirect
11             # subclasses.
12             # * Within each test case, all methods defined with a name matching
13             # /^test.+/ will be run.
14             # * Each test will run in its own test case instance.
15             # * Tests will be run in random order.
16             # * #setup will be called before each test is run.
17             # * #teardown will be called after each test is run.
18             # * Inherited tests are *not* run.
19             # * Tests may be run via +`prove`+, by loading (via +use+, +do+ or +require+)
20             # the files into another script, or by simply executing a file containing a
21             # test case in the Perl interpreter.
22             # * If you want to use a non-TAP output logger, +`prove`+ is not an option.
23             # * Options may be passed in either as command line options, or as environment
24             # variables.
25             # * Environment variable names are prefixed with 'TEST_MINI_'.
26             # * Valid options are:
27             # * +verbose+ - Specifies the logger's verbosity.
28             # * +filter+ - Only tests with names matching this pattern should be run.
29             # * +logger+ - Specifies an alternate output logger class.
30             # * +seed+ - Specifies a random number seed; used to specify repeatable
31             # test orderings.
32             package Test::Mini::Runner;
33 4     4   1221 use strict;
  4         9  
  4         177  
34 4     4   25 use warnings;
  4         7  
  4         148  
35              
36 4     4   5747 use Getopt::Long;
  4         64395  
  4         29  
37 4     4   762 use Try::Tiny;
  4         11  
  4         283  
38 4     4   4315 use MRO::Compat;
  4         13760  
  4         127  
39 4     4   786 use Test::Mini::TestCase;
  4         10  
  4         107  
40 4     4   21 use List::Util qw/ shuffle /;
  4         6  
  4         2305  
41              
42             # Constructor.
43             # Arguments may be provided explicitly to the constructor or implicitly via
44             # either @ARGV (parsed by {Getopt::Long}) or environment variables
45             # ("TEST_MINI_$option").
46             #
47             # @param [Hash] %args Initial state for the new instance.
48             # @option %args verbose (0) Logger verbosity.
49             # @option %args filter [String] ('') Test name filter.
50             # @option %args logger [Class] (Test::Mini::Logger::TAP) Logger class name.
51             # @option %args seed [Integer] (a random number +< 64_000_000+) Randomness seed.
52             sub new {
53 11     11 1 5360 my ($class, %args) = @_;
54              
55 11   50     443 my %argv = (
      50        
      50        
      33        
56             verbose => $ENV{TEST_MINI_VERBOSE} || 0,
57             filter => $ENV{TEST_MINI_FILTER} || '',
58             logger => $ENV{TEST_MINI_LOGGER} || 'Test::Mini::Logger::TAP',
59             seed => $ENV{TEST_MINI_SEED} || int(rand(64_000_000)),
60             );
61              
62 11         54 GetOptions(\%argv, qw/ verbose=s filter=s logger=s seed=i /);
63 11         3372 return bless { %argv, %args, exit_code => 0 }, $class;
64             }
65              
66             # @group Attribute Accessors
67              
68             # @return Logger verbosity.
69             sub verbose {
70 10     10 1 18 my $self = shift;
71 10         58 return $self->{verbose};
72             }
73              
74             # @return Test name filter.
75             sub filter {
76 10     10 1 20 my $self = shift;
77 10         33 return $self->{filter};
78             }
79              
80             # @return Logger instance.
81             sub logger {
82 210     210 1 272 my $self = shift;
83 210         981 return $self->{logger};
84             }
85              
86             # @return Randomness seed.
87             sub seed {
88 10     10 1 17 my $self = shift;
89 10         32 return $self->{seed};
90             }
91              
92             # @return Exit code, representing the status of the test run.
93             sub exit_code {
94 20     20 1 41 my $self = shift;
95 20         90 return $self->{exit_code};
96             }
97              
98             # @group Test Run Hooks
99              
100             # Begins the test run.
101             # Loads and instantiates the test output logger, then dispatches to
102             # {#run_test_suite} (passing the {#filter} and {#seed}, as appropriate).
103             #
104             # @return The result of the {#run_test_suite} call.
105             sub run {
106 3     3 1 9 my ($self) = @_;
107 3         9 my $logger = $self->logger;
108             try {
109 3 50   3   358 eval "require $logger;" or die $@;
110             }
111             catch {
112 0 0   0   0 eval "require Test::Mini::Logger::$logger;" or die $@;
113 3         32 };
114              
115 3         93 $logger = $logger->new(verbose => $self->verbose);
116 3         9 $self->{logger} = $logger;
117              
118 3         14 return $self->run_test_suite(filter => $self->filter, seed => $self->seed);
119             }
120              
121             # Runs the test suite.
122             # Finds subclasses of {Test::Mini::TestCase}, and dispatches to
123             # {#run_test_case} with the name of each test case and a list test methods to
124             # be run.
125             #
126             # @param [Hash] %args
127             # @option %args [String] filter Test name filter.
128             # @option %args [String] seed Randomness seed.
129             # @return The value of {#exit_code}.
130             sub run_test_suite {
131 10     10 1 30 my ($self, %args) = @_;
132 10         28 $self->logger->begin_test_suite(%args);
133              
134 10         31 srand($args{seed});
135 10         16 my @testcases = @{ mro::get_isarev('Test::Mini::TestCase') };
  10         78  
136              
137             # Since mro::get_isarev is guaranteed to never shrink, we should "double
138             # check" our testcases, to make sure that they actually are *still*
139             # subclasses of Test::Mini::TestCase.
140             # @see http://search.cpan.org/dist/perl-5.12.2/ext/mro/mro.pm#mro::get_isarev($classname)
141 10         28 @testcases = grep { $_->isa('Test::Mini::TestCase') } @testcases;
  9         93  
142              
143 10 100       27 $self->{exit_code} = 255 unless @testcases;
144              
145 10         62 for my $tc (shuffle @testcases) {
146 4     4   28 no strict 'refs';
  4         10  
  4         2430  
147 9 100       13 my @tests = grep { /^test.+/ && defined &{"$tc\::$_"}} keys %{"$tc\::"};
  224         586  
  54         298  
  9         98  
148 9         30 $self->run_test_case($tc, grep { $_ =~ qr/$args{filter}/ } @tests);
  54         388  
149             }
150              
151 10         42 $self->logger->finish_test_suite($self->exit_code);
152 10         36 return $self->exit_code;
153             }
154              
155             # Runs tests in a test case.
156             #
157             # @param [Class] $tc The test case to run.
158             # @param [Array] @tests A list of tests to be run.
159             sub run_test_case {
160 9     9 1 26 my ($self, $tc, @tests) = @_;
161 9         23 $self->logger->begin_test_case($tc, @tests);
162              
163 9 100       22 $self->{exit_code} = 127 unless @{[
  0         0  
164 9         14 (@tests, grep { $_->isa($tc) } @{ mro::get_isarev($tc) })
  9         56  
165             ]};
166              
167 9         56 $self->run_test($tc, $_) for shuffle @tests;
168              
169 9         25 $self->logger->finish_test_case($tc, @tests);
170 9         36 return scalar @tests;
171             }
172              
173             # Runs a specific test.
174             #
175             # @param [Class] $tc The test case owning the test method.
176             # @param [String] $test The name of the test method to be run.
177             # @return [Integer] The number of assertions called by the test.
178             sub run_test {
179 54     54 1 112 my ($self, $tc, $test) = @_;
180 54         113 $self->logger->begin_test($tc, $test);
181              
182 54         332 my $instance = $tc->new(name => $test);
183 54         248 my $assertions = $instance->run($self);
184              
185 54         134 $self->logger->finish_test($tc, $test, $assertions);
186 54         402 return $assertions;
187             }
188              
189             # @group Callbacks
190              
191             # Callback for passing tests.
192             #
193             # @param [Class] $tc The test case owning the test method.
194             # @param [String] $test The name of the passing test.
195             sub pass {
196 48     48 1 83 my ($self, $tc, $test) = @_;
197 48         165 $self->logger->pass($tc, $test);
198             }
199              
200             # Callback for skipped tests.
201             #
202             # @param [Class] $tc The test case owning the test method.
203             # @param [String] $test The name of the skipped test.
204             # @param [Test::Mini::Exception::Skip] $e The exception object.
205             sub skip {
206 1     1 1 3 my ($self, $tc, $test, $e) = @_;
207 1         14 $self->logger->skip($tc, $test, $e);
208             }
209              
210             # Callback for failing tests.
211             #
212             # @param [Class] $tc The test case owning the test method.
213             # @param [String] $test The name of the failed test.
214             # @param [Test::Mini::Exception::Assert] $e The exception object.
215             sub fail {
216 3     3 1 9 my ($self, $tc, $test, $e) = @_;
217 3 100       14 $self->{exit_code} = 1 unless $self->{exit_code};
218 3         12 $self->logger->fail($tc, $test, $e);
219             }
220              
221             # Callback for dying tests.
222             #
223             # @param [Class] $tc The test case owning the test method.
224             # @param [String] $test The name of the test with an error.
225             # @param [Test::Mini::Exception] $e The exception object.
226             sub error {
227 2     2 1 4 my ($self, $tc, $test, $e) = @_;
228 2 50       8 $self->{exit_code} = 1 unless $self->{exit_code};
229 2         6 $self->logger->error($tc, $test, $e);
230             }
231              
232             1;