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