| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
|
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
|
3
|
|
|
|
|
|
|
# |
|
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2020-2023 -- leonerd@leonerd.org.uk |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Test::Future::AsyncAwait::Awaitable 0.65; |
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
226497
|
use v5.14; |
|
|
1
|
|
|
|
|
9
|
|
|
9
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
24
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
5
|
use Test2::V0; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
5
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
1140
|
use Exporter 'import'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
114
|
|
|
14
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
15
|
|
|
|
|
|
|
test_awaitable |
|
16
|
|
|
|
|
|
|
); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
C - conformance tests for awaitable role API |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Test::More; |
|
25
|
|
|
|
|
|
|
use Test::Future::AsyncAwait::Awaitable; |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use My::Future::Subclass; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
test_awaitable "My subclass of Future", |
|
30
|
|
|
|
|
|
|
class => "My::Future::Subclass"; |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
done_testing; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This module provides a single test function, which runs a suite of subtests to |
|
37
|
|
|
|
|
|
|
check that a given class provides a useable implementation of the |
|
38
|
|
|
|
|
|
|
L role. It runs tests that simulate various |
|
39
|
|
|
|
|
|
|
ways in which L will try to use an instance of this class, |
|
40
|
|
|
|
|
|
|
to check that the implementation is valid. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=cut |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head2 test_awaitable |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
test_awaitable( $title, %args ) |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Runs the API conformance tests. C<$title> is printed in the test description |
|
53
|
|
|
|
|
|
|
output so should be some human-friendly string. |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Takes the following named arguments: |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=over 4 |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item class => STRING |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
Gives the name of the class. This is the class on which the C |
|
62
|
|
|
|
|
|
|
and C methods will be invoked. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=item new => CODE |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Optional. Gives a callback function to invoke to construct a new pending |
|
67
|
|
|
|
|
|
|
instance; used by the tests to create pending instances that would be passed |
|
68
|
|
|
|
|
|
|
into the C keyword. As this is not part of the API as such, the test |
|
69
|
|
|
|
|
|
|
code does not rely on being able to directly perform it via the API. |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
This argument is optional; if not provided the tests will simply try to invoke |
|
72
|
|
|
|
|
|
|
the regular C constructor on the given class name. For most |
|
73
|
|
|
|
|
|
|
implementations this should be sufficient. |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$f = $new->() |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item cancel => CODE |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Optional. Gives a callback function to invoke to cancel a pending instance, if |
|
80
|
|
|
|
|
|
|
the implementation provides cancellation semantics. If this callback is |
|
81
|
|
|
|
|
|
|
provided then an extra subtest suite is run to check the API around |
|
82
|
|
|
|
|
|
|
cancellation. |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
$cancel->( $f ) |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=item force => CODE |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Optional. Gives a callback function to invoke to wait for a promise to invoke |
|
89
|
|
|
|
|
|
|
its on-ready callbacks. Some future-like implementations will run these |
|
90
|
|
|
|
|
|
|
immediately when the future is marked as done or failed, and so this callback |
|
91
|
|
|
|
|
|
|
will not be required. Other implementations will defer these invocations, |
|
92
|
|
|
|
|
|
|
perhaps until the next tick of an event loop or similar. In the latter case, |
|
93
|
|
|
|
|
|
|
these implementations should provide a way for the test to wait for this to |
|
94
|
|
|
|
|
|
|
happen. |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
$force->( $f ) |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=back |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
my $FILE = __FILE__; |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
my %FIXED_MODULE_VERSIONS = ( |
|
105
|
|
|
|
|
|
|
'Future::PP' => '0.50', |
|
106
|
|
|
|
|
|
|
'Future::XS' => '0.09', |
|
107
|
|
|
|
|
|
|
); |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _complain_package_version |
|
110
|
|
|
|
|
|
|
{ |
|
111
|
0
|
|
|
0
|
|
0
|
my ( $pkg ) = @_; |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Drill down to the most base class that isn't Future::_base |
|
114
|
|
|
|
|
|
|
{ |
|
115
|
1
|
|
|
1
|
|
16
|
no strict 'refs'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
92
|
|
|
|
0
|
|
|
|
|
0
|
|
|
116
|
0
|
|
0
|
|
|
0
|
$pkg = ${"${pkg}::ISA"}[0] while @{"${pkg}::ISA"} and ${"${pkg}::ISA"}[0] ne "Future::_base"; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
1
|
|
|
1
|
|
6
|
my $pkgver = do { no strict 'refs'; ${"${pkg}::VERSION"} }; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1008
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
120
|
0
|
|
|
|
|
0
|
my $wantver = $FIXED_MODULE_VERSIONS{$pkg}; |
|
121
|
|
|
|
|
|
|
|
|
122
|
0
|
0
|
0
|
|
|
0
|
if( defined $wantver && $pkgver < $wantver ) { |
|
123
|
0
|
|
|
|
|
0
|
diag( "$pkg VERSION is only $pkgver; this might be fixed by updating to version $wantver" ); |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
else { |
|
126
|
0
|
|
|
|
|
0
|
diag( "$pkg VERSION is $pkgver; maybe a later version fixes it?" ); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub test_awaitable |
|
131
|
|
|
|
|
|
|
{ |
|
132
|
1
|
|
|
1
|
1
|
104
|
my ( $title, %args ) = @_; |
|
133
|
|
|
|
|
|
|
|
|
134
|
1
|
|
|
|
|
5
|
my $class = $args{class}; |
|
135
|
1
|
|
50
|
4
|
|
11
|
my $new = $args{new} || sub { return $class->new() }; |
|
|
4
|
|
|
|
|
18
|
|
|
136
|
1
|
|
|
|
|
2
|
my $cancel = $args{cancel}; |
|
137
|
1
|
|
|
|
|
4
|
my $force = $args{force}; |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
subtest "$title immediate done" => sub { |
|
140
|
1
|
|
|
1
|
|
6082
|
ok( my $f = $class->AWAIT_NEW_DONE( "result" ), "AWAIT_NEW_DONE yields object" ); |
|
141
|
|
|
|
|
|
|
|
|
142
|
1
|
|
|
|
|
411
|
ok( $f->AWAIT_IS_READY, 'AWAIT_IS_READY true' ); |
|
143
|
1
|
|
|
|
|
261
|
ok( !$f->AWAIT_IS_CANCELLED, 'AWAIT_IS_CANCELLED false' ); |
|
144
|
|
|
|
|
|
|
|
|
145
|
1
|
|
|
|
|
272
|
is( [ $f->AWAIT_GET ], [ "result" ], 'AWAIT_GET in list context' ); |
|
146
|
1
|
|
|
|
|
879
|
is( scalar $f->AWAIT_GET, "result", 'AWAIT_GET in scalar context' ); |
|
147
|
1
|
|
|
|
|
412
|
ok( defined eval { $f->AWAIT_GET; 1 }, 'AWAIT_GET in void context' ); |
|
|
1
|
|
|
|
|
8
|
|
|
|
1
|
|
|
|
|
14
|
|
|
148
|
1
|
|
|
|
|
11
|
}; |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
subtest "$title immediate fail" => sub { |
|
151
|
1
|
|
|
1
|
|
286
|
ok( my $f = $class->AWAIT_NEW_FAIL( "Oopsie" ), "AWAIT_NEW_FAIL yields object" ); |
|
152
|
|
|
|
|
|
|
|
|
153
|
1
|
|
|
|
|
270
|
ok( $f->AWAIT_IS_READY, 'AWAIT_IS_READY true' ); |
|
154
|
1
|
|
|
|
|
237
|
ok( !$f->AWAIT_IS_CANCELLED, 'AWAIT_IS_CANCELLED false' ); |
|
155
|
|
|
|
|
|
|
|
|
156
|
1
|
|
|
|
|
233
|
my $LINE = __LINE__+1; |
|
157
|
1
|
|
|
|
|
2
|
ok( !defined eval { $f->AWAIT_GET; 1 }, 'AWAIT_GET in void context' ); |
|
|
1
|
|
|
|
|
5
|
|
|
|
0
|
|
|
|
|
0
|
|
|
158
|
1
|
50
|
|
|
|
513
|
is( $@, "Oopsie at $FILE line $LINE.\n", 'AWAIT_GET throws exception' ) or |
|
159
|
|
|
|
|
|
|
_complain_package_version( ref $f ); |
|
160
|
1
|
|
|
|
|
2239
|
}; |
|
161
|
|
|
|
|
|
|
|
|
162
|
1
|
50
|
|
|
|
1832
|
my $fproto = $new->() or BAIL_OUT( "new did not yield an instance" ); |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
subtest "$title deferred done" => sub { |
|
165
|
1
|
|
|
1
|
|
286
|
ok( my $f = $fproto->AWAIT_CLONE, 'AWAIT_CLONE yields object' ); |
|
166
|
|
|
|
|
|
|
|
|
167
|
1
|
|
|
|
|
271
|
ok( !$f->AWAIT_IS_READY, 'AWAIT_IS_READY false' ); |
|
168
|
|
|
|
|
|
|
|
|
169
|
1
|
|
|
|
|
236
|
$f->AWAIT_DONE( "Late result" ); |
|
170
|
|
|
|
|
|
|
|
|
171
|
1
|
|
|
|
|
55
|
ok( $f->AWAIT_IS_READY, 'AWAIT_IS_READY true' ); |
|
172
|
|
|
|
|
|
|
|
|
173
|
1
|
|
|
|
|
233
|
is( scalar $f->AWAIT_GET, "Late result", 'AWAIT_GET in scalar context' ); |
|
174
|
1
|
|
|
|
|
17
|
}; |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
subtest "$title deferred fail" => sub { |
|
177
|
1
|
|
|
1
|
|
304
|
ok( my $f = $fproto->AWAIT_CLONE, 'AWAIT_CLONE yields object' ); |
|
178
|
|
|
|
|
|
|
|
|
179
|
1
|
|
|
|
|
267
|
ok( !$f->AWAIT_IS_READY, 'AWAIT_IS_READY false' ); |
|
180
|
|
|
|
|
|
|
|
|
181
|
1
|
|
|
|
|
240
|
$f->AWAIT_FAIL( "Late oopsie" ); |
|
182
|
|
|
|
|
|
|
|
|
183
|
1
|
|
|
|
|
46
|
ok( $f->AWAIT_IS_READY, 'AWAIT_IS_READY true' ); |
|
184
|
|
|
|
|
|
|
|
|
185
|
1
|
|
|
|
|
232
|
my $LINE = __LINE__+1; |
|
186
|
1
|
|
|
|
|
2
|
ok( !defined eval { $f->AWAIT_GET; 1 }, 'AWAIT_GET in void context' ); |
|
|
1
|
|
|
|
|
21
|
|
|
|
0
|
|
|
|
|
0
|
|
|
187
|
1
|
50
|
|
|
|
379
|
is( $@, "Late oopsie at $FILE line $LINE.\n", 'AWAIT_GET throws exception' ) or |
|
188
|
|
|
|
|
|
|
_complain_package_version( ref $f ); |
|
189
|
1
|
|
|
|
|
1699
|
}; |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
subtest "$title on-ready" => sub { |
|
192
|
1
|
50
|
|
1
|
|
278
|
my $f = $new->() or BAIL_OUT( "new did not yield an instance" ); |
|
193
|
|
|
|
|
|
|
|
|
194
|
1
|
|
|
|
|
11
|
my $called; |
|
195
|
1
|
|
|
|
|
12
|
$f->AWAIT_ON_READY( sub { $called++ } ); |
|
|
1
|
|
|
|
|
51
|
|
|
196
|
1
|
|
|
|
|
22
|
ok( !$called, 'AWAIT_ON_READY CB not yet invoked' ); |
|
197
|
|
|
|
|
|
|
|
|
198
|
1
|
|
|
|
|
239
|
$f->AWAIT_DONE( "ping" ); |
|
199
|
1
|
50
|
|
|
|
7
|
$force->( $f ) if $force; |
|
200
|
1
|
|
|
|
|
4
|
ok( $called, 'AWAIT_ON_READY CB now invoked' ); |
|
201
|
1
|
|
|
|
|
1805
|
}; |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
$cancel and subtest "$title cancellation" => sub { |
|
204
|
1
|
50
|
|
1
|
|
275
|
my $f1 = $new->() or BAIL_OUT( "new did not yield an instance" ); |
|
205
|
|
|
|
|
|
|
|
|
206
|
1
|
|
|
|
|
11
|
my $f2 = $f1->AWAIT_CLONE; |
|
207
|
|
|
|
|
|
|
|
|
208
|
1
|
|
|
|
|
18
|
$f1->AWAIT_CHAIN_CANCEL( $f2 ); |
|
209
|
|
|
|
|
|
|
|
|
210
|
1
|
|
|
|
|
22
|
ok( !$f2->AWAIT_IS_CANCELLED, 'AWAIT_IS_CANCELLED false before cancellation' ); |
|
211
|
|
|
|
|
|
|
|
|
212
|
1
|
|
|
|
|
253
|
$cancel->( $f1 ); |
|
213
|
|
|
|
|
|
|
|
|
214
|
1
|
|
|
|
|
106
|
ok( $f2->AWAIT_IS_CANCELLED, 'AWAIT_IS_CANCELLED true after AWAIT_ON_CANCEL propagation' ); |
|
215
|
|
|
|
|
|
|
|
|
216
|
1
|
50
|
|
|
|
238
|
my $f3 = $new->() or BAIL_OUT( "new did not yield an instance" ); |
|
217
|
|
|
|
|
|
|
|
|
218
|
1
|
|
|
|
|
9
|
my $cancelled; |
|
219
|
1
|
|
|
|
|
19
|
$f3->AWAIT_ON_CANCEL( sub { $cancelled++ } ); |
|
|
1
|
|
|
|
|
16
|
|
|
220
|
|
|
|
|
|
|
|
|
221
|
1
|
|
|
|
|
12
|
$cancel->( $f3 ); |
|
222
|
|
|
|
|
|
|
|
|
223
|
1
|
|
|
|
|
25
|
ok( $cancelled, 'AWAIT_ON_CANCEL invoked callback' ); |
|
224
|
1
|
50
|
|
|
|
1695
|
}; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head1 AUTHOR |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Paul Evans |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=cut |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
0x55AA; |