Skip Menu |

This queue is for tickets about the future CPAN distribution.

Report information
The Basics
Id: 84313
Status: resolved
Priority: 0/
Queue: future

People
Owner: Nobody in particular
Requestors: TOSHIOITO [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 0.11
Fixed in: 0.12



Subject: on_done() and on_fail() do not return the invocant in some cases
I tested the behavior of on_done() and on_fail() methods with the attached test script, and found out they do not return the invocant Future object in the following cases. - on_done() is called on already failed Future - on_done() is called on already cancelled Future - on_fail() is called on already done (succeeded) Future - on_fail() is called on already cancelled Future on_done() and on_fail() should always return the invocant. By the way, the last test in the attached test script fails because of the bug I reported in https://rt.cpan.org/Ticket/Display.html?id=84312
Subject: test_return_invocant.t
use strict; use warnings; use Future 0.11; use Test::More; use Test::Builder; sub create_callback { sub {} } sub create_future { Future->new } sub test_invocant { my ($f, $method, $arg, $label) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; my $die_test_message = "$method($arg) should not throw exception: $label"; eval { is($f->$method($arg), $f, "$method($arg) should return the invocant: $label"); pass($die_test_message); }; if($@) { fail($die_test_message); diag("Exception: $@"); } } foreach my $method (qw(on_done on_fail on_ready)) { foreach my $arg_creator (\&create_callback, \&create_future) { test_invocant(Future->new, $method, $arg_creator->(), "not ready"); test_invocant(Future->new->done, $method, $arg_creator->(), "done"); test_invocant(Future->new->fail("failure"), $method, $arg_creator->(), "failed"); test_invocant(Future->new->cancel(), $method, $arg_creator->(), "cancelled"); } } done_testing();
Oops; yes. A missing "return $self". Find attached a patch. -- Paul Evans
Subject: rt84313.patch
=== modified file 'lib/Future.pm' --- lib/Future.pm 2013-03-26 21:46:49 +0000 +++ lib/Future.pm 2013-03-31 02:06:04 +0000 @@ -642,7 +642,7 @@ my ( $code ) = @_; if( $self->is_ready ) { - return if $self->failure or $self->is_cancelled; + return $self if $self->failure or $self->is_cancelled; my $is_future = eval { $code->isa( "Future" ) }; $is_future ? $code->done( $self->get ) @@ -720,7 +720,7 @@ my ( $code ) = @_; if( $self->is_ready ) { - return if not $self->failure; + return $self if not $self->failure; my $is_future = eval { $code->isa( "Future" ) }; $is_future ? $code->fail( $self->failure ) === modified file 't/01future.t' --- t/01future.t 2013-03-14 17:10:13 +0000 +++ t/01future.t 2013-03-31 02:06:04 +0000 @@ -82,9 +82,12 @@ $future->done( already => "done" ); my @on_done_args; - $future->on_done( sub { @on_done_args = @_; } ); + identical( $future->on_done( sub { @on_done_args = @_; } ), $future, '->on_done returns future for immediate' ); + my $on_fail; + identical( $future->on_fail( sub { $on_fail++; } ), $future, '->on_fail returns future for immediate' ); - is_deeply( \@on_done_args, [ already => "done" ], 'Results passed to on_done for already-done future' ); + is_deeply( \@on_done_args, [ already => "done" ], 'Results passed to on_done for immediate future' ); + ok( !$on_fail, 'on_fail not invoked for immediate future' ); my $f1 = Future->new; my $f2 = Future->new; @@ -169,10 +172,13 @@ my $future = Future->new; $future->fail( "Already broken" ); + my $on_done; + identical( $future->on_done( sub { $on_done++; } ), $future, '->on_done returns future for immediate' ); my $failure; - $future->on_fail( sub { ( $failure ) = @_; } ); + identical( $future->on_fail( sub { ( $failure ) = @_; } ), $future, '->on_fail returns future for immediate' ); is( $failure, "Already broken", 'Exception passed to on_fail for already-failed future' ); + ok( !$on_done, 'on_done not invoked for immediately-failed future' ); my $f1 = Future->new; my $f2 = Future->new;
Thank you for fixing!