File-Raw-Archive

 view release on metacpan or  search on metacpan

t/26-stashed-entries.t  view on Meta::CPAN

#!perl
# Refcount lifecycle: an Entry holds a back-ref to its Reader; if the
# user stashes an Entry past Reader scope, the C handle should stay
# alive for accessor calls (which only look at the cached meta AV)
# but slurp on a Reader that the user explicitly closed should fail
# cleanly rather than crash.
use 5.010;
use strict;
use warnings;
use Test::More;
use File::Temp qw(tempdir);
use File::Raw::Archive;

my $dir = tempdir(CLEANUP => 1);
my $tar = "$dir/stash.tar";

my $w = File::Raw::Archive->create($tar);
$w->add(name => 'one.txt', content => 'first content');
$w->add(name => 'two.txt', content => 'second content');
$w->close;

# Case 1: stash an entry past Reader scope. The Reader stays alive
# because the entry holds a ref to it. Accessors keep working;
# slurp also works because the C handle is still allocated.
{
    my $stashed;
    {
        my $r = File::Raw::Archive->open($tar);
        $stashed = $r->next;       # entry holds ref to $r
        $stashed->slurp;            # cache the bytes inside the entry
        # $r goes out of scope here. Refcount drops by 1, but
        # $stashed's back-link keeps the C handle alive.
    }
    is($stashed->name, 'one.txt',
        'stashed entry: name accessor still works after Reader out of scope');
    is($stashed->size, length('first content'),
        'stashed entry: size accessor still works');
    is($stashed->slurp, 'first content',
        'stashed slurp returns memoised bytes');
    # Let $stashed go out of scope: chain unwinds, C handle freed.
}
ok(1, 'stashed-past-scope teardown completed without panic');

# Case 2: explicit close() on the Reader. The handle's C struct is
# freed immediately. A stashed entry's accessors still work (they
# read cached meta) but slurp() should croak gracefully if the bytes
# weren't memoised.
{
    my $r = File::Raw::Archive->open($tar);
    my $e = $r->next;
    is($e->name, 'one.txt', 'pre-close: accessor works');
    $r->close;
    is($e->name, 'one.txt', 'post-close: cached metadata still accessible');

    # slurp without prior memoisation should croak rather than segfault.
    my $err;
    eval { $e->slurp; 1 } or $err = $@;
    ok(defined $err, 'post-close slurp croaks (or returns empty)')
        or diag "got: " . ($e->slurp // 'undef');
}

# Case 3: Reader DESTROY runs cleanly when implicitly torn down with
# stashed entries kept around in an outer scope.
{
    my @stashed;
    my $r = File::Raw::Archive->open($tar);
    while (my $e = $r->next) {
        $e->slurp;       # memoise so post-close accessors work
        push @stashed, $e;
    }
    undef $r;            # drop direct reference
    # Entries still hold the back-link, so the handle is alive.
    is($stashed[0]->name, 'one.txt', 'first entry name post-undef');
    is($stashed[1]->slurp, 'second content', 'second entry slurp memoised');
    # Let @stashed go out of scope: full chain teardown.
}
ok(1, 'implicit Reader teardown via stashed-entries chain');

done_testing;



( run in 2.941 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )