Lib-Pepper
view release on metacpan or search on metacpan
lib/Lib/Pepper/Simple.pm view on Meta::CPAN
# DESIGN DECISION: Never finalize the library
# ========================================
# The Pepper C library has a critical limitation: once pepFinalize() is called,
# pepInitialize() cannot be called again in the same process (returns error -103).
#
# SOLUTION: We never call pepFinalize(), keeping the library loaded in memory.
# This allows creating new instances even after all previous instances are destroyed.
#
# Trade-offs:
# PRO: Can create instances â destroy all â create new instances â
# PRO: No -103 errors, can run unlimited test iterations â
# PRO: Simpler lifecycle management â
# CON: Library stays in memory until process exit (~few MB)
# CON: Cannot reset library state without restarting process
#
# Note: Instance-level resources (connections, handles) are still properly cleaned up.
# Only the library initialization state persists.
return;
}
t/04-multi-terminal.t view on Meta::CPAN
'Auto-counter remains independent');
undef $terminal;
}
# Test rapid create/destroy (stress test for memory leaks)
SKIP: {
skip 'Author-only test: Set PEPPER_LICENSE=/path/to/license.xml to run stress tests', 2 unless $haveLicense;
diag("Library path for stress test: '$libraryPath'");
my $iterations = 10; # Reduced from 100 for faster testing
my $success = 1;
for my $i (1..$iterations) {
my $terminal;
eval {
$terminal = Lib::Pepper::Simple->new(
terminal_type => PEP_TERMINAL_TYPE_GENERIC_ZVT,
terminal_address => '192.168.1.163:20008',
license_xml => $license_xml,
config_xml => $config_xml,
library_path => $libraryPath,
);
};
if($@) {
$success = 0;
diag("Iteration $i failed: $@");
last;
}
undef $terminal;
}
ok($success, "Rapid create/destroy stress test passed ($iterations iterations)");
# Verify library stays initialized after all instances destroyed (never-finalize design)
my $finalStatus = Lib::Pepper::Simple->library_status();
is($finalStatus->{instance_count}, 0, 'All instances cleaned up after stress test');
is($finalStatus->{initialized}, 1, 'Library STAYS initialized (never-finalize design)');
}
done_testing();
t/05-stress-test.t view on Meta::CPAN
diag("STRESS TEST: 100 instances - Create/Destroy cycles");
diag("=" x 70);
diag("");
# Verify initial state (never-finalize design: library may be initialized from previous tests)
my $initialStatus = Lib::Pepper::Simple->library_status();
# With never-finalize design, library stays initialized across tests
ok($initialStatus->{initialized} >= 0, 'Library status checked before stress test');
is($initialStatus->{instance_count}, 0, 'Instance count is 0 before stress test');
# Test 1: Sequential create/destroy (100 iterations)
diag("Test 1: Sequential create/destroy (100 iterations)");
my $startTime = time();
my $iterations = 100;
my $errors = 0;
my $warnings = 0;
# Capture warnings
local $SIG{__WARN__} = sub {
my $warning = shift;
# Ignore expected warnings from destructor
unless($warning =~ /Lib::Pepper finalization failed during DESTROY/) {
diag("WARNING: $warning");
$warnings++;
}
};
for my $i (1..$iterations) {
my $terminal;
eval {
$terminal = Lib::Pepper::Simple->new(
terminal_type => PEP_TERMINAL_TYPE_GENERIC_ZVT,
terminal_address => '192.168.1.163:20008',
license_xml => $license_xml,
config_xml => $config_xml,
library_path => $libraryPath,
);
};
t/05-stress-test.t view on Meta::CPAN
# Verify library state during iteration
if($i == 1) {
my $status = Lib::Pepper::Simple->library_status();
is($status->{initialized}, 1, "Library initialized on first iteration");
is($status->{instance_count}, 1, "Instance count is 1 on first iteration");
}
# Destroy terminal
undef $terminal;
# Every 10 iterations, verify cleanup
if($i % 10 == 0) {
my $status = Lib::Pepper::Simple->library_status();
is($status->{instance_count}, 0, "Instance count is 0 after iteration $i");
is($status->{initialized}, 1, "Library STAYS initialized after iteration $i (never-finalize design)");
diag("Progress: $i/$iterations iterations complete");
}
}
my $elapsed = time() - $startTime;
diag(sprintf("Sequential test completed in %.2f seconds (%.0f ms per iteration)",
$elapsed, ($elapsed / $iterations) * 1000));
is($errors, 0, "No errors during $iterations sequential iterations");
is($warnings, 0, "No unexpected warnings during sequential test");
# Verify final cleanup (never-finalize design: library STAYS initialized)
my $finalStatus = Lib::Pepper::Simple->library_status();
is($finalStatus->{initialized}, 1, 'Library STAYS initialized after sequential stress test (never-finalize design)');
is($finalStatus->{instance_count}, 0, 'Instance count is 0 after sequential stress test');
# Test 2: Multiple simultaneous instances (stress test cleanup)
diag("");
diag("Test 2: Multiple simultaneous instances (10 at a time, 10 cycles)");
t/06-leak-detection.t view on Meta::CPAN
terminal_type => PEP_TERMINAL_TYPE_GENERIC_ZVT,
terminal_address => '192.168.1.163:20008',
license_xml => $license_xml,
config_xml => $config_xml,
library_path => $libraryPath,
);
# Exercise the object
my $status = $terminal->checkStatus();
# Sample memory every 5 iterations
if($i % 5 == 0) {
$ps_output = `ps -o rss= -p $$`;
push @memory_samples, $ps_output + 0;
}
undef $terminal;
}
# Get final memory usage
$ps_output = `ps -o rss= -p $$`;
t/06-leak-detection.t view on Meta::CPAN
}
my $samples_count = scalar(@memory_samples) - 1;
my $growth_percentage = ($growing_trend / $samples_count) * 100;
diag("Samples showing growth: $growing_trend / $samples_count ($growth_percentage%)");
# Memory should not constantly grow (allow some variation due to malloc behavior)
ok($growth_percentage < 80, "Memory not constantly growing: $growth_percentage%");
# Total growth should be reasonable (< 5 MB for 20 iterations)
ok($memory_growth < 5_000, "Total memory growth acceptable (< 5MB): ${memory_growth}KB");
}
# TEST 3: Library State Cleanup Verification
{
diag("--- Test 3: Library State Cleanup Verification ---");
# Initial state
my $initial_status = Lib::Pepper::Simple->library_status();
is($initial_status->{instance_count}, 0, "Initial instance count is 0");
t/06-leak-detection.t view on Meta::CPAN
is($final_status->{instance_count}, 0, "Instance count returns to 0 after cleanup");
is($final_status->{initialized}, 1, "Library STAYS initialized (never-finalize design)");
# Note: instance_ids do NOT reset with never-finalize design
ok(exists $final_status->{instance_ids}, "Instance ID counters exist");
diag("");
}
# TEST 4: Stress Test with Leak Monitoring
{
diag("--- Test 4: Extended Stress Test (50 iterations) ---");
my $errors = 0;
my $leak_detected = 0;
# Never-finalize design allows unlimited create/destroy cycles without keeper pattern
for my $i (1..50) {
my $terminal;
eval {
$terminal = Lib::Pepper::Simple->new(
terminal_type => PEP_TERMINAL_TYPE_GENERIC_ZVT,
t/06-leak-detection.t view on Meta::CPAN
undef $terminal;
# Verify cleanup
$status = Lib::Pepper::Simple->library_status();
if($status->{instance_count} != 0) {
diag("Iteration $i: Cleanup failed, count: $status->{instance_count} (expected 0)");
$leak_detected = 1;
}
if($i % 10 == 0) {
diag("Progress: $i/50 iterations complete");
}
}
my $final_status = Lib::Pepper::Simple->library_status();
is($errors, 0, "No errors during 50 iterations (never-finalize design)");
is($leak_detected, 0, "No reference count leaks detected");
is($final_status->{instance_count}, 0, "Final cleanup successful");
diag("");
}
diag("=" x 70);
diag("MEMORY LEAK DETECTION COMPLETE");
diag("=" x 70);
( run in 1.146 second using v1.01-cache-2.11-cpan-71847e10f99 )