@@ -6,6 +6,7 @@ use futures::{stream, StreamExt as _};
6
6
use proptest:: prelude:: * ;
7
7
use proptest_derive:: Arbitrary ;
8
8
use std:: time:: Duration ;
9
+ use tokio_stream:: wrappers:: UnboundedReceiverStream ;
9
10
10
11
#[ derive( Clone , Debug , Arbitrary ) ]
11
12
struct TestState {
@@ -17,6 +18,8 @@ struct TestState {
17
18
18
19
#[ derive( Copy , Clone , Debug , Arbitrary ) ]
19
20
struct TestFutureDesc {
21
+ #[ proptest( strategy = "duration_strategy()" ) ]
22
+ start_delay : Duration ,
20
23
#[ proptest( strategy = "duration_strategy()" ) ]
21
24
delay : Duration ,
22
25
#[ proptest( strategy = "0usize..8" ) ]
@@ -28,10 +31,23 @@ fn duration_strategy() -> BoxedStrategy<Duration> {
28
31
( 0u64 ..1000 ) . prop_map ( Duration :: from_millis) . boxed ( )
29
32
}
30
33
34
+ #[ test]
35
+ fn test_examples ( ) {
36
+ let state = TestState {
37
+ max_weight : 1 ,
38
+ future_descriptions : vec ! [ TestFutureDesc {
39
+ start_delay: Duration :: ZERO ,
40
+ delay: Duration :: ZERO ,
41
+ weight: 0 ,
42
+ } ] ,
43
+ } ;
44
+ test_future_queue_impl ( state) ;
45
+ }
46
+
31
47
proptest ! {
32
48
#[ test]
33
49
fn proptest_future_queue( state: TestState ) {
34
- proptest_future_queue_impl ( state)
50
+ test_future_queue_impl ( state)
35
51
}
36
52
}
37
53
@@ -41,33 +57,48 @@ enum FutureEvent {
41
57
Finished ( usize , TestFutureDesc ) ,
42
58
}
43
59
44
- fn proptest_future_queue_impl ( state : TestState ) {
60
+ fn test_future_queue_impl ( state : TestState ) {
45
61
let runtime = tokio:: runtime:: Builder :: new_current_thread ( )
46
62
. enable_time ( )
47
63
. start_paused ( true )
48
64
. build ( )
49
65
. expect ( "tokio builder succeeded" ) ;
50
66
let ( sender, mut receiver) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
67
+ let ( future_sender, future_receiver) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
51
68
let futures = state
52
69
. future_descriptions
53
70
. iter ( )
54
71
. enumerate ( )
55
- . map ( |( id, desc) | {
72
+ . map ( move |( id, desc) | {
73
+ let desc = * desc;
56
74
let sender = sender. clone ( ) ;
57
- // For each description, create a future.
58
- let delay_fut = async move {
59
- // Send the fact that this future started to the mpsc queue.
60
- sender
61
- . send ( FutureEvent :: Started ( id, * desc) )
62
- . expect ( "receiver held open by loop" ) ;
63
- tokio:: time:: sleep ( desc. delay ) . await ;
64
- sender
65
- . send ( FutureEvent :: Finished ( id, * desc) )
66
- . expect ( "receiver held open by loop" ) ;
67
- } ;
68
- ( desc. weight , delay_fut)
69
- } ) ;
70
- let stream = stream:: iter ( futures) ;
75
+ let future_sender = future_sender. clone ( ) ;
76
+ async move {
77
+ // First, sleep for this long.
78
+ tokio:: time:: sleep ( desc. start_delay ) . await ;
79
+ // For each description, create a future.
80
+ let delay_fut = async move {
81
+ // Send the fact that this future started to the mpsc queue.
82
+ sender
83
+ . send ( FutureEvent :: Started ( id, desc) )
84
+ . expect ( "receiver held open by loop" ) ;
85
+ tokio:: time:: sleep ( desc. delay ) . await ;
86
+ sender
87
+ . send ( FutureEvent :: Finished ( id, desc) )
88
+ . expect ( "receiver held open by loop" ) ;
89
+ } ;
90
+ // Errors should never occur here.
91
+ if let Err ( err) = future_sender. send ( ( desc. weight , delay_fut) ) {
92
+ panic ! ( "future_receiver held open by loop: {}" , err) ;
93
+ }
94
+ }
95
+ } )
96
+ . collect :: < Vec < _ > > ( ) ;
97
+ let combined_future = stream:: iter ( futures) . buffer_unordered ( 1 ) . collect :: < ( ) > ( ) ;
98
+ runtime. spawn ( combined_future) ;
99
+
100
+ // We're going to use future_receiver as a stream.
101
+ let stream = UnboundedReceiverStream :: new ( future_receiver) ;
71
102
72
103
let mut completed_map = vec ! [ false ; state. future_descriptions. len( ) ] ;
73
104
let mut last_started_id: Option < usize > = None ;
@@ -76,13 +107,14 @@ fn proptest_future_queue_impl(state: TestState) {
76
107
runtime. block_on ( async move {
77
108
// Record values that have been completed in this map.
78
109
let mut stream = stream. future_queue ( state. max_weight ) ;
110
+ let mut receiver_done = false ;
79
111
loop {
80
112
tokio:: select! {
81
113
// biased ensures that the receiver is drained before the stream is polled. Without
82
114
// it, it's possible that we fail to record the completion of some futures in status_map.
83
115
biased;
84
116
85
- recv = receiver. recv( ) => {
117
+ recv = receiver. recv( ) , if !receiver_done => {
86
118
match recv {
87
119
Some ( FutureEvent :: Started ( id, desc) ) => {
88
120
// last_started_id must be 1 less than id.
@@ -106,6 +138,7 @@ fn proptest_future_queue_impl(state: TestState) {
106
138
}
107
139
None => {
108
140
// All futures finished -- going to check for completion in stream.next() below.
141
+ receiver_done = true ;
109
142
}
110
143
}
111
144
}
0 commit comments