1/* Unit testing for outcomes
2(C) 2013-2024 Niall Douglas <http://www.nedproductions.biz/> (18 commits)
3
4
5Boost Software License - Version 1.0 - August 17th, 2003
6
7Permission is hereby granted, free of charge, to any person or organization
8obtaining a copy of the software and accompanying documentation covered by
9this license (the "Software") to use, reproduce, display, distribute,
10execute, and transmit the Software, and to prepare derivative works of the
11Software, and to permit third-parties to whom the Software is furnished to
12do so, all subject to the following:
13
14The copyright notices in the Software and this entire statement, including
15the above license grant, this restriction and the following disclaimer,
16must be included in all copies of the Software, in whole or in part, and
17all derivative works of the Software, unless such copies or derivative
18works are solely in the form of machine-executable object code generated by
19a source language processor.
20
21THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27DEALINGS 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#include <iostream>
35
36#ifdef _MSC_VER
37#pragma warning(disable : 4702) // unreachable code
38#endif
39
40BOOST_OUTCOME_AUTO_TEST_CASE(works_outcome, "Tests that the outcome works as intended")
41{
42 using namespace BOOST_OUTCOME_V2_NAMESPACE;
43
44 static_assert(std::is_constructible<outcome<long>, int>::value, "Sanity check that monad can be constructed from a value_type");
45 static_assert(!std::is_constructible<outcome<outcome<long>>, int>::value,
46 "Sanity check that outer monad can be constructed from an inner monad's value_type");
47#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
48 static_assert(!std::is_constructible<outcome<outcome<outcome<long>>>, int>::value,
49 "Sanity check that outer monad can be constructed from an inner inner monad's value_type");
50 static_assert(!std::is_constructible<outcome<outcome<outcome<outcome<long>>>>, int>::value,
51 "Sanity check that outer monad can be constructed from an inner inner monad's value_type");
52#endif
53
54 static_assert(std::is_constructible<outcome<int>, outcome<long>>::value, "Sanity check that compatible monads can be constructed from one another");
55 static_assert(std::is_constructible<outcome<outcome<int>>, outcome<long>>::value, "Sanity check that outer monad can be constructed from a compatible monad");
56#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
57 static_assert(!std::is_constructible<outcome<outcome<outcome<int>>>, outcome<long>>::value,
58 "Sanity check that outer monad can be constructed from a compatible monad up to two nestings deep");
59 static_assert(!std::is_constructible<outcome<outcome<outcome<outcome<int>>>>, outcome<long>>::value,
60 "Sanity check that outer monad can be constructed from a compatible monad three or more nestings deep");
61#endif
62 static_assert(!std::is_constructible<outcome<std::string>, outcome<int>>::value,
63 "Sanity check that incompatible monads cannot be constructed from one another");
64
65 static_assert(std::is_constructible<outcome<int>, outcome<void>>::value, "Sanity check that all monads can be constructed from a void monad");
66 static_assert(std::is_constructible<outcome<outcome<int>>, outcome<void>>::value, "Sanity check that outer monad can be constructed from a compatible monad");
67#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 9 // GCCs before 9 barf on this
68 static_assert(std::is_constructible<outcome<outcome<outcome<int>>>, outcome<void>>::value,
69 "Sanity check that outer monad can be constructed from a compatible monad up to two nestings deep");
70#endif
71 static_assert(!std::is_constructible<outcome<void>, outcome<int>>::value, "Sanity check that incompatible monads cannot be constructed from one another");
72
73 static_assert(std::is_void<result<void>::value_type>::value, "Sanity check that result<void> has a void value_type");
74 static_assert(std::is_void<result<void, void>::error_type>::value, "Sanity check that result<void, void> has a void error_type");
75 // static_assert(std::is_void<outcome<void, void, void>::exception_type>::value, "Sanity check that outcome<void, void, void> has a void exception_type");
76
77 static_assert(std::is_same<outcome<int>::value_type, int>::value, "Sanity check that outcome<int> has a int value_type");
78 static_assert(std::is_same<outcome<int>::error_type, boost::system::error_code>::value, "Sanity check that outcome<int> has a error_code error_type");
79 static_assert(std::is_same<outcome<int>::exception_type, boost::exception_ptr>::value, "Sanity check that outcome<int> has a exception_ptr exception_type");
80
81
82 { // errored int
83 outcome<int> m(boost::system::errc::bad_address);
84 BOOST_CHECK(!m);
85 BOOST_CHECK(!m.has_value());
86 BOOST_CHECK(m.has_error());
87 BOOST_CHECK(!m.has_exception());
88 BOOST_CHECK_THROW(m.value(), boost::system::system_error);
89 BOOST_CHECK_NO_THROW(m.error());
90 BOOST_CHECK_THROW(m.exception(), bad_outcome_access);
91 BOOST_CHECK_THROW(boost::rethrow_exception(m.failure()), boost::system::system_error);
92 }
93 { // errored void
94 outcome<void> m(boost::system::errc::bad_address);
95 BOOST_CHECK(!m);
96 BOOST_CHECK(!m.has_value());
97 BOOST_CHECK(m.has_error());
98 BOOST_CHECK(!m.has_exception());
99 BOOST_CHECK_THROW(([&m]() -> void { return m.value(); }()), boost::system::system_error);
100 BOOST_CHECK_NO_THROW(m.error());
101 BOOST_CHECK_THROW(m.exception(), bad_outcome_access);
102 BOOST_CHECK_THROW(boost::rethrow_exception(m.failure()), boost::system::system_error);
103 }
104 { // valued int
105 outcome<int> m(5);
106 BOOST_CHECK(m);
107 BOOST_CHECK(m.has_value());
108 BOOST_CHECK(!m.has_error());
109 BOOST_CHECK(!m.has_exception());
110 BOOST_CHECK(m.value() == 5);
111 m.value() = 6;
112 BOOST_CHECK(m.value() == 6);
113 BOOST_CHECK_THROW(m.error(), bad_outcome_access);
114 BOOST_CHECK_THROW(m.exception(), bad_outcome_access);
115 BOOST_CHECK(!m.failure());
116 }
117 { // moves do not clear state
118 outcome<std::string> m("niall");
119 BOOST_CHECK(m);
120 BOOST_CHECK(m.has_value());
121 BOOST_CHECK(!m.has_error());
122 BOOST_CHECK(!m.has_exception());
123 BOOST_CHECK(m.value() == "niall");
124 m.value() = "NIALL";
125 BOOST_CHECK(m.value() == "NIALL");
126 auto temp(std::move(m).value());
127 BOOST_CHECK(temp == "NIALL");
128 BOOST_CHECK(m.value().empty()); // NOLINT
129 }
130 { // valued void
131 outcome<void> m(in_place_type<void>);
132 BOOST_CHECK(m);
133 BOOST_CHECK(m.has_value());
134 BOOST_CHECK(!m.has_error());
135 BOOST_CHECK(!m.has_exception());
136 BOOST_CHECK_NO_THROW(m.value()); // works, but type returned is unusable
137 BOOST_CHECK_THROW(m.error(), bad_outcome_access);
138 BOOST_CHECK_THROW(m.exception(), bad_outcome_access);
139 BOOST_CHECK(!m.failure());
140 }
141 { // errored
142 boost::system::error_code ec(5, boost::system::system_category());
143 outcome<int> m(ec);
144 BOOST_CHECK(!m);
145 BOOST_CHECK(!m.has_value());
146 BOOST_CHECK(m.has_error());
147 BOOST_CHECK(!m.has_exception());
148 BOOST_CHECK_THROW(m.value(), boost::system::system_error);
149 BOOST_CHECK(m.error() == ec);
150 BOOST_CHECK_THROW(m.exception(), bad_outcome_access);
151#ifndef BOOST_NO_EXCEPTIONS
152 BOOST_CHECK(m.failure());
153 try
154 {
155 boost::rethrow_exception(p: m.failure());
156 }
157 catch(const boost::system::system_error &ex)
158 {
159 BOOST_CHECK(ex.code() == ec);
160 BOOST_CHECK(ex.code().value() == 5);
161 }
162#endif
163 }
164#if !defined(__APPLE__) || defined(__cpp_exceptions)
165 { // excepted
166 boost::system::error_code ec(5, boost::system::system_category());
167 auto e = boost::copy_exception(e: boost::system::system_error(ec)); // NOLINT
168 outcome<int> m(e);
169 BOOST_CHECK(!m);
170 BOOST_CHECK(!m.has_value());
171 BOOST_CHECK(!m.has_error());
172 BOOST_CHECK(m.has_exception());
173 BOOST_CHECK_THROW(m.value(), boost::system::system_error);
174 BOOST_CHECK_THROW(m.error(), bad_outcome_access);
175 BOOST_CHECK(m.exception() == e);
176#ifndef BOOST_NO_EXCEPTIONS
177 BOOST_CHECK(m.failure());
178 try
179 {
180 boost::rethrow_exception(p: m.failure());
181 }
182 catch(const boost::system::system_error &ex)
183 {
184 BOOST_CHECK(ex.code() == ec);
185 BOOST_CHECK(ex.code().value() == 5);
186 }
187#endif
188 }
189 { // custom error type
190 struct Foo
191 {
192 };
193 auto e = boost::copy_exception(e: Foo());
194 outcome<int> m(e);
195 BOOST_CHECK(!m);
196 BOOST_CHECK(!m.has_value());
197 BOOST_CHECK(!m.has_error());
198 BOOST_CHECK(m.has_exception());
199 BOOST_CHECK_THROW(m.value(), Foo);
200 BOOST_CHECK_THROW(m.error(), bad_outcome_access);
201 BOOST_CHECK(m.exception() == e);
202 }
203 { // outcome<void, void> should work
204 boost::system::error_code ec(5, boost::system::system_category());
205 auto e = boost::copy_exception(e: boost::system::system_error(ec));
206 outcome<void, void> m(e);
207 BOOST_CHECK(!m);
208 BOOST_CHECK(!m.has_value());
209 BOOST_CHECK(!m.has_error());
210 BOOST_CHECK(m.has_exception());
211 }
212#endif
213
214
215 {
216 outcome<int> a(5);
217 outcome<int> b(make_error_code(e: boost::system::errc::invalid_argument));
218 std::cout << sizeof(a) << std::endl; // 40 bytes
219 a.assume_value();
220 b.assume_error();
221#ifndef BOOST_NO_EXCEPTIONS
222 try
223 {
224 b.value();
225 std::cerr << "fail" << std::endl;
226 std::terminate();
227 }
228 catch(const boost::system::system_error & /*unused*/)
229 {
230 }
231#endif
232 static_assert(!std::is_default_constructible<decltype(a)>::value, "");
233 static_assert(!std::is_nothrow_default_constructible<decltype(a)>::value, "");
234 static_assert(std::is_copy_constructible<decltype(a)>::value, "");
235 static_assert(!std::is_trivially_copy_constructible<decltype(a)>::value, "");
236 static_assert(std::is_nothrow_copy_constructible<decltype(a)>::value, "");
237 static_assert(std::is_copy_assignable<decltype(a)>::value, "");
238 static_assert(!std::is_trivially_copy_assignable<decltype(a)>::value, "");
239 static_assert(std::is_nothrow_copy_assignable<decltype(a)>::value, "");
240 static_assert(!std::is_trivially_destructible<decltype(a)>::value, "");
241 static_assert(std::is_nothrow_destructible<decltype(a)>::value, "");
242
243 // Test void compiles
244 outcome<void> c(in_place_type<void>);
245 outcome<void> c2(c);
246 (void) c2;
247 // Test int, void compiles
248 outcome<int, void> d(in_place_type<boost::exception_ptr>);
249 }
250
251 {
252 // Can only be constructed via multiple args
253 struct udt3
254 {
255 udt3() = delete;
256 udt3(udt3 &&) = delete;
257 udt3(const udt3 &) = delete;
258 udt3 &operator=(udt3 &&) = delete;
259 udt3 &operator=(const udt3 &) = delete;
260 explicit udt3(int /*unused*/, const char * /*unused*/, std::nullptr_t /*unused*/) {}
261 ~udt3() = default;
262 };
263 // Test a udt which can only be constructed in place compiles
264 outcome<udt3> g(in_place_type<udt3>, 5, static_cast<const char *>("niall"), nullptr);
265 // Does converting inplace construction also work?
266 outcome<udt3> h(5, static_cast<const char *>("niall"), nullptr);
267 outcome<udt3> i(ENOMEM, boost::system::generic_category());
268 BOOST_CHECK(h.has_value());
269 BOOST_CHECK(i.has_error());
270 }
271}
272

source code of boost/libs/outcome/test/tests/core-outcome.cpp