TER1 (Statement): 100.00%
TER2 (Branch): 100.00%
TER3 (LCSAJ): 100.0% (1/1)
Approximate LCSAJ segments: 15
● 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.
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;