• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Unit testing for outcomes
2 (C) 2013-2020 Niall Douglas <http://www.nedproductions.biz/> (18 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/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 
40 BOOST_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());
__anonbfe3fb300102() 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(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(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(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(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(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(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;
udt3udt3260       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