1 /* Unit testing for outcomes
2 (C) 2013-2020 Niall Douglas <http://www.nedproductions.biz/> (30 commits)
5 Boost Software License - Version 1.0 - August 17th, 2003
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:
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.
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.
30 #ifdef TESTING_WG21_EXPERIMENTAL_RESULT
31 #include <boost/outcome/experimental/result.hpp>
32 #define BOOST_OUTCOME_AUTO_TEST_CASE(...) BOOST_AUTO_TEST_CASE(__VA_ARGS__)
34 #include <boost/outcome/result.hpp>
36 #include <boost/test/unit_test.hpp>
37 #include <boost/test/unit_test_monitor.hpp>
41 #ifndef BOOST_NO_EXCEPTIONS
42 // Custom error type with payload
45 boost::system::error_code ec
;
46 const char *str
{nullptr};
48 payload(boost::system::errc::errc_t _ec
, const char *_str
)
49 : ec(make_error_code(_ec
))
54 struct payload_exception
: std::runtime_error
56 explicit payload_exception(const char *what
)
57 : std::runtime_error(what
)
61 inline const boost::system::error_code
&make_error_code(const payload
&p
)
65 inline void outcome_throw_as_system_error_with_payload(const payload
&p
)
67 throw payload_exception(p
.str
);
71 BOOST_OUTCOME_AUTO_TEST_CASE(works_result
, "Tests that the result works as intended")
73 #ifdef TESTING_WG21_EXPERIMENTAL_RESULT
74 using namespace std::experimental
;
75 using std::in_place_type
;
77 using namespace BOOST_OUTCOME_V2_NAMESPACE
;
80 static_assert(std::is_constructible
<result
<long>, int>::value
, "Sanity check that monad can be constructed from a value_type");
81 static_assert(!std::is_constructible
<result
<result
<long>>, int>::value
, "Sanity check that outer monad can be constructed from an inner monad's value_type");
82 #if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
83 static_assert(!std::is_constructible
<result
<result
<result
<long>>>, int>::value
, "Sanity check that outer monad can be constructed from an inner inner monad's value_type");
84 static_assert(!std::is_constructible
<result
<result
<result
<result
<long>>>>, int>::value
, "Sanity check that outer monad can be constructed from an inner inner monad's value_type");
87 static_assert(std::is_constructible
<result
<int>, result
<long>>::value
, "Sanity check that compatible monads can be constructed from one another");
88 static_assert(std::is_constructible
<result
<result
<int>>, result
<long>>::value
, "Sanity check that outer monad can be constructed from a compatible monad");
89 #if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
90 static_assert(!std::is_constructible
<result
<result
<result
<int>>>, result
<long>>::value
, "Sanity check that outer monad can be constructed from a compatible monad up to two nestings deep");
91 static_assert(!std::is_constructible
<result
<result
<result
<result
<int>>>>, result
<long>>::value
, "Sanity check that outer monad can be constructed from a compatible monad three or more nestings deep");
93 static_assert(!std::is_constructible
<result
<std::string
>, result
<int>>::value
, "Sanity check that incompatible monads cannot be constructed from one another");
95 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
96 static_assert(std::is_constructible
<result
<int>, result
<void>>::value
, "Sanity check that all monads can be constructed from a void monad");
97 static_assert(std::is_constructible
<result
<result
<int>>, result
<void>>::value
, "Sanity check that outer monad can be constructed from a compatible monad");
98 #if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
99 static_assert(std::is_constructible
<result
<result
<result
<int>>>, result
<void>>::value
, "Sanity check that outer monad can be constructed from a compatible monad up to two nestings deep");
101 static_assert(!std::is_constructible
<result
<void>, result
<int>>::value
, "Sanity check that incompatible monads cannot be constructed from one another");
103 static_assert(std::is_void
<result
<void>::value_type
>::value
, "Sanity check that result<void> has a void value_type");
104 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
105 static_assert(std::is_void
<result
<void, void>::error_type
>::value
, "Sanity check that result<void, void> has a void error_type");
108 static_assert(std::is_same
<result
<int>::value_type
, int>::value
, "Sanity check that result<int> has a int value_type");
109 static_assert(std::is_same
<result
<int>::error_type
, boost::system::error_code
>::value
, "Sanity check that result<int> has a error_code error_type");
113 result
<int> m(boost::system::errc::bad_address
);
115 BOOST_CHECK(!m
.has_value());
116 BOOST_CHECK(m
.has_error());
117 // BOOST_CHECK(!m.has_exception());
118 BOOST_CHECK_THROW(m
.value(), boost::system::system_error
);
119 BOOST_CHECK_NO_THROW(m
.error());
122 result
<void> m(boost::system::errc::bad_address
);
124 BOOST_CHECK(!m
.has_value());
125 BOOST_CHECK(m
.has_error());
126 // BOOST_CHECK(!m.has_exception());
127 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
128 BOOST_CHECK_THROW(([&m
]() -> void { return m
.value(); }()), boost::system::system_error
);
130 BOOST_CHECK_NO_THROW(m
.error());
135 BOOST_CHECK(m
.has_value());
136 BOOST_CHECK(!m
.has_error());
137 // BOOST_CHECK(!m.has_exception());
138 BOOST_CHECK(m
.value() == 5);
140 BOOST_CHECK(m
.value() == 6);
141 BOOST_CHECK_THROW(m
.error(), bad_result_access
);
144 result
<bool> m(false);
146 BOOST_CHECK(m
.has_value());
147 BOOST_CHECK(!m
.has_error());
148 // BOOST_CHECK(!m.has_exception());
149 BOOST_CHECK(m
.value() == false);
151 BOOST_CHECK(m
.value() == true);
152 BOOST_CHECK_THROW(m
.error(), bad_result_access
);
154 { // moves do not clear state
155 result
<std::string
> m("niall");
157 BOOST_CHECK(m
.has_value());
158 BOOST_CHECK(!m
.has_error());
159 // BOOST_CHECK(!m.has_exception());
160 BOOST_CHECK(m
.value() == "niall");
162 BOOST_CHECK(m
.value() == "NIALL");
163 auto temp(std::move(m
).value());
164 BOOST_CHECK(temp
== "NIALL");
165 BOOST_CHECK(m
.value().empty()); // NOLINT
168 result
<void> m(in_place_type
<void>);
170 BOOST_CHECK(m
.has_value());
171 BOOST_CHECK(!m
.has_error());
172 // BOOST_CHECK(!m.has_exception());
173 BOOST_CHECK_NO_THROW(m
.value()); // works, but type returned is unusable
174 BOOST_CHECK_THROW(m
.error(), bad_result_access
);
177 boost::system::error_code
ec(5, boost::system::system_category());
180 BOOST_CHECK(!m
.has_value());
181 BOOST_CHECK(m
.has_error());
182 // BOOST_CHECK(!m.has_exception());
183 BOOST_CHECK_THROW(m
.value(), boost::system::system_error
);
184 BOOST_CHECK(m
.error() == ec
);
186 #if !defined(__APPLE__) || defined(__cpp_exceptions)
188 boost::system::error_code
ec(5, boost::system::system_category());
189 auto e
= boost::copy_exception(boost::system::system_error(ec
)); // NOLINT
190 result
<int, boost::exception_ptr
> m(e
);
192 BOOST_CHECK(!m
.has_value());
193 BOOST_CHECK(m
.has_error());
194 // BOOST_CHECK(!m.has_exception());
195 BOOST_CHECK_THROW(m
.value(), boost::system::system_error
);
196 BOOST_CHECK(m
.error() == e
);
199 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
200 { // custom error type
204 result
<int, Foo
> m(in_place_type
<Foo
>);
206 BOOST_CHECK(!m
.has_value());
207 BOOST_CHECK(m
.has_error());
208 // BOOST_CHECK(!m.has_exception());
209 // BOOST_CHECK_NO_THROW(m.value());
210 // BOOST_CHECK_NO_THROW(m.error());
213 { // void, void is permitted, but is not constructible
214 result
<void, void> *m
= nullptr;
221 // Deliberately define non-trivial operations
226 udt(udt
&&o
) noexcept
: _v(o
._v
) {}
227 udt(const udt
&o
) // NOLINT
231 udt
&operator=(udt
&&o
) noexcept
236 udt
&operator=(const udt
&o
) // NOLINT
243 // No default construction, no copy nor move
247 udt2(udt2
&&) = delete;
248 udt2(const udt2
&) = delete;
249 udt2
&operator=(udt2
&&) = delete;
250 udt2
&operator=(const udt2
&) = delete;
251 explicit udt2(int /*unused*/) {}
254 // Can only be constructed via multiple args
258 udt3(udt3
&&) = delete;
259 udt3(const udt3
&) = delete;
260 udt3
&operator=(udt3
&&) = delete;
261 udt3
&operator=(const udt3
&) = delete;
262 explicit udt3(int /*unused*/, const char * /*unused*/, std::nullptr_t
/*unused*/) {}
268 result
<int> b(make_error_code(boost::system::errc::invalid_argument
));
269 std::cout
<< sizeof(a
) << std::endl
; // 32 bytes
275 #ifndef BOOST_NO_EXCEPTIONS
279 std::cerr
<< "fail" << std::endl
;
282 catch(const boost::system::system_error
& /*unused*/)
286 static_assert(!std::is_default_constructible
<decltype(a
)>::value
, "");
287 static_assert(!std::is_nothrow_default_constructible
<decltype(a
)>::value
, "");
288 static_assert(std::is_copy_constructible
<decltype(a
)>::value
, "");
289 // Quality of implementation of std::optional is poor :(
290 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
291 static_assert(std::is_trivially_copy_constructible
<decltype(a
)>::value
, "");
292 static_assert(std::is_nothrow_copy_constructible
<decltype(a
)>::value
, "");
293 static_assert(std::is_copy_assignable
<decltype(a
)>::value
, "");
294 static_assert(std::is_trivially_copy_assignable
<decltype(a
)>::value
, "");
295 static_assert(std::is_nothrow_copy_assignable
<decltype(a
)>::value
, "");
297 static_assert(std::is_trivially_destructible
<decltype(a
)>::value
, "");
298 static_assert(std::is_nothrow_destructible
<decltype(a
)>::value
, "");
300 // Test void compiles
301 result
<void> c(in_place_type
<void>);
305 // Test a standard udt compiles
306 result
<udt
> d(in_place_type
<udt
>);
308 static_assert(!std::is_default_constructible
<decltype(d
)>::value
, "");
309 static_assert(!std::is_nothrow_default_constructible
<decltype(d
)>::value
, "");
310 static_assert(std::is_copy_constructible
<decltype(d
)>::value
, "");
311 static_assert(!std::is_trivially_copy_constructible
<decltype(d
)>::value
, "");
312 static_assert(!std::is_nothrow_copy_constructible
<decltype(d
)>::value
, "");
313 static_assert(std::is_copy_assignable
<decltype(d
)>::value
, "");
314 static_assert(!std::is_trivially_copy_assignable
<decltype(d
)>::value
, "");
315 static_assert(!std::is_nothrow_copy_assignable
<decltype(d
)>::value
, "");
316 static_assert(std::is_move_assignable
<decltype(d
)>::value
, "");
317 static_assert(!std::is_trivially_move_assignable
<decltype(d
)>::value
, "");
318 static_assert(std::is_nothrow_move_assignable
<decltype(d
)>::value
, "");
319 static_assert(!std::is_trivially_destructible
<decltype(d
)>::value
, "");
320 static_assert(std::is_nothrow_destructible
<decltype(d
)>::value
, "");
322 // Test a highly pathological udt compiles
323 result
<udt2
> e(in_place_type
<udt2
>, 5);
324 // result<udt2> e2(e);
325 static_assert(!std::is_default_constructible
<decltype(e
)>::value
, "");
326 static_assert(!std::is_nothrow_default_constructible
<decltype(e
)>::value
, "");
327 static_assert(!std::is_copy_constructible
<decltype(e
)>::value
, "");
328 static_assert(!std::is_trivially_copy_constructible
<decltype(e
)>::value
, "");
329 static_assert(!std::is_nothrow_copy_constructible
<decltype(e
)>::value
, "");
330 static_assert(!std::is_copy_assignable
<decltype(e
)>::value
, "");
331 static_assert(!std::is_trivially_copy_assignable
<decltype(e
)>::value
, "");
332 static_assert(!std::is_nothrow_copy_assignable
<decltype(e
)>::value
, "");
333 static_assert(!std::is_move_assignable
<decltype(e
)>::value
, "");
334 static_assert(!std::is_trivially_move_assignable
<decltype(e
)>::value
, "");
335 static_assert(!std::is_nothrow_move_assignable
<decltype(e
)>::value
, "");
337 // Test a udt which can only be constructed in place compiles
338 result
<udt3
> g(in_place_type
<udt3
>, 5, static_cast<const char *>("niall"), nullptr);
339 // Does converting inplace construction also work?
340 result
<udt3
> h(5, static_cast<const char *>("niall"), nullptr);
341 result
<udt3
> i(ENOMEM
, boost::system::generic_category());
342 BOOST_CHECK(h
.has_value());
343 BOOST_CHECK(i
.has_error());
346 // Test direct use of error code enum works
348 constexpr result
<int, boost::system::errc::errc_t
> a(5), b(boost::system::errc::invalid_argument
);
349 static_assert(a
.value() == 5, "a is not 5");
350 static_assert(b
.error() == boost::system::errc::invalid_argument
, "b is not errored");
351 BOOST_CHECK_THROW(b
.value(), boost::system::system_error
);
354 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
355 #ifndef BOOST_NO_EXCEPTIONS
356 // Test payload facility
358 const char *niall
= "niall";
359 result
<int, payload
> b
{boost::system::errc::invalid_argument
, niall
};
365 catch(const payload_exception
&e
)
367 BOOST_CHECK(!strcmp(e
.what(), niall
));