1 /* Unit testing for outcomes
2 (C) 2013-2020 Niall Douglas <http://www.nedproductions.biz/> (8 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/experimental/status_result.hpp>
31
32 template <class T, class S = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::system_code, class NoValuePolicy = BOOST_OUTCOME_V2_NAMESPACE::experimental::policy::default_status_result_policy<T, S>> using result = BOOST_OUTCOME_V2_NAMESPACE::experimental::status_result<T, S, NoValuePolicy>;
33 using BOOST_OUTCOME_V2_NAMESPACE::in_place_type;
34
35 #include <boost/test/unit_test.hpp>
36 #include <boost/test/unit_test_monitor.hpp>
37
38 #include <exception>
39 #include <iostream>
40
41 #ifndef BOOST_NO_EXCEPTIONS
42 // Custom error type with payload
43 struct payload
44 {
45 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc ec{BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc::success};
46 const char *str{nullptr};
47 payload() = default;
payloadpayload48 payload(BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc _ec, const char *_str)
49 : ec(_ec)
50 , str(_str)
51 {
52 }
53 };
54 struct payload_exception : std::exception
55 {
56 const char *_what{nullptr};
payload_exceptionpayload_exception57 explicit payload_exception(const char *what)
58 : _what(what)
59 {
60 }
whatpayload_exception61 virtual const char *what() const noexcept override final { return _what; } // NOLINT
62 };
63
64 class _payload_domain;
65 using status_code_payload = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<_payload_domain>;
66 class _payload_domain : public BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code_domain
67 {
68 template <class> friend class status_code;
69 using _base = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code_domain;
70
71 public:
72 using value_type = payload;
73 using string_ref = _base::string_ref;
74
75 public:
_payload_domain()76 constexpr _payload_domain() noexcept : _base(0x7b782c8f935e34ba) {}
77
78 static inline constexpr const _payload_domain &get();
79
name() const80 virtual _base::string_ref name() const noexcept override final { return string_ref("payload domain"); } // NOLINT
81 protected:
_do_failure(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code) const82 virtual bool _do_failure(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
83 {
84 assert(code.domain() == *this); // NOLINT
85 return static_cast<const status_code_payload &>(code).value().ec != BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::errc::success; // NOLINT
86 }
_do_equivalent(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code1,const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code2) const87 virtual bool _do_equivalent(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code1, const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code2) const noexcept override final // NOLINT
88 {
89 assert(code1.domain() == *this); // NOLINT
90 const auto &c1 = static_cast<const status_code_payload &>(code1); // NOLINT
91 if(code2.domain() == *this)
92 {
93 const auto &c2 = static_cast<const status_code_payload &>(code2); // NOLINT
94 return c1.value().ec == c2.value().ec;
95 }
96 return false;
97 }
_generic_code(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code) const98 virtual BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::generic_code _generic_code(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
99 {
100 assert(code.domain() == *this); // NOLINT
101 return static_cast<const status_code_payload &>(code).value().ec; // NOLINT
102 }
_do_message(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code) const103 virtual _base::string_ref _do_message(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const noexcept override final // NOLINT
104 {
105 assert(code.domain() == *this); // NOLINT
106 const auto &c = static_cast<const status_code_payload &>(code); // NOLINT
107 return string_ref(BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::detail::generic_code_message(c.value().ec));
108 }
_do_throw_exception(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> & code) const109 virtual void _do_throw_exception(const BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::status_code<void> &code) const override final // NOLINT
110 {
111 assert(code.domain() == *this); // NOLINT
112 const auto &c = static_cast<const status_code_payload &>(code); // NOLINT
113 throw payload_exception(c.value().str);
114 }
115 };
116 constexpr _payload_domain payload_domain;
get()117 inline constexpr const _payload_domain &_payload_domain::get()
118 {
119 return payload_domain;
120 }
make_status_code(payload c)121 inline status_code_payload make_status_code(payload c) noexcept
122 {
123 return status_code_payload(BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::in_place, c);
124 }
125 #endif
126
127 BOOST_OUTCOME_AUTO_TEST_CASE(works_status_code_result, "Tests that the result with status_code works as intended")
128 {
129 using namespace BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE;
130
131 { // errored int
132 result<int> m(generic_code{errc::bad_address});
133 BOOST_CHECK(!m);
134 BOOST_CHECK(!m.has_value());
135 BOOST_CHECK(m.has_error());
136 // BOOST_CHECK(!m.has_exception());
137 BOOST_CHECK_THROW(m.value(), generic_error);
138 BOOST_CHECK_NO_THROW(m.error());
139 }
140 { // errored void
141 result<void> m(generic_code{errc::bad_address});
142 BOOST_CHECK(!m);
143 BOOST_CHECK(!m.has_value());
144 BOOST_CHECK(m.has_error());
145 // BOOST_CHECK(!m.has_exception());
__anon13ec391f0102() 146 BOOST_CHECK_THROW(([&m]() -> void { return m.value(); }()), generic_error);
147 BOOST_CHECK_NO_THROW(m.error());
148 }
149 { // valued int
150 result<int> m(5);
151 BOOST_CHECK(m);
152 BOOST_CHECK(m.has_value());
153 BOOST_CHECK(!m.has_error());
154 // BOOST_CHECK(!m.has_exception());
155 BOOST_CHECK(m.value() == 5);
156 m.value() = 6;
157 BOOST_CHECK(m.value() == 6);
158 }
159 { // valued bool
160 result<bool> m(false);
161 BOOST_CHECK(m);
162 BOOST_CHECK(m.has_value());
163 BOOST_CHECK(!m.has_error());
164 // BOOST_CHECK(!m.has_exception());
165 BOOST_CHECK(m.value() == false);
166 m.value() = true;
167 BOOST_CHECK(m.value() == true);
168 }
169 { // moves do not clear state
170 result<std::string> m("niall");
171 BOOST_CHECK(m);
172 BOOST_CHECK(m.has_value());
173 BOOST_CHECK(!m.has_error());
174 // BOOST_CHECK(!m.has_exception());
175 BOOST_CHECK(m.value() == "niall");
176 m.value() = "NIALL";
177 BOOST_CHECK(m.value() == "NIALL");
178 auto temp(std::move(m).value());
179 BOOST_CHECK(temp == "NIALL");
180 BOOST_CHECK(m.value().empty()); // NOLINT
181 }
182 { // valued void
183 result<void> m(in_place_type<void>);
184 BOOST_CHECK(m);
185 BOOST_CHECK(m.has_value());
186 BOOST_CHECK(!m.has_error());
187 // BOOST_CHECK(!m.has_exception());
188 BOOST_CHECK_NO_THROW(m.value()); // works, but type returned is unusable
189 }
190 { // errored
191 error ec(errc::no_link);
192 result<int> m(ec.clone());
193 BOOST_CHECK(!m);
194 BOOST_CHECK(!m.has_value());
195 BOOST_CHECK(m.has_error());
196 // BOOST_CHECK(!m.has_exception());
197 BOOST_CHECK_THROW(m.value(), generic_error);
198 BOOST_CHECK(m.error() == ec);
199 }
200 if(false) // NOLINT
201 { // void, void is permitted, but is not constructible
202 result<void, void> *m = nullptr;
203 m->value();
204 m->error();
205 }
206
207 {
208 // Deliberately define non-trivial operations
209 struct udt
210 {
211 int _v{0};
212 udt() = default;
udtudt213 udt(udt &&o) noexcept : _v(o._v) {}
udtudt214 udt(const udt &o) // NOLINT
215 : _v(o._v)
216 {
217 }
operator =udt218 udt &operator=(udt &&o) noexcept
219 {
220 _v = o._v;
221 return *this;
222 }
operator =udt223 udt &operator=(const udt &o) // NOLINT
224 {
225 _v = o._v;
226 return *this;
227 }
~udtudt228 ~udt() { _v = 0; }
229 };
230 // No default construction, no copy nor move
231 struct udt2
232 {
233 udt2() = delete;
234 udt2(udt2 &&) = delete;
235 udt2(const udt2 &) = delete;
236 udt2 &operator=(udt2 &&) = delete;
237 udt2 &operator=(const udt2 &) = delete;
udt2udt2238 explicit udt2(int /*unused*/) {}
239 ~udt2() = default;
240 };
241 // Can only be constructed via multiple args
242 struct udt3
243 {
244 udt3() = delete;
245 udt3(udt3 &&) = delete;
246 udt3(const udt3 &) = delete;
247 udt3 &operator=(udt3 &&) = delete;
248 udt3 &operator=(const udt3 &) = delete;
udt3udt3249 explicit udt3(int /*unused*/, const char * /*unused*/, std::nullptr_t /*unused*/) {}
250 ~udt3() = default;
251 };
252
253 result<int> a(5);
254 result<int> b(generic_code{errc::invalid_argument});
255 std::cout << sizeof(a) << std::endl; // 32 bytes
256 if(false) // NOLINT
257 {
258 b.assume_value();
259 a.assume_error();
260 }
261 #ifndef BOOST_NO_EXCEPTIONS
262 try
263 {
264 b.value();
265 std::cerr << "fail" << std::endl;
266 std::terminate();
267 }
268 catch(const generic_error &e)
269 {
270 BOOST_CHECK(!strcmp(e.what(), b.error().message().c_str()));
271 }
272 #endif
273 static_assert(!std::is_default_constructible<decltype(a)>::value, "");
274 static_assert(!std::is_nothrow_default_constructible<decltype(a)>::value, "");
275 static_assert(!std::is_copy_constructible<decltype(a)>::value, "");
276 // Quality of implementation of std::optional is poor :(
277 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
278 static_assert(!std::is_trivially_copy_constructible<decltype(a)>::value, "");
279 static_assert(!std::is_nothrow_copy_constructible<decltype(a)>::value, "");
280 static_assert(!std::is_copy_assignable<decltype(a)>::value, "");
281 static_assert(!std::is_trivially_copy_assignable<decltype(a)>::value, "");
282 static_assert(!std::is_nothrow_copy_assignable<decltype(a)>::value, "");
283 #endif
284 static_assert(!std::is_trivially_destructible<decltype(a)>::value, "");
285 static_assert(std::is_nothrow_destructible<decltype(a)>::value, "");
286
287 // Test void compiles
288 result<void> c(in_place_type<void>);
289
290 // Test a standard udt compiles
291 result<udt> d(in_place_type<udt>);
292 static_assert(!std::is_default_constructible<decltype(d)>::value, "");
293 static_assert(!std::is_nothrow_default_constructible<decltype(d)>::value, "");
294 static_assert(!std::is_copy_constructible<decltype(d)>::value, "");
295 static_assert(!std::is_trivially_copy_constructible<decltype(d)>::value, "");
296 static_assert(!std::is_nothrow_copy_constructible<decltype(d)>::value, "");
297 static_assert(!std::is_copy_assignable<decltype(d)>::value, "");
298 static_assert(!std::is_trivially_copy_assignable<decltype(d)>::value, "");
299 static_assert(!std::is_nothrow_copy_assignable<decltype(d)>::value, "");
300 static_assert(std::is_move_assignable<decltype(d)>::value, "");
301 static_assert(!std::is_trivially_move_assignable<decltype(d)>::value, "");
302 static_assert(std::is_nothrow_move_assignable<decltype(d)>::value, "");
303 static_assert(!std::is_trivially_destructible<decltype(d)>::value, "");
304 static_assert(std::is_nothrow_destructible<decltype(d)>::value, "");
305
306 // Test a highly pathological udt compiles
307 result<udt2> e(in_place_type<udt2>, 5);
308 // result<udt2> e2(e);
309 static_assert(!std::is_default_constructible<decltype(e)>::value, "");
310 static_assert(!std::is_nothrow_default_constructible<decltype(e)>::value, "");
311 static_assert(!std::is_copy_constructible<decltype(e)>::value, "");
312 static_assert(!std::is_trivially_copy_constructible<decltype(e)>::value, "");
313 static_assert(!std::is_nothrow_copy_constructible<decltype(e)>::value, "");
314 static_assert(!std::is_copy_assignable<decltype(e)>::value, "");
315 static_assert(!std::is_trivially_copy_assignable<decltype(e)>::value, "");
316 static_assert(!std::is_nothrow_copy_assignable<decltype(e)>::value, "");
317 static_assert(!std::is_move_assignable<decltype(e)>::value, "");
318 static_assert(!std::is_trivially_move_assignable<decltype(e)>::value, "");
319 static_assert(!std::is_nothrow_move_assignable<decltype(e)>::value, "");
320
321 // Test a udt which can only be constructed in place compiles
322 result<udt3> g(in_place_type<udt3>, 5, static_cast<const char *>("niall"), nullptr);
323 // Does converting inplace construction also work?
324 result<udt3> h(5, static_cast<const char *>("niall"), nullptr);
325 result<udt3> i(generic_code{errc::not_enough_memory});
326 BOOST_CHECK(h.has_value());
327 BOOST_CHECK(i.has_error());
328 }
329
330 // Test direct use of error code enum works
331 {
332 constexpr result<int, errc, BOOST_OUTCOME_V2_NAMESPACE::policy::all_narrow> a(5), b(errc::invalid_argument);
333 static_assert(a.value() == 5, "a is not 5");
334 static_assert(b.error() == errc::invalid_argument, "b is not errored");
335 }
336
337 #ifndef BOOST_NO_EXCEPTIONS
338 // Test payload facility
339 {
340 const char *niall = "niall";
341 result<int, status_code_payload> b{payload{errc::invalid_argument, niall}};
342 try
343 {
344 b.value();
345 BOOST_CHECK(false);
346 }
347 catch(const payload_exception &e)
348 {
349 BOOST_CHECK(!strcmp(e.what(), niall));
350 }
351 catch(...)
352 {
353 BOOST_CHECK(false);
354 }
355 }
356 #endif
357 }
358