Data-CountMinSketch-Shared

 view release on metacpan or  search on metacpan

t/03-frozen.t  view on Meta::CPAN

    is $st->{readonly}, 1, 'stats.readonly set';
    ok $st->{width} >= 2, 'stats geometry present read-only';

    ok $ro->width >= 2, 'width readable read-only';
    cmp_ok $ro->depth, '>=', 1, 'depth readable read-only';
    cmp_ok $ro->cells, '>=', $ro->width, 'cells readable read-only';
    is $ro->path, $path, 'path readable read-only';
    is $ro->memfd, -1, 'memfd readable read-only (file-backed => -1)';
    eval { $ro->sync };
    ok !$@, 'sync on read-only view is a silent no-op';

    like exception(sub { $ro->add("x") }),        qr/read-only/, 'add on read-only view croaks';
    like exception(sub { $ro->add_many(["x"]) }), qr/read-only/, 'add_many on read-only view croaks';
    like exception(sub { $ro->clear }),           qr/read-only/, 'clear on read-only view croaks';
    like exception(sub { $ro->freeze }),          qr/read-only/, 'freeze on read-only view croaks';
}

# ---- two independent read-only views of the same file, concurrently ----
{
    my $a = Data::CountMinSketch::Shared->new_readonly($path);
    my $b = Data::CountMinSketch::Shared->new_readonly($path);
    ok $a->estimate("apple") && $b->estimate("apple"), 'two read-only views query the same file';
}

# ---- refuse a read-write reopen of a sealed file ----
like exception(sub { Data::CountMinSketch::Shared->new($path, 0.01, 0.01) }),
     qr/frozen|read-only/, 'read-write reopen of a sealed file is refused';

# ---- new_readonly rejects a non-frozen file ----
{
    my $u = "$dir/unsealed.cms";
    { my $cms = Data::CountMinSketch::Shared->new($u, 0.01, 0.01); $cms->add("q"); }
    like exception(sub { Data::CountMinSketch::Shared->new_readonly($u) }),
         qr/not frozen/, 'new_readonly on an unsealed file croaks';
}

# ---- new_readonly error paths ----
like exception(sub { Data::CountMinSketch::Shared->new_readonly("$dir/does-not-exist.cms") }),
     qr/open|No such/, 'new_readonly on a missing path croaks';
like exception(sub { Data::CountMinSketch::Shared->new_readonly(undef) }),
     qr/required/, 'new_readonly requires a path';

# ---- merge FROM a frozen (read-only) other into a mutable sketch (lock-free o read) ----
{
    my $m   = "$dir/other.cms";
    my $B   = Data::CountMinSketch::Shared->new($m, 0.01, 0.01);
    $B->add($_) for qw(xx yy zz);
    $B->freeze;
    my $Bro = Data::CountMinSketch::Shared->new_readonly($m);

    my $A = Data::CountMinSketch::Shared->new(undef, 0.01, 0.01);   # same geometry
    $A->add("local");
    $A->merge($Bro);   # must read Bro's cells without locking its PROT_READ map
    cmp_ok $A->estimate("local"), '>=', 1, 'merge target keeps its own items';
    cmp_ok $A->estimate($_), '>=', 1, "merged-in '$_' present after merge from frozen other" for qw(xx yy zz);
    is $A->total, 1 + 3, 'merge target total includes its own add plus the frozen other';
}

done_testing;

# minimal exception helper (avoid a Test::Fatal dependency)
sub exception {
    my $code = shift;
    my $err;
    { local $@; eval { $code->(); 1 } or $err = $@; }
    return $err;
}



( run in 1.777 second using v1.01-cache-2.11-cpan-54e63673c56 )