• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Unit testing for outcomes
2 (C) 2013-2020 Niall Douglas <http://www.nedproductions.biz/> (30 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 #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__)
33 #else
34 #include <boost/outcome/result.hpp>
35 #endif
36 #include <boost/test/unit_test.hpp>
37 #include <boost/test/unit_test_monitor.hpp>
38 
39 #include <iostream>
40 
41 #ifndef BOOST_NO_EXCEPTIONS
42 // Custom error type with payload
43 struct payload
44 {
45   boost::system::error_code ec;
46   const char *str{nullptr};
47   payload() = default;
payloadpayload48   payload(boost::system::errc::errc_t _ec, const char *_str)
49       : ec(make_error_code(_ec))
50       , str(_str)
51   {
52   }
53 };
54 struct payload_exception : std::runtime_error
55 {
payload_exceptionpayload_exception56   explicit payload_exception(const char *what)
57       : std::runtime_error(what)
58   {
59   }
60 };
make_error_code(const payload & p)61 inline const boost::system::error_code &make_error_code(const payload &p)
62 {
63   return p.ec;
64 }
outcome_throw_as_system_error_with_payload(const payload & p)65 inline void outcome_throw_as_system_error_with_payload(const payload &p)
66 {
67   throw payload_exception(p.str);
68 }
69 #endif
70 
71 BOOST_OUTCOME_AUTO_TEST_CASE(works_result, "Tests that the result works as intended")
72 {
73 #ifdef TESTING_WG21_EXPERIMENTAL_RESULT
74   using namespace std::experimental;
75   using std::in_place_type;
76 #else
77   using namespace BOOST_OUTCOME_V2_NAMESPACE;
78 #endif
79 
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");
85 #endif
86 
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");
92 #endif
93   static_assert(!std::is_constructible<result<std::string>, result<int>>::value, "Sanity check that incompatible monads cannot be constructed from one another");
94 
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");
100 #endif
101   static_assert(!std::is_constructible<result<void>, result<int>>::value, "Sanity check that incompatible monads cannot be constructed from one another");
102 #endif
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");
106 #endif
107 
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");
110 
111 
112   {  // errored int
113     result<int> m(boost::system::errc::bad_address);
114     BOOST_CHECK(!m);
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());
120   }
121   {  // errored void
122     result<void> m(boost::system::errc::bad_address);
123     BOOST_CHECK(!m);
124     BOOST_CHECK(!m.has_value());
125     BOOST_CHECK(m.has_error());
126 // BOOST_CHECK(!m.has_exception());
127 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
__anonda03bb350102() 128     BOOST_CHECK_THROW(([&m]() -> void { return m.value(); }()), boost::system::system_error);
129 #endif
130     BOOST_CHECK_NO_THROW(m.error());
131   }
132   {  // valued int
133     result<int> m(5);
134     BOOST_CHECK(m);
135     BOOST_CHECK(m.has_value());
136     BOOST_CHECK(!m.has_error());
137     // BOOST_CHECK(!m.has_exception());
138     BOOST_CHECK(m.value() == 5);
139     m.value() = 6;
140     BOOST_CHECK(m.value() == 6);
141     BOOST_CHECK_THROW(m.error(), bad_result_access);
142   }
143   {  // valued bool
144     result<bool> m(false);
145     BOOST_CHECK(m);
146     BOOST_CHECK(m.has_value());
147     BOOST_CHECK(!m.has_error());
148     // BOOST_CHECK(!m.has_exception());
149     BOOST_CHECK(m.value() == false);
150     m.value() = true;
151     BOOST_CHECK(m.value() == true);
152     BOOST_CHECK_THROW(m.error(), bad_result_access);
153   }
154   {  // moves do not clear state
155     result<std::string> m("niall");
156     BOOST_CHECK(m);
157     BOOST_CHECK(m.has_value());
158     BOOST_CHECK(!m.has_error());
159     // BOOST_CHECK(!m.has_exception());
160     BOOST_CHECK(m.value() == "niall");
161     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
166   }
167   {  // valued void
168     result<void> m(in_place_type<void>);
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_NO_THROW(m.value());  // works, but type returned is unusable
174     BOOST_CHECK_THROW(m.error(), bad_result_access);
175   }
176   {  // errored
177     boost::system::error_code ec(5, boost::system::system_category());
178     result<int> m(ec);
179     BOOST_CHECK(!m);
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);
185   }
186 #if !defined(__APPLE__) || defined(__cpp_exceptions)
187   {  // errored, custom
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);
191     BOOST_CHECK(!m);
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);
197   }
198 #endif
199 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
200   {  // custom error type
201     struct Foo
202     {
203     };
204     result<int, Foo> m(in_place_type<Foo>);
205     BOOST_CHECK(!m);
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());
211   }
212   if(false)  // NOLINT
213   {          // void, void is permitted, but is not constructible
214     result<void, void> *m = nullptr;
215     m->value();
216     m->error();
217   }
218 #endif
219 
220   {
221     // Deliberately define non-trivial operations
222     struct udt
223     {
224       int _v{0};
225       udt() = default;
udtudt226       udt(udt &&o) noexcept : _v(o._v) {}
udtudt227       udt(const udt &o)  // NOLINT
228       : _v(o._v)
229       {
230       }
operator =udt231       udt &operator=(udt &&o) noexcept
232       {
233         _v = o._v;
234         return *this;
235       }
operator =udt236       udt &operator=(const udt &o)  // NOLINT
237       {
238         _v = o._v;
239         return *this;
240       }
~udtudt241       ~udt() { _v = 0; }
242     };
243     // No default construction, no copy nor move
244     struct udt2
245     {
246       udt2() = delete;
247       udt2(udt2 &&) = delete;
248       udt2(const udt2 &) = delete;
249       udt2 &operator=(udt2 &&) = delete;
250       udt2 &operator=(const udt2 &) = delete;
udt2udt2251       explicit udt2(int /*unused*/) {}
252       ~udt2() = default;
253     };
254     // Can only be constructed via multiple args
255     struct udt3
256     {
257       udt3() = delete;
258       udt3(udt3 &&) = delete;
259       udt3(const udt3 &) = delete;
260       udt3 &operator=(udt3 &&) = delete;
261       udt3 &operator=(const udt3 &) = delete;
udt3udt3262       explicit udt3(int /*unused*/, const char * /*unused*/, std::nullptr_t /*unused*/) {}
263       ~udt3() = default;
264     };
265 
266 
267     result<int> a(5);
268     result<int> b(make_error_code(boost::system::errc::invalid_argument));
269     std::cout << sizeof(a) << std::endl;  // 32 bytes
270     if(false)                             // NOLINT
271     {
272       b.assume_value();
273       a.assume_error();
274     }
275 #ifndef BOOST_NO_EXCEPTIONS
276     try
277     {
278       b.value();
279       std::cerr << "fail" << std::endl;
280       std::terminate();
281     }
282     catch(const boost::system::system_error & /*unused*/)
283     {
284     }
285 #endif
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, "");
296 #endif
297     static_assert(std::is_trivially_destructible<decltype(a)>::value, "");
298     static_assert(std::is_nothrow_destructible<decltype(a)>::value, "");
299 
300     // Test void compiles
301     result<void> c(in_place_type<void>);
302     result<void> c2(c);
303     (void) c2;
304 
305     // Test a standard udt compiles
306     result<udt> d(in_place_type<udt>);
307     result<udt> d2(d);
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, "");
321 
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, "");
336 
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());
344   }
345 
346   // Test direct use of error code enum works
347   {
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);
352   }
353 
354 #ifndef TESTING_WG21_EXPERIMENTAL_RESULT
355 #ifndef BOOST_NO_EXCEPTIONS
356   // Test payload facility
357   {
358     const char *niall = "niall";
359     result<int, payload> b{boost::system::errc::invalid_argument, niall};
360     try
361     {
362       b.value();
363       BOOST_CHECK(false);
364     }
365     catch(const payload_exception &e)
366     {
367       BOOST_CHECK(!strcmp(e.what(), niall));
368     }
369     catch(...)
370     {
371       BOOST_CHECK(false);
372     }
373   }
374 #endif
375 #endif
376 }
377