summaryrefslogtreecommitdiff
path: root/t/13needs_any.t
diff options
context:
space:
mode:
Diffstat (limited to 't/13needs_any.t')
-rw-r--r--t/13needs_any.t200
1 files changed, 200 insertions, 0 deletions
diff --git a/t/13needs_any.t b/t/13needs_any.t
new file mode 100644
index 0000000..b94a762
--- /dev/null
+++ b/t/13needs_any.t
@@ -0,0 +1,200 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Fatal;
+use Test::Refcount;
+
+use Future;
+
+# One done
+{
+ my $f1 = Future->new;
+ my $f2 = Future->new;
+ my $c2;
+ $f2->on_cancel( sub { $c2++ } );
+
+ my $future = Future->needs_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 ->needs_any' );
+ is_refcount( $f2, 2, '$f2 has refcount 2 after adding to ->needs_any' );
+
+ my $ready;
+ $future->on_ready( sub { $ready++ } );
+
+ ok( !$future->is_ready, '$future not yet ready' );
+
+ $f1->done( one => 1 );
+
+ is( $ready, 1, '$future is now ready' );
+
+ ok( $future->is_ready, '$future now ready after f1 ready' );
+ is_deeply( [ $future->get ], [ one => 1 ], 'results from $future->get' );
+
+ is_deeply( [ $future->pending_futures ],
+ [],
+ '$future->pending_futures after $f1 done' );
+
+ is_deeply( [ $future->ready_futures ],
+ [ $f1, $f2 ],
+ '$future->ready_futures after $f1 done' );
+
+ is_deeply( [ $future->done_futures ],
+ [ $f1 ],
+ '$future->done_futures after $f1 done' );
+
+ is_deeply( [ $future->failed_futures ],
+ [],
+ '$future->failed_futures after $f1 done' );
+
+ is_deeply( [ $future->cancelled_futures ],
+ [ $f2 ],
+ '$future->cancelled_futures after $f1 done' );
+
+ 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' );
+
+ is( $c2, 1, 'Unfinished child future cancelled on failure' );
+}
+
+# One fails
+{
+ my $f1 = Future->new;
+ my $f2 = Future->new;
+
+ my $future = Future->needs_any( $f1, $f2 );
+
+ my $ready;
+ $future->on_ready( sub { $ready++ } );
+
+ ok( !$future->is_ready, '$future not yet ready' );
+
+ $f1->fail( "Partly fails" );
+
+ ok( !$future->is_ready, '$future not yet ready after $f1 fails' );
+
+ $f2->done( two => 2 );
+
+ ok( $future->is_ready, '$future now ready after $f2 done' );
+ is_deeply( [ $future->get ], [ two => 2 ], '$future->get after $f2 done' );
+
+ is_deeply( [ $future->done_futures ],
+ [ $f2 ],
+ '$future->done_futures after $f2 done' );
+
+ is_deeply( [ $future->failed_futures ],
+ [ $f1 ],
+ '$future->failed_futures after $f2 done' );
+}
+
+# All fail
+{
+ my $f1 = Future->new;
+ my $f2 = Future->new;
+
+ my $future = Future->needs_any( $f1, $f2 );
+
+ my $ready;
+ $future->on_ready( sub { $ready++ } );
+
+ ok( !$future->is_ready, '$future not yet ready' );
+
+ $f1->fail( "Partly fails" );
+
+ $f2->fail( "It fails" );
+
+ is( $ready, 1, '$future is now ready' );
+
+ ok( $future->is_ready, '$future now ready after f2 fails' );
+ is( $future->failure, "It fails", '$future->failure yields exception' );
+ my $file = __FILE__;
+ my $line = __LINE__ + 1;
+ like( exception { $future->get }, qr/^It fails at \Q$file line $line\E\.?\n$/, '$future->get throws exception' );
+
+ is_deeply( [ $future->failed_futures ],
+ [ $f1, $f2 ],
+ '$future->failed_futures after all fail' );
+}
+
+# immediately done
+{
+ my $future = Future->needs_any( Future->fail("F1"), Future->done );
+
+ ok( $future->is_ready, '$future of already-done sub already ready' );
+}
+
+# immediately fails
+{
+ my $future = Future->needs_any( Future->fail("F1") );
+
+ ok( $future->is_ready, '$future of already-failed sub already ready' );
+ $future->failure;
+}
+
+# cancel propagation
+{
+ my $f1 = Future->new;
+ my $c1;
+ $f1->on_cancel( sub { $c1++ } );
+
+ my $f2 = Future->new;
+ my $c2;
+ $f2->on_cancel( sub { $c2++ } );
+
+ my $future = Future->needs_all( $f1, $f2 );
+
+ $f2->fail( "booo" );
+
+ $future->cancel;
+
+ is( $c1, 1, '$future->cancel marks subs cancelled' );
+ is( $c2, undef, '$future->cancel ignores ready subs' );
+}
+
+# cancelled convergent
+{
+ my $f1 = Future->new;
+ my $f2 = Future->new;
+
+ my $future = Future->needs_any( $f1, $f2 );
+
+ $f1->cancel;
+
+ ok( !$future->is_ready, '$future not yet ready after first cancellation' );
+
+ $f2->done( "result" );
+
+ 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->needs_any( $f3 );
+
+ $f3->cancel;
+
+ ok( $future->is_ready, '$future is ready after final cancellation' );
+
+ like( scalar $future->failure, qr/ cancelled/, 'Failure mentions cancelled' );
+}
+
+# needs_any on none
+{
+ my $f = Future->needs_any( () );
+
+ ok( $f->is_ready, 'needs_any on no Futures already done' );
+ is( scalar $f->failure, "Cannot ->needs_any with no subfutures",
+ '->get on empty needs_any is empty' );
+}
+
+done_testing;