]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/outcome/test/tests/swap.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / outcome / test / tests / swap.cpp
1 /* Unit testing for outcomes
2 (C) 2013-2022 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3
4
5 Boost Software License - Version 1.0 - August 17th, 2003
6
7 Permission is hereby granted, free of charge, to any person or organization
8 obtaining a copy of the software and accompanying documentation covered by
9 this license (the "Software") to use, reproduce, display, distribute,
10 execute, and transmit the Software, and to prepare derivative works of the
11 Software, and to permit third-parties to whom the Software is furnished to
12 do so, all subject to the following:
13
14 The copyright notices in the Software and this entire statement, including
15 the above license grant, this restriction and the following disclaimer,
16 must be included in all copies of the Software, in whole or in part, and
17 all derivative works of the Software, unless such copies or derivative
18 works are solely in the form of machine-executable object code generated by
19 a source language processor.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 DEALINGS IN THE SOFTWARE.
28 */
29
30 #include <boost/outcome.hpp>
31 #include <boost/test/unit_test.hpp>
32 #include <boost/test/unit_test_monitor.hpp>
33
34 /* Should be this:
35
36 78 move constructor count = 2
37 65 move assignment count = 3
38 78 move assignment count = 1
39 65 move constructor count = 2
40 78 move assignment count = 0
41 65 move assignment count = 1
42
43 78 move constructor count = 1
44 65 move assignment count = 2
45 78 move assignment count = 0
46 65 move assignment count = 1
47 78 move assignment count = 0
48
49 78 move constructor count = 2
50 65 move assignment count = 3
51 78 move assignment count = 1
52 65 move constructor count = 2
53 78 move assignment count = 0
54 65 move assignment count = 1
55
56 78 move constructor count = 1
57 65 move assignment count = 2
58 78 move assignment count = 0
59 65 move assignment count = 1
60 78 move assignment count = 0
61
62 78 move constructor count = 2
63 65 move assignment count = 3
64 78 move assignment count = 1
65 65 move constructor count = 2
66 78 move assignment count = 0
67 65 move assignment count = 1
68
69 78 move constructor count = 1
70 65 move assignment count = 2
71 78 move assignment count = 0
72 65 move assignment count = 1
73 78 move assignment count = 0
74
75 78 move constructor count = 2
76 65 move assignment count = 3
77 78 move assignment count = 1
78 65 move constructor count = 2
79 78 move assignment count = 0
80 65 move assignment count = 1
81
82 78 move constructor count = 1
83 65 move assignment count = 2
84 78 move assignment count = 0
85 65 move assignment count = 1
86 78 move assignment count = 0
87 */
88
89 #ifndef BOOST_NO_EXCEPTIONS
90 #ifdef _MSC_VER
91 #pragma warning(push)
92 #pragma warning(disable: 4297) // function assumed not to throw an exception but does
93 #endif
94 #if defined(__GNUC__) && !defined(__clang__)
95 #pragma GCC diagnostic push
96 #pragma GCC diagnostic ignored "-Wterminate"
97 #endif
98 template <bool mc, bool ma> struct Throwy
99 {
100 int count{0}, inc{0}, id{0};
101 Throwy() = default;
102 Throwy(int c, int d, int i = 1) noexcept
103 : count(c)
104 , inc(i)
105 , id(d)
106 {
107 }
108 Throwy(const Throwy &) = delete;
109 Throwy &operator=(const Throwy &) = delete;
110 Throwy(Throwy &&o) noexcept(!mc)
111 : count(o.count - o.inc)
112 , inc(o.inc)
113 , id(o.id) // NOLINT
114 {
115 if(mc)
116 {
117 std::cout << " " << id << " move constructor count = " << count << std::endl;
118 if(!count)
119 {
120 std::cout << " " << id << " move constructor throws!" << std::endl;
121 throw std::bad_alloc();
122 }
123 }
124 o.count = 0;
125 o.inc = 0;
126 o.id = 0;
127 }
128 Throwy &operator=(Throwy &&o) noexcept(!ma)
129 {
130 count = o.count - o.inc;
131 if(ma)
132 {
133 std::cout << " " << o.id << " move assignment count = " << count << std::endl;
134 if(!count)
135 {
136 std::cout << " " << o.id << " move assignment throws!" << std::endl;
137 throw std::bad_alloc();
138 }
139 }
140 inc = o.inc;
141 id = o.id;
142 o.count = 0;
143 o.inc = 0;
144 o.id = 0;
145 return *this;
146 }
147 ~Throwy() = default;
148 };
149 #if defined(__GNUC__) && !defined(__clang__)
150 #pragma GCC diagnostic pop
151 #endif
152 #ifdef _MSC_VER
153 #pragma warning(pop)
154 #endif
155 enum class ErrorCode
156 {
157 dummy
158 };
159 enum class ErrorCode2
160 {
161 dummy
162 };
163 template <bool mc, bool ma> using resulty1 = BOOST_OUTCOME_V2_NAMESPACE::result<Throwy<mc, ma>, ErrorCode, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
164 template <bool mc, bool ma> using resulty2 = BOOST_OUTCOME_V2_NAMESPACE::result<ErrorCode, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
165 template <bool mc, bool ma> using outcomey1 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, Throwy<mc, ma>, ErrorCode2, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
166 template <bool mc, bool ma> using outcomey2 = BOOST_OUTCOME_V2_NAMESPACE::outcome<ErrorCode, ErrorCode2, Throwy<mc, ma>, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow>;
167 #endif
168
169 BOOST_OUTCOME_AUTO_TEST_CASE(works_outcome_swap, "Tests that the outcome swaps as intended")
170 {
171 using namespace BOOST_OUTCOME_V2_NAMESPACE;
172 { // Does swap actually swap?
173 outcome<std::string> a("niall"), b("douglas");
174 BOOST_CHECK(a.value() == "niall");
175 BOOST_CHECK(b.value() == "douglas");
176 swap(a, b);
177 BOOST_CHECK(a.value() == "douglas");
178 BOOST_CHECK(b.value() == "niall");
179 a = boost::system::errc::not_enough_memory;
180 swap(a, b);
181 BOOST_CHECK(a.value() == "niall");
182 BOOST_CHECK(b.error() == boost::system::errc::not_enough_memory);
183 BOOST_CHECK(!a.has_lost_consistency());
184 BOOST_CHECK(!b.has_lost_consistency());
185 }
186 #ifndef BOOST_NO_EXCEPTIONS
187 { // Is noexcept propagated?
188 using nothrow_t = Throwy<false, false>;
189 using nothrow = resulty1<false, false>;
190 static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
191 static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
192 static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
193 static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
194
195 static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
196
197 static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
198 nothrow a(1, 78), b(1, 65);
199 a.swap(b);
200 static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
201 }
202 { // Is noexcept propagated?
203 using nothrow_t = Throwy<false, false>;
204 using nothrow = resulty2<false, false>;
205 static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
206 static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
207 static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
208 static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
209
210 static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
211
212 static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
213 nothrow a(1, 78), b(1, 65);
214 a.swap(b);
215 static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
216 }
217
218 { // Does swap implement the strong guarantee?
219 using throwy_t = Throwy<true, true>;
220 using throwy = resulty1<true, true>;
221 static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
222 static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
223 static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
224 static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
225
226 static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
227
228 std::cout << "Result value first swap succeeds, second swap second move assignment throws:" << std::endl;
229 {
230 throwy a(3, 78), b(4, 65);
231 a.swap(b);
232 static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
233 BOOST_CHECK(a.value().id == 65);
234 BOOST_CHECK(b.value().id == 78);
235 try
236 {
237 a.swap(b); // fails on first assignment
238 BOOST_REQUIRE(false);
239 }
240 catch(const std::bad_alloc & /*unused*/)
241 {
242 BOOST_CHECK(a.value().id == 65); // ensure it is perfectly restored
243 BOOST_CHECK(b.value().id == 78);
244 }
245 BOOST_CHECK(!a.has_lost_consistency());
246 BOOST_CHECK(!b.has_lost_consistency());
247 }
248 std::cout << "\nResult value second move assignment throws, on recover second move assignment throws:" << std::endl;
249 {
250 throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
251 try
252 {
253 a.swap(b);
254 BOOST_REQUIRE(false);
255 }
256 catch(const std::bad_alloc & /*unused*/)
257 {
258 BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
259 BOOST_CHECK(b.has_lost_consistency());
260 }
261 }
262 }
263 std::cout << "\nResult error first swap succeeds, second swap first move assignment throws:" << std::endl;
264 { // Does swap implement the strong guarantee?
265 using throwy_t = Throwy<true, true>;
266 using throwy = resulty2<true, true>;
267 static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
268 static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
269 static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
270 static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
271
272 static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
273
274 {
275 throwy a(3, 78), b(4, 65);
276 a.swap(b);
277 static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
278 BOOST_CHECK(a.error().id == 65);
279 BOOST_CHECK(b.error().id == 78);
280 try
281 {
282 a.swap(b); // fails on first assignment
283 BOOST_REQUIRE(false);
284 }
285 catch(const std::bad_alloc & /*unused*/)
286 {
287 BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
288 BOOST_CHECK(b.error().id == 78);
289 }
290 BOOST_CHECK(!a.has_lost_consistency());
291 BOOST_CHECK(!b.has_lost_consistency());
292 }
293 std::cout << "\nResult error second move assignment throws, on recover second move assignment throws:" << std::endl;
294 {
295 throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
296 try
297 {
298 a.swap(b);
299 BOOST_REQUIRE(false);
300 }
301 catch(const std::bad_alloc & /*unused*/)
302 {
303 BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
304 BOOST_CHECK(b.has_lost_consistency());
305 }
306 }
307 }
308
309 { // Is noexcept propagated?
310 using nothrow_t = Throwy<false, false>;
311 using nothrow = outcomey1<false, false>;
312 static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
313 static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
314 static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
315 static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
316
317 static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
318
319 static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
320 nothrow a(1, 78), b(1, 65);
321 a.swap(b);
322 static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
323 }
324 { // Is noexcept propagated?
325 using nothrow_t = Throwy<false, false>;
326 using nothrow = outcomey1<false, false>;
327 static_assert(std::is_nothrow_move_constructible<nothrow_t>::value, "throwy not correct!");
328 static_assert(std::is_nothrow_move_assignable<nothrow_t>::value, "throwy not correct!");
329 static_assert(std::is_nothrow_move_constructible<nothrow>::value, "type not correct!");
330 static_assert(std::is_nothrow_move_assignable<nothrow>::value, "type not correct!");
331
332 static_assert(detail::is_nothrow_swappable<nothrow_t>::value, "is_nothrow_swappable is not correct!");
333
334 static_assert(noexcept(nothrow(0, 0)), "type has a throwing value constructor!");
335 nothrow a(1, 78), b(1, 65);
336 a.swap(b);
337 static_assert(noexcept(a.swap(b)), "type has a throwing swap!");
338 }
339
340 std::cout << "\n\nOutcome value first swap succeeds, second swap first move assignment throws:" << std::endl;
341 { // Does swap implement the strong guarantee?
342 using throwy_t = Throwy<true, true>;
343 using throwy = outcomey1<true, true>;
344 static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
345 static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
346 static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
347 static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
348
349 static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
350
351 {
352 throwy a(3, 78), b(4, 65);
353 a.swap(b);
354 static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
355 BOOST_CHECK(a.error().id == 65);
356 BOOST_CHECK(b.error().id == 78);
357 try
358 {
359 a.swap(b); // fails on first assignment
360 BOOST_REQUIRE(false);
361 }
362 catch(const std::bad_alloc & /*unused*/)
363 {
364 BOOST_CHECK(a.error().id == 65); // ensure it is perfectly restored
365 BOOST_CHECK(b.error().id == 78);
366 }
367 BOOST_CHECK(!a.has_lost_consistency());
368 BOOST_CHECK(!b.has_lost_consistency());
369 }
370 std::cout << "\nOutcome value second move assignment throws, on recover second move assignment throws:" << std::endl;
371 {
372 throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
373 try
374 {
375 a.swap(b);
376 BOOST_REQUIRE(false);
377 }
378 catch(const std::bad_alloc & /*unused*/)
379 {
380 BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
381 BOOST_CHECK(b.has_lost_consistency());
382 }
383 }
384 }
385 std::cout << "\nOutcome error first swap succeeds, second swap first move assignment throws:" << std::endl;
386 { // Does swap implement the strong guarantee?
387 using throwy_t = Throwy<true, true>;
388 using throwy = outcomey2<true, true>;
389 static_assert(!std::is_nothrow_move_constructible<throwy_t>::value, "throwy not correct!");
390 static_assert(!std::is_nothrow_move_assignable<throwy_t>::value, "throwy not correct!");
391 static_assert(!std::is_nothrow_move_constructible<throwy>::value, "type not correct!");
392 static_assert(!std::is_nothrow_move_assignable<throwy>::value, "type not correct!");
393
394 static_assert(!detail::is_nothrow_swappable<throwy_t>::value, "is_nothrow_swappable is not correct!");
395
396 {
397 throwy a(3, 78), b(4, 65);
398 a.swap(b);
399 static_assert(!noexcept(a.swap(b)), "type has a non-throwing swap!");
400 BOOST_CHECK(a.exception().id == 65);
401 BOOST_CHECK(b.exception().id == 78);
402 try
403 {
404 a.swap(b); // fails on first assignment
405 BOOST_REQUIRE(false);
406 }
407 catch(const std::bad_alloc & /*unused*/)
408 {
409 BOOST_CHECK(a.exception().id == 65); // ensure it is perfectly restored
410 BOOST_CHECK(b.exception().id == 78);
411 }
412 BOOST_CHECK(!a.has_lost_consistency());
413 BOOST_CHECK(!b.has_lost_consistency());
414 }
415 std::cout << "\nOutcome error second move assignment throws, on recover second move assignment throws:" << std::endl;
416 {
417 throwy a(2, 78), b(3, 65); // fails on second assignment, cannot restore
418 try
419 {
420 a.swap(b);
421 BOOST_REQUIRE(false);
422 }
423 catch(const std::bad_alloc & /*unused*/)
424 {
425 BOOST_CHECK(a.has_lost_consistency()); // both must be marked tainted
426 BOOST_CHECK(b.has_lost_consistency());
427 }
428 }
429 std::cout << std::endl;
430 }
431 #endif
432 }