Lib-Pepper

 view release on metacpan or  search on metacpan

t/06-leak-detection.t  view on Meta::CPAN

    # Create and destroy 10 terminals
    # Note: Never-finalize design allows unlimited create/destroy cycles
    for my $i (1..10) {
        my $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,
        );

        # Do a simple status check to exercise the object
        my $status = $terminal->checkStatus();

        # Destroy terminal
        undef $terminal;
    }

    my $final_count = Devel::Leak::CheckSV($handle);
    my $leaked_svs = $final_count - $initial_count;

    diag("Final SV count:   $final_count");
    diag("Leaked SVs:       $leaked_svs");
    diag("");

    # Allow some tolerance for Perl internals (caches, etc.)
    # Typical leak-free code might show 0-50 SVs depending on Perl version
    ok($leaked_svs < 100, "Perl SV leaks acceptable (< 100): $leaked_svs");

    if($leaked_svs > 0) {
        diag("Note: Small SV count increases can be normal due to Perl's internal caching");
    }
}

# TEST 2: Process Memory Growth Detection
SKIP: {
    skip 'Devel::Size not available', 3 unless $have_devel_size;

    diag("--- Test 2: Process Memory Growth Detection ---");

    # Get initial memory usage
    my $ps_output = `ps -o rss= -p $$`;
    my $initial_memory = $ps_output + 0;  # Convert to number
    diag("Initial memory:   $initial_memory KB");

    # Create and destroy terminals with memory sampling
    # Note: Never-finalize design allows unlimited create/destroy cycles
    my @memory_samples;
    for my $i (1..20) {
        my $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,
        );

        # 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 $$`;
    my $final_memory = $ps_output + 0;
    my $memory_growth = $final_memory - $initial_memory;

    diag("Final memory:     $final_memory KB");
    diag("Memory growth:    $memory_growth KB");
    diag("Memory samples:   " . join(", ", @memory_samples) . " KB");
    diag("");

    # Check if memory is stable (not constantly growing)
    my $growing_trend = 0;
    for my $i (1..$#memory_samples) {
        $growing_trend++ if $memory_samples[$i] > $memory_samples[$i-1];
    }

    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");

    # Create multiple terminals
    my @terminals;
    for my $i (1..5) {
        push @terminals, 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,
        );
    }

    my $mid_status = Lib::Pepper::Simple->library_status();
    is($mid_status->{instance_count}, 5, "Instance count is 5 after creating 5 terminals");

    # Destroy all terminals
    @terminals = ();

    my $final_status = Lib::Pepper::Simple->library_status();
    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,
                terminal_address => '192.168.1.163:20008',
                license_xml      => $license_xml,
                config_xml       => $config_xml,
            library_path     => $libraryPath,
            );
        };

        if($@) {
            diag("Iteration $i failed: $@");
            $errors++;
            last;
        }

        # Check instance count
        my $status = Lib::Pepper::Simple->library_status();
        if($status->{instance_count} != 1) {
            diag("Iteration $i: Wrong instance count: $status->{instance_count} (expected 1)");
            $leak_detected = 1;
        }

        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);

done_testing();



( run in 0.585 second using v1.01-cache-2.11-cpan-71847e10f99 )