Class-Abstract
view release on metacpan or search on metacpan
t/extended_tests.t view on Meta::CPAN
'new("") croaks: defined empty string fails the length guard';
# Test::Returns: the call croaks, so there is no return value to check.
# Verify the error message is exactly right by re-testing with explicit match.
throws_ok { Class::Abstract::new('') }
qr/new\(\) requires a defined class name as invocant/,
'new("") error message matches documented text exactly';
};
# Purpose: check_abstract("") must croak -- same guard, same condition
subtest 'check_abstract("") -- defined-but-empty class name croaks' => sub {
plan tests => 2;
throws_ok { Class::Abstract::check_abstract('') }
$config{err_chk_undef},
'check_abstract("") croaks: defined empty string fails the length guard';
throws_ok { Class::Abstract::check_abstract('') }
qr/check_abstract\(\) requires a defined class name/,
'check_abstract("") error message matches documented text exactly';
};
# ===========================================================================
# SECTION 3: Production scenario
#
# The 'l&&!r' condition for the bypass guard in new() and check_abstract() is:
# $config{harness_bypass} is truthy (default=1)
# $ENV{HARNESS_ACTIVE} is NOT set (empty string or undef)
#
# This is the real-world production scenario: code runs outside a test harness
# with all defaults. Enforcement must fire because
# $BYPASS=0 AND ($harness_bypass=1 AND $HARNESS_ACTIVE='') = 0 || 0 = false.
# ===========================================================================
# Purpose: enforcement fires in production context (no harness, no bypass)
subtest 'production scenario -- enforcement fires with defaults outside harness' => sub {
plan tests => 4;
diag 'Testing production context: BYPASS=0, harness_bypass=1, HARNESS_ACTIVE=""'
if $ENV{TEST_VERBOSE};
# new(): BYPASS=0, harness_bypass=1 (default), HARNESS_ACTIVE="" (not in harness)
{
local $Class::Abstract::BYPASS = 0;
local $Class::Abstract::config{harness_bypass} = $config{harness_bypass_on};
local $ENV{HARNESS_ACTIVE} = $config{harness_active_off};
throws_ok { ET::Abstract->new() }
$config{err_abstract},
'new() croaks in production: BYPASS=0, harness_bypass=1, HARNESS_ACTIVE=""';
}
# new(): verify concrete class still works in the same context
{
local $Class::Abstract::BYPASS = 0;
local $Class::Abstract::config{harness_bypass} = $config{harness_bypass_on};
local $ENV{HARNESS_ACTIVE} = $config{harness_active_off};
my $obj;
lives_ok { $obj = ET::Concrete->new() }
'new(concrete) lives in production context (only abstract class is blocked)';
ok blessed($obj), 'concrete object is blessed in production context';
}
# check_abstract(): same production context
{
local $Class::Abstract::BYPASS = 0;
local $Class::Abstract::config{harness_bypass} = $config{harness_bypass_on};
local $ENV{HARNESS_ACTIVE} = $config{harness_active_off};
throws_ok { Class::Abstract::check_abstract($config{pkg_abstract}) }
$config{err_abstract},
'check_abstract() croaks in production: same default context';
}
};
# ===========================================================================
# SECTION 4: new() with blessed abstract-class instance as invocant
#
# When a blessed object of an abstract class is passed as invocant, new()
# extracts the class name via blessed() then checks if that class is abstract.
# With enforcement on, the croak must fire because the extracted class IS abstract.
#
# This tests the LCSAJ path:
# if(ref): T -> unless(blessed): F -> class=ref(obj) -> bypass: off -> abstract: T -> CROAK
# ===========================================================================
# Purpose: blessed instance of abstract class passed to new() -- croak after class extraction
subtest 'new() -- blessed abstract-class instance causes enforcement after class extraction' => sub {
plan tests => 3;
# Create a blessed abstract instance (bypass must be on to create it)
my $abstract_obj;
{
local $Class::Abstract::BYPASS = 1;
$abstract_obj = ET::Abstract->new();
}
diag 'abstract obj class: ' . (blessed($abstract_obj) // 'undef') if $ENV{TEST_VERBOSE};
# Precondition: the object exists and is blessed into the abstract class
ok blessed($abstract_obj), 'precondition: abstract object created with bypass';
is ref($abstract_obj), $config{pkg_abstract},
'precondition: object is blessed into ET::Abstract';
# With enforcement on, new($abstract_obj) must croak:
# ref branch -> class = 'ET::Abstract' -> abstract check -> croak
enforcement_on {
throws_ok { Class::Abstract::new($abstract_obj) }
qr/Cannot instantiate abstract class ET::Abstract directly/,
'new(blessed abstract obj): croak after class extraction from blessed ref';
};
};
# ===========================================================================
# SECTION 5: check_abstract() with concrete class and blessed object invocant
#
# check_abstract() must silently return undef when the class is concrete.
# Also tests the path where a blessed concrete OBJECT is passed as invocant
# (ref branch: truthy -> blessed: truthy -> class extracted from ref).
# ===========================================================================
( run in 0.937 second using v1.01-cache-2.11-cpan-800906f7e73 )