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; |