1#!/usr/bin/perl
2
3use v5.10;
4use strict;
5use warnings;
6
7use Test::More;
8use Test::Identity;
9
10use Future;
11use Future::Utils qw( fmap_void );
12
13# fmap_void from ARRAY, no concurrency
14{
15   my @subf;
16   my $future = fmap_void {
17      is( $_, $_[0], 'item passed in $_ as well as @_' );
18      return $subf[$_[0]] = Future->new
19   } foreach => [ 0 .. 2 ];
20
21   ok( defined $future, '$future defined for fmap non-concurrent' );
22
23   ok(  defined $subf[0], '$subf[0] defined' );
24   ok( !defined $subf[1], '$subf[1] not yet defined' );
25
26   $subf[0]->done;
27
28   ok( defined $subf[1], '$subf[1] defined after $subf[0] done' );
29
30   $subf[1]->done;
31
32   $subf[2]->done;
33
34   ok( $future->is_ready, '$future now ready after subs done' );
35   is_deeply( [ $future->result ], [], '$future->result empty for fmap_void' );
36}
37
38# fmap_void from CODE
39{
40   my @subf;
41   my $future = fmap_void {
42      return $subf[$_[0]] = Future->new
43   } generate => do { my $count = 0;
44                      sub { return unless $count < 3; $count++ } };
45
46   ok( defined $future, '$future defined for fmap non-concurrent from CODE' );
47
48   ok( defined $subf[0], '$subf[0] defined' );
49
50   $subf[0]->done;
51   $subf[1]->done;
52   $subf[2]->done;
53
54   ok( $future->is_ready, '$future now ready after subs done from CODE' );
55}
56
57# fmap_void concurrent
58{
59   my @subf;
60   my $future = fmap_void {
61      return $subf[$_[0]] = Future->new
62   } foreach => [ 0 .. 4 ],
63     concurrent => 2;
64
65   ok( defined $future, '$future defined for fmap concurrent=2' );
66
67   ok( defined $subf[0], '$subf[0] defined' );
68   ok( defined $subf[1], '$subf[1] defined' );
69
70   $subf[0]->done; $subf[1]->done;
71
72   ok( defined $subf[2], '$subf[2] defined' );
73   ok( defined $subf[3], '$subf[3] defined' );
74
75   $subf[2]->done; $subf[3]->done;
76
77   ok( defined $subf[4], '$subf[4] deifned' );
78   ok( !$future->is_ready, '$future not yet ready while one sub remains' );
79
80   $subf[4]->done;
81
82   ok( $future->is_ready, '$future now ready after concurrent subs done' );
83}
84
85# fmap_void late-addition concurrently
86{
87   my @items = ( 1, 2, 3 );
88   my @subf;
89   my $future = fmap_void {
90      my $val = shift;
91      my $f = $subf[$val] = Future->new;
92      $f->on_done( sub { push @items, 4, 5, 6 } ) if $val == 3;
93      $f
94   } foreach => \@items,
95     concurrent => 4;
96
97   ok( defined $future, '$future defined for fmap concurrent=3 late-add' );
98
99   ok( $subf[1] && $subf[2] && $subf[3], '3 subfutures initally ready' );
100
101   $subf[1]->done;
102   $subf[2]->done;
103
104   ok( !$subf[4], 'No $subf[4] before $subf[3] done' );
105
106   $subf[3]->done;
107
108   ok( $subf[4] && $subf[5] && $subf[6], '3 new subfutures now ready' );
109
110   $subf[4]->done;
111   $subf[5]->done;
112   $subf[6]->done;
113
114   ok( $future->is_ready, '$future now ready after all 6 subfutures done' );
115}
116
117# fmap_void on immediates
118{
119   my $future = fmap_void {
120      return Future->done
121   } foreach => [ 0 .. 2 ];
122
123   ok( $future->is_ready, '$future already ready for fmap on immediates' );
124}
125
126# fmap_void on non/immediate mix
127{
128   my @item_f = ( my $item = Future->new, Future->done, Future->done );
129   my $future = fmap_void {
130      return $_[0];
131   } foreach => \@item_f,
132     concurrent => 2;
133
134   ok( !$future->is_ready, '$future not yet ready before non-immediate done' );
135
136   $item->done;
137   ok( $future->is_ready, '$future now ready after non-immediate done' );
138}
139
140# fmap_void fail
141{
142   my @subf;
143   my $future = fmap_void {
144      return $subf[$_[0]] = Future->new;
145   } foreach => [ 0, 1, 2 ],
146     concurrent => 2;
147
148   ok( !$subf[0]->is_cancelled, '$subf[0] not cancelled before failure' );
149
150   $subf[1]->fail( "failure" );
151
152   ok( $subf[0]->is_cancelled, '$subf[0] now cancelled after $subf[1] failure' );
153   ok( $future->is_ready, '$future now ready after $sub[1] failure' );
154   is( scalar $future->failure, "failure", '$future->failure after $sub[1] failure' );
155   ok( !defined $subf[2], '$subf[2] was never started after $subf[1] failure' );
156}
157
158# fmap_void immediate fail
159{
160   my @subf;
161   my $future = fmap_void {
162      if( $_[0] eq "fail" ) {
163         return Future->fail( "failure" );
164      }
165      else {
166         $subf[$_[0]] = Future->new;
167      }
168   } foreach => [ 0, "fail", 2 ],
169     concurrent => 3;
170
171   ok( $future->is_ready, '$future is already ready' );
172   is( scalar $future->failure, "failure", '$future->failure after immediate failure' );
173
174   ok( $subf[0]->is_cancelled, '$subf[0] is cancelled after immediate failure' );
175   ok( !defined $subf[2], '$subf[2] was never started after immediate failure' );
176}
177
178# fmap_void cancel
179{
180   my @subf;
181   my $future = fmap_void {
182      return $subf[$_[0]] = Future->new;
183   } foreach => [ 0, 1, 2 ],
184     concurrent => 2;
185
186   $future->cancel;
187
188   ok( $subf[0]->is_cancelled, '$subf[0] now cancelled after ->cancel' );
189   ok( $subf[1]->is_cancelled, '$subf[1] now cancelled after ->cancel' );
190   ok( !defined $subf[2], '$subf[2] was never started after ->cancel' );
191}
192
193# fmap_void return
194{
195   my $future = fmap_void {
196      return Future->done;
197   } foreach => [ 0 ], return => my $ret = Future->new;
198
199   identical( $future, $ret, 'repeat with return yields correct instance' );
200}
201
202done_testing;
203