1#!/usr/bin/perl 2 3use v5.10; 4use strict; 5use warnings; 6 7use Test::More; 8use Test::Fatal; 9use Test::Refcount; 10 11use Future; 12 13# All done 14{ 15 my $f1 = Future->new; 16 my $f2 = Future->new; 17 18 my $future = Future->needs_all( $f1, $f2 ); 19 is_oneref( $future, '$future has refcount 1 initially' ); 20 21 # Two refs; one lexical here, one in $future 22 is_refcount( $f1, 2, '$f1 has refcount 2 after adding to ->needs_all' ); 23 is_refcount( $f2, 2, '$f2 has refcount 2 after adding to ->needs_all' ); 24 25 my $ready; 26 $future->on_ready( sub { $ready++ } ); 27 28 ok( !$future->is_ready, '$future not yet ready' ); 29 30 $f1->done( one => 1 ); 31 $f2->done( two => 2 ); 32 33 is( $ready, 1, '$future is now ready' ); 34 35 ok( $future->is_ready, '$future now ready after f2 ready' ); 36 is_deeply( [ $future->result ], [ one => 1, two => 2 ], '$future->result after f2 ready' ); 37 38 is_refcount( $future, 1, '$future has refcount 1 at end of test' ); 39 undef $future; 40 41 is_refcount( $f1, 1, '$f1 has refcount 1 at end of test' ); 42 is_refcount( $f2, 1, '$f2 has refcount 1 at end of test' ); 43} 44 45# One fails 46{ 47 my $f1 = Future->new; 48 my $f2 = Future->new; 49 my $c2; 50 $f2->on_cancel( sub { $c2++ } ); 51 52 my $future = Future->needs_all( $f1, $f2 ); 53 54 my $ready; 55 $future->on_ready( sub { $ready++ } ); 56 57 ok( !$future->is_ready, '$future not yet ready' ); 58 59 $f1->fail( "It fails" ); 60 61 is( $ready, 1, '$future is now ready' ); 62 63 ok( $future->is_ready, '$future now ready after f1 fails' ); 64 is( $future->failure, "It fails", '$future->failure yields exception' ); 65 my $file = __FILE__; 66 my $line = __LINE__ + 1; 67 like( exception { $future->result }, qr/^It fails at \Q$file line $line\E\.?\n$/, '$future->result throws exception' ); 68 69 is( $c2, 1, 'Unfinished child future cancelled on failure' ); 70 71 is_deeply( [ $future->pending_futures ], 72 [], 73 '$future->pending_futures after $f1 failure' ); 74 75 is_deeply( [ $future->ready_futures ], 76 [ $f1, $f2 ], 77 '$future->ready_futures after $f1 failure' ); 78 79 is_deeply( [ $future->done_futures ], 80 [], 81 '$future->done_futures after $f1 failure' ); 82 83 is_deeply( [ $future->failed_futures ], 84 [ $f1 ], 85 '$future->failed_futures after $f1 failure' ); 86 87 is_deeply( [ $future->cancelled_futures ], 88 [ $f2 ], 89 '$future->cancelled_futures after $f1 failure' ); 90} 91 92# immediately done 93{ 94 my $future = Future->needs_all( Future->done ); 95 96 ok( $future->is_ready, '$future of already-done sub already ready' ); 97} 98 99# immediately fails 100{ 101 my $future = Future->needs_all( Future->fail("F1"), Future->done ); 102 103 ok( $future->is_ready, '$future of already-failed sub already ready' ); 104} 105 106# cancel propagation 107{ 108 my $f1 = Future->new; 109 my $c1; 110 $f1->on_cancel( sub { $c1++ } ); 111 112 my $f2 = Future->new; 113 my $c2; 114 $f2->on_cancel( sub { $c2++ } ); 115 116 my $future = Future->needs_all( $f1, $f2 ); 117 118 $f2->done; 119 120 $future->cancel; 121 122 is( $c1, 1, '$future->cancel marks subs cancelled' ); 123 is( $c2, undef, '$future->cancel ignores ready subs' ); 124} 125 126# cancelled convergent 127{ 128 my $f1 = Future->new; 129 my $f2 = Future->new; 130 131 my $future = Future->needs_all( $f1, $f2 ); 132 133 $f1->cancel; 134 135 ok( $future->is_ready, '$future of cancelled sub is ready after first cancellation' ); 136 137 like( scalar $future->failure, qr/ cancelled/, 'Failure mentions cancelled' ); 138} 139 140# needs_all on none 141{ 142 my $f = Future->needs_all( () ); 143 144 ok( $f->is_ready, 'needs_all on no Futures already done' ); 145 is_deeply( [ $f->result ], [], '->result on empty needs_all is empty' ); 146} 147 148# weakself retention (RT120468) 149{ 150 my $f = Future->new; 151 152 my $wait; 153 $wait = Future->needs_all( 154 $f, 155 my $cancelled = Future->new->on_cancel( sub { 156 undef $wait; 157 }), 158 ); 159 160 is( exception { $f->fail("oopsie\n") }, undef, 161 'no problems cancelling a Future which clears the original ->needs_all ref' ); 162 163 ok( $cancelled->is_cancelled, 'cancellation occured as expected' ); 164 ok( $f->is_failed, '->needs_all is marked as done' ); 165} 166 167done_testing; 168