| File: | blib/lib/App/Test/Generator/Planner.pm |
| Coverage: | 100.0% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package App::Test::Generator::Planner; | |||||
| 2 | ||||||
| 3 | 9 9 9 | 132808 8 120 | use strict; | |||
| 4 | 9 9 9 | 13 8 170 | use warnings; | |||
| 5 | 9 9 9 | 15 7 154 | use Carp qw(croak); | |||
| 6 | 9 9 9 | 402 3260 145 | use Readonly; | |||
| 7 | ||||||
| 8 | 9 9 9 | 968 11 145 | use App::Test::Generator::TestStrategy; | |||
| 9 | 9 9 9 | 977 9 124 | use App::Test::Generator::Planner::Isolation; | |||
| 10 | 9 9 9 | 932 18 113 | use App::Test::Generator::Planner::Fixture; | |||
| 11 | 9 9 9 | 943 9 110 | use App::Test::Generator::Planner::Mock; | |||
| 12 | 9 9 9 | 912 10 1795 | use App::Test::Generator::Planner::Grouping; | |||
| 13 | ||||||
| 14 | our $VERSION = '0.41'; | |||||
| 15 | ||||||
| 16 | # Accessor type strings used in plan_all() strategy mapping | |||||
| 17 | Readonly my $ACCESSOR_GET => 'get'; | |||||
| 18 | Readonly my $ACCESSOR_GETSET => 'getset'; | |||||
| 19 | Readonly my $ACCESSOR_INJECTOR => 'injector'; | |||||
| 20 | ||||||
| 21 | # Output type string for boolean detection | |||||
| 22 | Readonly my $OUTPUT_BOOLEAN => 'boolean'; | |||||
| 23 | ||||||
| 24 - 67 | =head1 VERSION
Version 0.41
=head2 new
Construct a new Planner instance.
my $planner = App::Test::Generator::Planner->new(
schemas => \%schemas,
package => 'My::Module',
);
=head3 Arguments
=over 4
=item * C<schemas> - hashref of method name to schema hashref. Required.
=item * C<package> - the Perl package name of the module under test. Required.
=back
=head3 Returns
A blessed hashref. Croaks if C<schemas> or C<package> is missing.
=head3 API specification
=head4 input
{
schemas => { type => HASHREF },
package => { type => SCALAR },
}
=head4 output
{
type => OBJECT,
isa => 'App::Test::Generator::Planner',
}
=cut | |||||
| 68 | ||||||
| 69 | sub new { | |||||
| 70 | 58 | 232987 | my ($class, %args) = @_; | |||
| 71 | ||||||
| 72 | # schemas and package are required for meaningful planning | |||||
| 73 | 58 | 126 | croak 'schemas required' unless defined $args{schemas}; | |||
| 74 | 56 | 73 | croak 'package required' unless defined $args{package}; | |||
| 75 | ||||||
| 76 | return bless { | |||||
| 77 | schemas => $args{schemas}, | |||||
| 78 | package => $args{package}, | |||||
| 79 | 54 | 108 | }, $class; | |||
| 80 | } | |||||
| 81 | ||||||
| 82 - 111 | =head2 plan_all
Generate a test plan for every method in the schema.
my $plans = $planner->plan_all();
=head3 Arguments
None beyond C<$self>.
=head3 Returns
A hashref mapping method name to a plan hashref. Each plan hashref
contains boolean flags such as C<getter_test>, C<getset_test>,
C<object_injection_test>, and C<boolean_test> indicating which test
types should be emitted for that method.
=head3 API specification
=head4 input
{
self => { type => OBJECT, isa => 'App::Test::Generator::Planner' },
}
=head4 output
{ type => HASHREF }
=cut | |||||
| 112 | ||||||
| 113 | sub plan_all { | |||||
| 114 | 46 | 336 | my $self = $_[0]; | |||
| 115 | 46 | 34 | my %method_plan; | |||
| 116 | ||||||
| 117 | # Build a plan for each method in the schema | |||||
| 118 | 46 46 | 35 78 | foreach my $method (keys %{ $self->{schemas} }) { | |||
| 119 | 159 | 148 | my $schema = $self->{schemas}{$method}; | |||
| 120 | 159 | 85 | my %plan; | |||
| 121 | ||||||
| 122 | # Map accessor type to the appropriate test flag | |||||
| 123 | 159 | 160 | if($schema->{accessor} && $schema->{accessor}->{type}) { | |||
| 124 | 26 | 25 | my $type = $schema->{accessor}->{type}; | |||
| 125 | 26 | 48 | if($type eq $ACCESSOR_GET) { | |||
| 126 | 8 | 28 | $plan{getter_test} = 1; | |||
| 127 | } elsif($type eq $ACCESSOR_GETSET) { | |||||
| 128 | 6 | 33 | $plan{getset_test} = 1; | |||
| 129 | } elsif($type eq $ACCESSOR_INJECTOR) { | |||||
| 130 | # Object injection requires a mock object in the test | |||||
| 131 | 4 | 29 | $plan{object_injection_test} = 1; | |||
| 132 | } | |||||
| 133 | } | |||||
| 134 | ||||||
| 135 | # Boolean output type requires a predicate test | |||||
| 136 | 159 | 207 | if($schema->{output}->{type} && $schema->{output}->{type} eq $OUTPUT_BOOLEAN) { | |||
| 137 | 10 | 35 | $plan{boolean_test} = 1; | |||
| 138 | } | |||||
| 139 | ||||||
| 140 | 159 | 189 | $method_plan{$method} = \%plan; | |||
| 141 | } | |||||
| 142 | ||||||
| 143 | 46 | 63 | return \%method_plan; | |||
| 144 | } | |||||
| 145 | ||||||
| 146 - 188 | =head2 build_plan
Build a comprehensive test plan by running the schema through all
available planning subsystems in sequence: strategy generation,
isolation, fixture, mock, and grouping.
my $plan = $planner->build_plan();
=head3 Arguments
None beyond C<$self>.
=head3 Returns
A hashref with five keys: C<strategy> (from
L<App::Test::Generator::TestStrategy/generate_plan>), C<isolation>
(from L<App::Test::Generator::Planner::Isolation/plan>, given the
strategy as context), C<fixture> (from
L<App::Test::Generator::Planner::Fixture/plan>, given the isolation
plan as context), C<mock> (from
L<App::Test::Generator::Planner::Mock/plan>), and C<groups> (from
L<App::Test::Generator::Planner::Grouping/plan>).
=head3 Notes
Unlike C<plan_all>, which is the planner actually used by the test
generation pipeline, C<build_plan> is not currently called anywhere
else in this distribution. It is kept as a public entry point for
the richer combined plan it produces.
=head3 API specification
=head4 input
{
self => { type => OBJECT, isa => 'App::Test::Generator::Planner' },
}
=head4 output
{ type => HASHREF }
=cut | |||||
| 189 | ||||||
| 190 | sub build_plan { | |||||
| 191 | 4 | 702 | my $self = $_[0]; | |||
| 192 | ||||||
| 193 | # Generate the base strategy from the schema | |||||
| 194 | my $strategy_engine = App::Test::Generator::TestStrategy->new( | |||||
| 195 | schema => $self->{schemas} | |||||
| 196 | 4 | 19 | ); | |||
| 197 | 4 | 25 | my $strategy = $strategy_engine->generate_plan(); | |||
| 198 | ||||||
| 199 | # Apply isolation, fixture, mock and grouping layers | |||||
| 200 | my $isolation = App::Test::Generator::Planner::Isolation->new()->plan( | |||||
| 201 | 4 | 15 | $self->{schemas}, $strategy | |||
| 202 | ); | |||||
| 203 | my $fixture = App::Test::Generator::Planner::Fixture->new()->plan( | |||||
| 204 | 4 | 24 | $self->{schemas}, $isolation | |||
| 205 | ); | |||||
| 206 | 4 | 23 | my $mock = App::Test::Generator::Planner::Mock->new()->plan($self->{schemas}); | |||
| 207 | 4 | 18 | my $groups = App::Test::Generator::Planner::Grouping->new()->plan($self->{schemas}); | |||
| 208 | ||||||
| 209 | return { | |||||
| 210 | 4 | 29 | strategy => $strategy, | |||
| 211 | isolation => $isolation, | |||||
| 212 | fixture => $fixture, | |||||
| 213 | mock => $mock, | |||||
| 214 | groups => $groups, | |||||
| 215 | }; | |||||
| 216 | } | |||||
| 217 | ||||||
| 218 - 222 | =head1 AUTHOR Nigel Horne =cut | |||||
| 223 | ||||||
| 224 | 1; | |||||