10
10
11
11
#include < boost/mysql/impl/internal/variant_stream.hpp>
12
12
13
+ #include < boost/asio/cancellation_type.hpp>
13
14
#include < boost/asio/error.hpp>
14
15
#include < boost/asio/generic/stream_protocol.hpp>
15
16
#include < boost/asio/ip/address.hpp>
16
17
#include < boost/asio/ip/tcp.hpp>
17
18
#include < boost/asio/local/stream_protocol.hpp>
19
+ #include < boost/core/span.hpp>
18
20
#include < boost/test/tools/detail/per_element_manip.hpp>
19
21
#include < boost/test/tools/detail/print_helper.hpp>
20
22
#include < boost/test/unit_test.hpp>
28
30
using namespace boost ::mysql;
29
31
using namespace boost ::mysql::test;
30
32
namespace asio = boost::asio;
33
+ using asio::cancellation_type_t ;
31
34
using asio::ip::tcp;
32
35
using boost::test_tools::per_element;
33
36
using detail::vsconnect_action_type;
@@ -68,7 +71,7 @@ struct fixture : io_context_fixture
68
71
detail::variant_stream_state st{ctx.get_executor (), nullptr };
69
72
any_address addr;
70
73
71
- std::array<tcp::endpoint, 2 > tcp_endpoints () const
74
+ static std::array<tcp::endpoint, 2 > tcp_endpoints ()
72
75
{
73
76
return {
74
77
{
@@ -77,6 +80,11 @@ struct fixture : io_context_fixture
77
80
}
78
81
};
79
82
}
83
+
84
+ static tcp::resolver::results_type make_resolver_results (boost::span<const tcp::endpoint> endpoints)
85
+ {
86
+ return tcp::resolver::results_type::create (endpoints.begin (), endpoints.end (), " my_host" , " 1234" );
87
+ }
80
88
};
81
89
82
90
BOOST_FIXTURE_TEST_CASE (tcp_success, fixture)
@@ -86,21 +94,21 @@ BOOST_FIXTURE_TEST_CASE(tcp_success, fixture)
86
94
detail::variant_stream_connect_algo algo{st, addr};
87
95
88
96
// Initiate: we should resolve
89
- auto act = algo.resume (error_code (), nullptr );
97
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
90
98
BOOST_TEST (act.type == vsconnect_action_type::resolve);
91
99
BOOST_TEST (*act.data .resolve .hostname == " my_host" );
92
100
BOOST_TEST (*act.data .resolve .service == " 1234" );
93
101
94
102
// Resolving done: we should connect
95
103
auto endpoints = tcp_endpoints ();
96
- auto r = tcp::resolver::results_type::create (endpoints. begin (), endpoints. end (), " my_host " , " 1234 " );
97
- act = algo.resume (error_code (), &r);
104
+ auto r = make_resolver_results (endpoints);
105
+ act = algo.resume (error_code (), &r, cancellation_type_t ::none );
98
106
BOOST_TEST (act.type == vsconnect_action_type::connect);
99
107
BOOST_TEST (act.data .connect == endpoints, per_element ());
100
108
101
109
// Connect done: success
102
110
st.sock .open (asio::ip::tcp::v4 ()); // Simulate a connection - otherwise setting sock options fails
103
- act = algo.resume (error_code (), nullptr );
111
+ act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
104
112
BOOST_TEST (act.type == vsconnect_action_type::none);
105
113
BOOST_TEST (act.data .err == error_code ());
106
114
}
@@ -112,14 +120,14 @@ BOOST_FIXTURE_TEST_CASE(tcp_error_resolve, fixture)
112
120
detail::variant_stream_connect_algo algo{st, addr};
113
121
114
122
// Initiate: we should resolve
115
- auto act = algo.resume (error_code (), nullptr );
123
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
116
124
BOOST_TEST (act.type == vsconnect_action_type::resolve);
117
125
BOOST_TEST (*act.data .resolve .hostname == " my_host" );
118
126
BOOST_TEST (*act.data .resolve .service == " 1234" );
119
127
120
128
// Resolving error: done
121
129
asio::ip::tcp::resolver::results_type r;
122
- act = algo.resume (asio::error::connection_reset, &r);
130
+ act = algo.resume (asio::error::connection_reset, &r, cancellation_type_t ::none );
123
131
BOOST_TEST (act.type == vsconnect_action_type::none);
124
132
BOOST_TEST (act.data .err == asio::error::connection_reset);
125
133
}
@@ -131,20 +139,20 @@ BOOST_FIXTURE_TEST_CASE(tcp_error_connect, fixture)
131
139
detail::variant_stream_connect_algo algo{st, addr};
132
140
133
141
// Initiate: we should resolve
134
- auto act = algo.resume (error_code (), nullptr );
142
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
135
143
BOOST_TEST (act.type == vsconnect_action_type::resolve);
136
144
BOOST_TEST (*act.data .resolve .hostname == " my_host" );
137
145
BOOST_TEST (*act.data .resolve .service == " 1234" );
138
146
139
147
// Resolving done: we should connect
140
148
auto endpoints = tcp_endpoints ();
141
- auto r = tcp::resolver::results_type::create (endpoints. begin (), endpoints. end (), " my_host " , " 1234 " );
142
- act = algo.resume (error_code (), &r);
149
+ auto r = make_resolver_results (endpoints);
150
+ act = algo.resume (error_code (), &r, cancellation_type_t ::none );
143
151
BOOST_TEST (act.type == vsconnect_action_type::connect);
144
152
BOOST_TEST (act.data .connect == endpoints, per_element ());
145
153
146
154
// Connect failed: done. No socket option is set
147
- act = algo.resume (asio::error::connection_reset, nullptr );
155
+ act = algo.resume (asio::error::connection_reset, nullptr , cancellation_type_t ::none );
148
156
BOOST_TEST (act.type == vsconnect_action_type::none);
149
157
BOOST_TEST (act.data .err == asio::error::connection_reset);
150
158
}
@@ -158,12 +166,12 @@ BOOST_FIXTURE_TEST_CASE(unix_success, fixture)
158
166
159
167
// Initiate: we should connect
160
168
const asio::local::stream_protocol::endpoint endpoints[]{" /my/path" };
161
- auto act = algo.resume (error_code (), nullptr );
169
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
162
170
BOOST_TEST (act.type == vsconnect_action_type::connect);
163
171
BOOST_TEST (act.data .connect == endpoints, per_element ());
164
172
165
173
// Connect done: success. No socket option is set
166
- act = algo.resume (error_code (), nullptr );
174
+ act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
167
175
BOOST_TEST (act.type == vsconnect_action_type::none);
168
176
BOOST_TEST (act.data .err == error_code ());
169
177
}
@@ -176,12 +184,12 @@ BOOST_FIXTURE_TEST_CASE(unix_error_connect, fixture)
176
184
177
185
// Initiate: we should connect
178
186
const asio::local::stream_protocol::endpoint endpoints[]{" /my/path" };
179
- auto act = algo.resume (error_code (), nullptr );
187
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
180
188
BOOST_TEST (act.type == vsconnect_action_type::connect);
181
189
BOOST_TEST (act.data .connect == endpoints, per_element ());
182
190
183
191
// Connect failed: done. No socket option is set
184
- act = algo.resume (asio::error::network_reset, nullptr );
192
+ act = algo.resume (asio::error::network_reset, nullptr , cancellation_type_t ::none );
185
193
BOOST_TEST (act.type == vsconnect_action_type::none);
186
194
BOOST_TEST (act.data .err == asio::error::network_reset);
187
195
}
@@ -193,16 +201,117 @@ BOOST_FIXTURE_TEST_CASE(unix_unsupported, fixture)
193
201
detail::variant_stream_connect_algo algo{st, addr};
194
202
195
203
// Initiate: immediate completion
196
- auto act = algo.resume (error_code (), nullptr );
204
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
197
205
BOOST_TEST (act.type == vsconnect_action_type::immediate);
198
206
199
207
// Resuming again yields the error
200
- act = algo.resume (error_code (), nullptr );
208
+ act = algo.resume (error_code (), nullptr , cancellation_type_t ::none );
201
209
BOOST_TEST (act.type == vsconnect_action_type::none);
202
210
BOOST_TEST (act.data .err == asio::error::operation_not_supported);
203
211
}
204
212
#endif
205
213
214
+ // Cancellation: we use the cancellation state and error on cancellation
215
+ // Only relevant in the TCP case, as UNIX connect is a single operation
216
+ // If the cancellation state contains the terminal type, we fail
217
+ BOOST_FIXTURE_TEST_CASE (cancellation_contains_terminal, fixture)
218
+ {
219
+ struct
220
+ {
221
+ const char * name;
222
+ cancellation_type_t cancellation_state;
223
+ } test_cases[] = {
224
+ {" terminal" , cancellation_type_t ::terminal},
225
+ {" all" , cancellation_type_t ::all },
226
+ };
227
+
228
+ for (const auto & tc : test_cases)
229
+ {
230
+ BOOST_TEST_CONTEXT (tc.name )
231
+ {
232
+ // Setup
233
+ addr.emplace_host_and_port (" my_host" , 1234 );
234
+ detail::variant_stream_connect_algo algo{st, addr};
235
+
236
+ // Initiate: we should resolve
237
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none);
238
+ BOOST_TEST (act.type == vsconnect_action_type::resolve);
239
+
240
+ // Resolving finished successfully, but the cancellation state is set
241
+ auto endpoints = tcp_endpoints ();
242
+ auto r = make_resolver_results (endpoints);
243
+ act = algo.resume (error_code (), &r, tc.cancellation_state );
244
+ BOOST_TEST (act.type == vsconnect_action_type::none);
245
+ BOOST_TEST (act.data .err == asio::error::operation_aborted);
246
+ }
247
+ }
248
+ }
249
+
250
+ // Since we only support terminal cancellation, we ignore other cancellation types
251
+ BOOST_FIXTURE_TEST_CASE (cancellation_no_terminal, fixture)
252
+ {
253
+ struct
254
+ {
255
+ const char * name;
256
+ cancellation_type_t cancellation_state;
257
+ } test_cases[] = {
258
+ {" partial" , cancellation_type_t ::partial },
259
+ {" total" , cancellation_type_t ::total },
260
+ {" partial+total" , cancellation_type_t ::partial | cancellation_type_t ::total},
261
+ {" other" , static_cast <cancellation_type_t >(0x80 ) },
262
+ };
263
+
264
+ for (const auto & tc : test_cases)
265
+ {
266
+ BOOST_TEST_CONTEXT (tc.name )
267
+ {
268
+ // Setup
269
+ addr.emplace_host_and_port (" my_host" , 1234 );
270
+ detail::variant_stream_connect_algo algo{st, addr};
271
+
272
+ // Initiate: we should resolve
273
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none);
274
+ BOOST_TEST (act.type == vsconnect_action_type::resolve);
275
+ BOOST_TEST (*act.data .resolve .hostname == " my_host" );
276
+ BOOST_TEST (*act.data .resolve .service == " 1234" );
277
+
278
+ // Resolving done: we should connect
279
+ auto endpoints = tcp_endpoints ();
280
+ auto r = make_resolver_results (endpoints);
281
+ act = algo.resume (error_code (), &r, tc.cancellation_state );
282
+ BOOST_TEST (act.type == vsconnect_action_type::connect);
283
+ BOOST_TEST (act.data .connect == endpoints, per_element ());
284
+
285
+ // Connect done: success
286
+ // Simulate a connection - otherwise setting sock options fails
287
+ st.sock .open (asio::ip::tcp::v4 ());
288
+ act = algo.resume (error_code (), nullptr , tc.cancellation_state );
289
+ BOOST_TEST (act.type == vsconnect_action_type::none);
290
+ BOOST_TEST (act.data .err == error_code ());
291
+ }
292
+ }
293
+ }
294
+
295
+ // If there is an I/O error and the cancellation state is set, the error wins
296
+ BOOST_FIXTURE_TEST_CASE (cancellation_error, fixture)
297
+ {
298
+ // Setup
299
+ addr.emplace_host_and_port (" my_host" , 1234 );
300
+ detail::variant_stream_connect_algo algo{st, addr};
301
+
302
+ // Initiate: we should resolve
303
+ auto act = algo.resume (error_code (), nullptr , cancellation_type_t ::none);
304
+ BOOST_TEST (act.type == vsconnect_action_type::resolve);
305
+ BOOST_TEST (*act.data .resolve .hostname == " my_host" );
306
+ BOOST_TEST (*act.data .resolve .service == " 1234" );
307
+
308
+ // Resolving error, and the cancellation state is set
309
+ asio::ip::tcp::resolver::results_type r;
310
+ act = algo.resume (asio::error::connection_reset, &r, cancellation_type_t ::terminal);
311
+ BOOST_TEST (act.type == vsconnect_action_type::none);
312
+ BOOST_TEST (act.data .err == asio::error::connection_reset);
313
+ }
314
+
206
315
BOOST_AUTO_TEST_SUITE_END ()
207
316
208
317
} // namespace
0 commit comments