• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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