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 )