lib/App/Test/Generator/Planner.pm

Structural Coverage (Approximate)

TER1 (Statement): 100.00%
TER2 (Branch): 100.00%
TER3 (LCSAJ): 100.0% (1/1)
Approximate LCSAJ segments: 15

LCSAJ Legend

Covered — this LCSAJ path was executed during testing.

Not covered — this LCSAJ path was never executed. These are the paths to focus on.

Multiple dots on a line indicate that multiple control-flow paths begin at that line. Hovering over any dot shows:

        start → end → jump
        

Uncovered paths show [NOT COVERED] in the tooltip.

Mutant Testing Legend

Survived (tests missed this) Killed (tests detected this) No mutation
    1: package App::Test::Generator::Planner;
    2: 
    3: use strict;
    4: use warnings;
    5: use Carp qw(croak);
    6: use Readonly;
    7: 
    8: use App::Test::Generator::TestStrategy;
    9: use App::Test::Generator::Planner::Isolation;
   10: use App::Test::Generator::Planner::Fixture;
   11: use App::Test::Generator::Planner::Mock;
   12: 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: =head1 VERSION
   25: 
   26: Version 0.41
   27: 
   28: =head2 new
   29: 
   30: Construct a new Planner instance.
   31: 
   32:     my $planner = App::Test::Generator::Planner->new(
   33:         schemas => \%schemas,
   34:         package => 'My::Module',
   35:     );
   36: 
   37: =head3 Arguments
   38: 
   39: =over 4
   40: 
   41: =item * C<schemas> - hashref of method name to schema hashref. Required.
   42: 
   43: =item * C<package> - the Perl package name of the module under test. Required.
   44: 
   45: =back
   46: 
   47: =head3 Returns
   48: 
   49: A blessed hashref. Croaks if C<schemas> or C<package> is missing.
   50: 
   51: =head3 API specification
   52: 
   53: =head4 input
   54: 
   55:     {
   56:         schemas => { type => HASHREF },
   57:         package => { type => SCALAR  },
   58:     }
   59: 
   60: =head4 output
   61: 
   62:     {
   63:         type => OBJECT,
   64:         isa  => 'App::Test::Generator::Planner',
   65:     }
   66: 
   67: =cut
   68: 
   69: sub new {
   70: 	my ($class, %args) = @_;
   71: 
   72: 	# schemas and package are required for meaningful planning
   73: 	croak 'schemas required' unless defined $args{schemas};
   74: 	croak 'package required' unless defined $args{package};
   75: 
   76: 	return bless {
   77: 		schemas => $args{schemas},
   78: 		package => $args{package},
   79: 	}, $class;
   80: }
   81: 
   82: =head2 plan_all
   83: 
   84: Generate a test plan for every method in the schema.
   85: 
   86:     my $plans = $planner->plan_all();
   87: 
   88: =head3 Arguments
   89: 
   90: None beyond C<$self>.
   91: 
   92: =head3 Returns
   93: 
   94: A hashref mapping method name to a plan hashref. Each plan hashref
   95: contains boolean flags such as C<getter_test>, C<getset_test>,

Mutants (Total: 1, Killed: 1, Survived: 0)

96: C<object_injection_test>, and C<boolean_test> indicating which test 97: types should be emitted for that method.

Mutants (Total: 1, Killed: 1, Survived: 0)

98: 99: =head3 API specification 100: 101: =head4 input 102: 103: { 104: self => { type => OBJECT, isa => 'App::Test::Generator::Planner' }, 105: } 106: 107: =head4 output 108:

Mutants (Total: 1, Killed: 1, Survived: 0)

109: { type => HASHREF } 110: 111: =cut 112: 113: sub plan_all { 114 → 118 → 143 114: my $self = $_[0]; 115: my %method_plan; 116: 117: # Build a plan for each method in the schema 118: foreach my $method (keys %{ $self->{schemas} }) { 119: my $schema = $self->{schemas}{$method}; 120: my %plan; 121: 122: # Map accessor type to the appropriate test flag 123: if($schema->{accessor} && $schema->{accessor}->{type}) { 124: my $type = $schema->{accessor}->{type}; 125: if($type eq $ACCESSOR_GET) { 126: $plan{getter_test} = 1; 127: } elsif($type eq $ACCESSOR_GETSET) { 128: $plan{getset_test} = 1; 129: } elsif($type eq $ACCESSOR_INJECTOR) { 130: # Object injection requires a mock object in the test 131: $plan{object_injection_test} = 1; 132: } 133: } 134: 135: # Boolean output type requires a predicate test 136: if($schema->{output}->{type} && $schema->{output}->{type} eq $OUTPUT_BOOLEAN) { 137: $plan{boolean_test} = 1; 138: } 139: 140: $method_plan{$method} = \%plan; 141: } 142: 143: return \%method_plan; 144: } 145: 146: =head2 build_plan 147: 148: Build a comprehensive test plan by running the schema through all 149: available planning subsystems in sequence: strategy generation, 150: isolation, fixture, mock, and grouping. 151: 152: my $plan = $planner->build_plan(); 153: 154: =head3 Arguments 155: 156: None beyond C<$self>. 157: 158: =head3 Returns 159: 160: A hashref with five keys: C<strategy> (from 161: L<App::Test::Generator::TestStrategy/generate_plan>), C<isolation> 162: (from L<App::Test::Generator::Planner::Isolation/plan>, given the 163: strategy as context), C<fixture> (from 164: L<App::Test::Generator::Planner::Fixture/plan>, given the isolation 165: plan as context), C<mock> (from 166: L<App::Test::Generator::Planner::Mock/plan>), and C<groups> (from 167: L<App::Test::Generator::Planner::Grouping/plan>). 168: 169: =head3 Notes 170: 171: Unlike C<plan_all>, which is the planner actually used by the test 172: generation pipeline, C<build_plan> is not currently called anywhere 173: else in this distribution. It is kept as a public entry point for 174: the richer combined plan it produces. 175: 176: =head3 API specification 177: 178: =head4 input 179: 180: { 181: self => { type => OBJECT, isa => 'App::Test::Generator::Planner' }, 182: } 183: 184: =head4 output 185: 186: { type => HASHREF } 187: 188: =cut 189: 190: sub build_plan { 191: 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: ); 197: 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: $self->{schemas}, $strategy 202: ); 203: my $fixture = App::Test::Generator::Planner::Fixture->new()->plan( 204: $self->{schemas}, $isolation 205: ); 206: my $mock = App::Test::Generator::Planner::Mock->new()->plan($self->{schemas}); 207: my $groups = App::Test::Generator::Planner::Grouping->new()->plan($self->{schemas}); 208: 209: return { 210: strategy => $strategy, 211: isolation => $isolation, 212: fixture => $fixture, 213: mock => $mock, 214: groups => $groups, 215: }; 216: } 217: 218: =head1 AUTHOR 219: 220: Nigel Horne 221: 222: =cut 223: 224: 1;