The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;
use Test::Builder;
use Test::More;
use Async::Selector;
use Test::Memory::Cycle;


note('--- test for memory leak');

my $destroyed_selectors = 0;
my $destroyed_watchers = 0;

sub resetCount {
    $destroyed_selectors = $destroyed_watchers = 0;
}

sub checkCount {
    my ($exp_selectors, $exp_watchers) = @_;
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    is($destroyed_selectors, $exp_selectors, "destroyed selectors: $exp_selectors");
    is($destroyed_watchers, $exp_watchers, "destroyed watchers: $exp_watchers");
}

package Async::Selector;

sub DESTROY {
    $destroyed_selectors++;
}

package Async::Selector::Watcher;

sub DESTROY {
    $destroyed_watchers++;
}

package main;

{
    resetCount();
    my $s = Async::Selector->new();
    checkCount(0,0);
}
checkCount(1,0);


{
    resetCount();
    my $s = Async::Selector->new();
    my $w = $s->watch(a => 1, sub {});
    checkCount(0,0);
    memory_cycle_ok($s, "no cyclic ref in selector");
    $w->cancel();
    checkCount(0,0);
}
checkCount(1,1);

{
    resetCount();
    my $s = Async::Selector->new();
    $s->watch(a => 1, sub {});
    memory_cycle_ok($s, "no cyclic ref in selector");
    checkCount(0,0);
}
checkCount(1,1);

{
    resetCount();
    my $w;
    {
        my $s = Async::Selector->new();
        $w = $s->watch(a => 1, sub {});
        checkCount(0,0);
        ok($w->active, "w is active");
        memory_cycle_ok($s, "no cyclic ref in selector");
    }
    checkCount(1,0);
    ok(!$w->active, "w is inactive because selector is destroyed");
}
checkCount(1,1);

{
    resetCount();
    my $w;
    {
        my $s = Async::Selector->new();
        $w = $s->watch(a => 1, sub {});
        my $x = $s->watch(a => 1, sub {});
        checkCount(0,0);
        memory_cycle_ok($s, "no cyclic ref in selector");
    }
    checkCount(1,1);
    ok(!$w->active, "w is inactive because selector is destroyed");
}
checkCount(1,2);

{
    resetCount();
    my $s = Async::Selector->new();
    my $w;
    $w = $s->watch(a => 1, sub {
        undef $w;
    });
    memory_cycle_exists($w, "cyclic ref because of the closure");
    $w->cancel();
    memory_cycle_ok($w, "there is no cyclic ref because cancel() releases the closure");
}
checkCount(1,1);

done_testing();