lib/App/Project/Doctor/Check/Base.pm

Structural Coverage (Approximate)

TER1 (Statement): 100.00%
TER2 (Branch): 0%
TER3 (LCSAJ): -
Approximate LCSAJ segments: 1

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::Project::Doctor::Check::Base;
    2: 
    3: use strict;
    4: use warnings;
    5: use autodie qw(:all);
    6: 
    7: use Carp qw(croak);
    8: 
    9: our $VERSION = '0.02';
   10: 
   11: # ---------------------------------------------------------------------------
   12: # Constructor
   13: # ---------------------------------------------------------------------------
   14: 
   15: sub new {
   16: 	my ($class, %args) = @_;
   17: 	return bless {%args}, $class;

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

18: } 19: 20: # --------------------------------------------------------------------------- 21: # Required interface -- subclasses MUST override these three 22: # --------------------------------------------------------------------------- 23: 24: =head2 name (required) 25: 26: Short label used in the report table, e.g. C<Tests>. 27: 28: =cut 29: 30: sub name { 31: croak ref(shift) . ' must implement name()'; 32: } 33: 34: =head2 description (required) 35: 36: One-sentence description of what the check verifies. 37: 38: =cut 39: 40: sub description { 41: croak ref(shift) . ' must implement description()'; 42: } 43: 44: =head2 check( $context ) (required) 45: 46: Accepts an L<App::Project::Doctor::Context> and returns a list of 47: L<App::Project::Doctor::Finding> objects (empty list on a clean pass). 48: 49: =cut 50: 51: sub check { 52: croak ref(shift) . ' must implement check()'; 53: } 54: 55: # --------------------------------------------------------------------------- 56: # Optional interface with sensible defaults 57: # --------------------------------------------------------------------------- 58: 59: =head2 can_fix 60: 61: Returns true when this check can produce fixable findings. Default 0. 62: 63: =cut 64: 65: sub can_fix { 0 } 66: 67: =head2 category 68: 69: Grouping label for report presentation. Default C<general>. 70: 71: =cut 72: 73: sub category { 'general' } 74: 75: =head2 order 76: 77: Numeric sort key: lower numbers appear first in the report. Default 50. 78: 79: =cut 80: 81: sub order { 50 } 82: 83: 1; 84: 85: __END__ 86: 87: =head1 NAME 88: 89: App::Project::Doctor::Check::Base - Base class for all check plugins 90: 91: =head1 VERSION 92: 93: 0.02 94: 95: =head1 SYNOPSIS 96: 97: package App::Project::Doctor::Check::MyCheck; 98: 99: use strict; 100: use warnings; 101: use autodie qw(:all); 102: 103: use parent -norequire, 'App::Project::Doctor::Check::Base'; 104: 105: sub name { 'My Check' } 106: sub description { 'Verifies something important.' } 107: sub can_fix { 1 } 108: sub order { 70 } 109: 110: sub check { 111: my ($self, $ctx) = @_; 112: return () if $ctx->has_file('something-good'); 113: require App::Project::Doctor::Finding; 114: return App::Project::Doctor::Finding->new( 115: severity => 'error', 116: message => 'Missing something-good', 117: check_name => $self->name, 118: fix => sub { ... }, 119: ); 120: } 121: 122: 1; 123: 124: =head1 DESCRIPTION 125: 126: Traditional OO base class that defines the interface every 127: C<App::Project::Doctor::Check::*> plugin must implement. 128: 129: Calling C<name>, C<description>, or C<check> on an instance that has not 130: overridden them will C<croak> at runtime with a clear message. 131: 132: Subclasses inherit via: 133: 134: use parent -norequire, 'App::Project::Doctor::Check::Base'; 135: 136: The C<-norequire> flag is used because C<Base> is always loaded by the 137: orchestrator before the subclass is instantiated. 138: 139: =head1 REQUIRED METHODS 140: 141: Subclasses must implement C<name>, C<description>, and C<check>. 142: See L</SYNOPSIS> for the expected signatures. 143: 144: =head1 OPTIONAL METHODS 145: 146: =head2 can_fix 147: 148: Boolean -- default 0. 149: 150: =head2 category 151: 152: String grouping label -- default C<general>. 153: 154: =head2 order 155: 156: Numeric sort key -- default 50. 157: 158: =head3 MESSAGES 159: 160: Code | Trigger | Resolution 161: -----|--------------------------------|---------------------------- 162: B001 | name/description/check called | Override the method in the 163: | on base class directly | subclass 164: 165: =head3 FORMAL SPECIFICATION 166: 167: Check == { name : String, description : String, 168: check : Context -> [Finding], 169: can_fix : Bool, category : String, order : N } 170: 171: run : Check x Context -> [Finding] 172: run c ctx == check c ctx 173: 174: =head1 AUTHOR 175: 176: Nigel Horne C<< <njh@nigelhorne.com> >> 177: 178: =head1 LICENSE 179: 180: Copyright (C) 2026 Nigel Horne. 181: This library is free software; you can redistribute it and/or modify 182: it under the same terms as Perl itself. 183: 184: =cut