#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use Test::Fatal;
use Test::Identity;
use Test::Refcount;
use Future;
# First done
{
my $f1 = Future->new;
my $f2 = Future->new;
my $future = Future->wait_any( $f1, $f2 );
is_oneref( $future, '$future has refcount 1 initially' );
# Two refs; one lexical here, one in $future
is_refcount( $f1, 2, '$f1 has refcount 2 after adding to ->wait_any' );
is_refcount( $f2, 2, '$f2 has refcount 2 after adding to ->wait_any' );
is_deeply( [ $future->pending_futures ],
[ $f1, $f2 ],
'$future->pending_futures before any ready' );
is_deeply( [ $future->ready_futures ],
[],
'$future->done_futures before any ready' );
my @on_ready_args;
$future->on_ready( sub { @on_ready_args = @_ } );
ok( !$future->is_ready, '$future not yet ready' );
is( scalar @on_ready_args, 0, 'on_ready not yet invoked' );
$f1->done( one => 1 );
is_deeply( [ $future->pending_futures ],
[],
'$future->pending_futures after $f1 ready' );
is_deeply( [ $future->ready_futures ],
[ $f1, $f2 ],
'$future->ready_futures after $f1 ready' );
is_deeply( [ $future->done_futures ],
[ $f1 ],
'$future->done_futures after $f1 ready' );
is_deeply( [ $future->cancelled_futures ],
[ $f2 ],
'$future->cancelled_futures after $f1 ready' );
is( scalar @on_ready_args, 1, 'on_ready passed 1 argument' );
identical( $on_ready_args[0], $future, 'Future passed to on_ready' );
undef @on_ready_args;
ok( $future->is_ready, '$future now ready after f1 ready' );
is_deeply( [ $future->get ], [ one => 1 ], 'results from $future->get' );
is_refcount( $future, 1, '$future has refcount 1 at end of test' );
undef $future;
is_refcount( $f1, 1, '$f1 has refcount 1 at end of test' );
is_refcount( $f2, 1, '$f2 has refcount 1 at end of test' );
}
# First fails
{
my $f1 = Future->new;
my $f2 = Future->new;
my $future = Future->wait_any( $f1, $f2 );
$f1->fail( "It fails\n" );
ok( $future->is_ready, '$future now ready after a failure' );
is( $future->failure, "It fails\n", '$future->failure yields exception' );
is( exception { $future->get }, "It fails\n", '$future->get throws exception' );
ok( $f2->is_cancelled, '$f2 cancelled after a failure' );
}
# immediately done
{
my $f1 = Future->done;
my $future = Future->wait_any( $f1 );
ok( $future->is_ready, '$future of already-ready sub already ready' );
}
# cancel propagation
{
my $f1 = Future->new;
my $c1;
$f1->on_cancel( sub { $c1++ } );
my $future = Future->wait_all( $f1 );
$future->cancel;
is( $c1, 1, '$future->cancel marks subs cancelled' );
}
# cancelled dependents
{
my $f1 = Future->new;
my $f2 = Future->new;
my $future = Future->wait_any( $f1, $f2 );
$f1->cancel;
ok( !$future->is_ready, '$future not yet ready after first cancellation' );
$f2->done( "result" );
ok( $future->is_ready, '$future is ready' );
is_deeply( [ $future->done_futures ],
[ $f2 ],
'->done_futures with cancellation' );
is_deeply( [ $future->cancelled_futures ],
[ $f1 ],
'->cancelled_futures with cancellation' );
my $f3 = Future->new;
$future = Future->wait_any( $f3 );
$f3->cancel;
ok( $future->is_ready, '$future is ready after final cancellation' );
like( scalar $future->failure, qr/ cancelled/, 'Failure mentions cancelled' );
}
# wait_any on none
{
my $f = Future->wait_any( () );
ok( $f->is_ready, 'wait_any on no Futures already done' );
is( scalar $f->failure, "Cannot ->wait_any with no subfutures",
'->get on empty wait_any is empty' );
}
done_testing;