File Coverage

File:blib/lib/App/Test/Generator/Planner.pm
Coverage:100.0%

linestmtbrancondsubtimecode
1package 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
14our $VERSION = '0.41';
15
16# Accessor type strings used in plan_all() strategy mapping
17Readonly my $ACCESSOR_GET      => 'get';
18Readonly my $ACCESSOR_GETSET   => 'getset';
19Readonly my $ACCESSOR_INJECTOR => 'injector';
20
21# Output type string for boolean detection
22Readonly 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
69sub 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
113sub 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
190sub 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
2241;