1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors. 2 // https://github.com/Dobiasd/FunctionalPlus 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 #include <doctest/doctest.h> 8 #include <fplus/fplus.hpp> 9 #include <vector> 10 11 namespace { 12 13 auto sqrtToResult = [](auto x) __anonf9e3fb340202(auto x) 14 { 15 return x < 0.0f ? fplus::error<float>(std::string("no sqrt of negative numbers")) : 16 fplus::ok<float, std::string>(static_cast<float>(sqrt(static_cast<float>(x)))); 17 }; 18 19 auto sqrtToResultInt = [](auto x) __anonf9e3fb340302(auto x) 20 { 21 return x < 0 ? fplus::error<int>(std::string("no sqrt of negative numbers")) : 22 fplus::ok<int, std::string>(fplus::round(sqrt(static_cast<float>(x)))); 23 }; 24 IntToFloat(const int & x)25 float IntToFloat(const int& x) 26 { 27 return static_cast<float>(x); 28 } 29 30 typedef std::vector<fplus::result<int, std::string>> IntResults; 31 typedef std::vector<int> Ints; 32 typedef std::vector<std::string> Strings; 33 } 34 35 class resultTestState { 36 public: resultTestState(int x)37 explicit resultTestState(int x) : x_(x) {} Add(int y)38 void Add(int y) { x_ += y; } Get() const39 int Get() const { return x_; } 40 private: 41 int x_; 42 }; 43 44 TEST_CASE("result_test - ok_with_default") 45 { 46 using namespace fplus; 47 auto x = ok<int, std::string>(2); 48 auto y = error<int, std::string>("an error"); 49 auto Or42 = bind_1st_of_2(ok_with_default<int, std::string>, 42); 50 REQUIRE_EQ(Or42(x), 2); 51 REQUIRE_EQ(Or42(y), 42); 52 } 53 54 TEST_CASE("maybe_test - join_result") 55 { 56 using namespace fplus; 57 using Ok = int; 58 using Err = std::string; 59 using Res = result<Ok, Err>; 60 REQUIRE_EQ(join_result(ok<Res, Err>(ok<Ok, Err>(2))), (ok<Ok, Err>(2))); 61 REQUIRE_EQ(join_result(ok<Res, Err>(error<Ok, Err>("e"))), (error<Ok, Err>("e"))); 62 REQUIRE_EQ(join_result(error<Res, Err>("e")), (error<Ok, Err>("e"))); 63 } 64 65 TEST_CASE("result_test - and_then_result") 66 { 67 using namespace fplus; 68 auto ok_4 = ok<int, std::string>(4); 69 auto ok_2 = ok<int, std::string>(2); 70 auto an_error = error<int, std::string>("an error"); 71 REQUIRE_EQ(and_then_result(sqrtToResultInt, ok_4), ok_2); 72 REQUIRE_EQ(and_then_result(sqrtToResultInt, an_error), an_error); 73 __anonf9e3fb340402(const auto& str) 74 const auto string_to_result_int_string = [](const auto& str) { 75 if (str == "42") 76 return ok<int, std::string>(42); 77 else 78 return error<int, std::string>("not 42"); 79 }; 80 REQUIRE_EQ(and_then_result(string_to_result_int_string, (ok<std::string, std::string>("3"))), (error<int, std::string>("not 42"))); 81 REQUIRE_EQ(and_then_result(string_to_result_int_string, (ok<std::string, std::string>("42"))), (ok<int, std::string>(42))); 82 REQUIRE_EQ(and_then_result(string_to_result_int_string, (error<std::string, std::string>("error"))), (error<int, std::string>("error"))); 83 } 84 85 TEST_CASE("result_test - compose_result") 86 { 87 using namespace fplus; 88 auto x = ok<int, std::string>(2); 89 auto y = error<int, std::string>("an error"); 90 auto sqrtAndSqrt = compose_result(sqrtToResult, sqrtToResult); 91 REQUIRE_EQ(lift_result(square<int>, x), (ok<int, std::string>(4))); 92 REQUIRE_EQ(lift_result(square<int>, y), (error<int>(std::string("an error")))); 93 94 auto sqrtIntAndSqrtIntAndSqrtInt = compose_result(sqrtToResultInt, sqrtToResultInt, sqrtToResultInt); 95 REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtInt(256), (ok<int, std::string>(2))); 96 auto sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt = compose_result(sqrtToResultInt, sqrtToResultInt, sqrtToResultInt, sqrtToResultInt); 97 REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt(65536), (ok<int, std::string>(2))); 98 99 const auto LiftedIntToFloat = 100 [](const result<int, std::string>& r) -> result<float, std::string> __anonf9e3fb340502(const result<int, std::string>& r) 101 { 102 return lift_result(IntToFloat, r); 103 }; 104 auto OkInt = ok<int, std::string>; 105 auto IntToResultFloat = compose(OkInt, LiftedIntToFloat); 106 auto IntToFloatAndSqrtAndSqrt = compose_result(IntToResultFloat, sqrtAndSqrt); 107 REQUIRE(is_in_interval(1.41f, 1.42f, unsafe_get_ok<float> 108 (IntToFloatAndSqrtAndSqrt(4)))); 109 110 // first callable can take a variadic number of arguments __anonf9e3fb340602(auto a, auto b) 111 auto sumToResult = [](auto a, auto b) { 112 return ok<decltype(a + b), std::string>(a + b); 113 }; __anonf9e3fb340702(auto sum) 114 auto squareSumResult = compose_result(sumToResult, [](auto sum) { 115 return ok<decltype(sum * sum), std::string>(sum * sum); 116 }); 117 118 REQUIRE_EQ(squareSumResult(5, 5), (ok<int, std::string>(100))); 119 } 120 121 TEST_CASE("result_test - lift") 122 { 123 using namespace fplus; 124 auto x = ok<int, std::string>(2); 125 auto y = error<int, std::string>("an error"); 126 auto SquareAndSquare = compose(square<int>, square<int>); 127 REQUIRE_EQ((lift_result(SquareAndSquare, x)), (ok<int, std::string>(16))); __anonf9e3fb340802(auto n) 128 REQUIRE_EQ((lift_result([](auto n) { return square(n) * square(n); }, x)), 129 (ok<int, std::string>(16))); 130 } 131 132 TEST_CASE("result_test - lift_both") 133 { 134 using namespace fplus; 135 const auto x = ok<int, std::string>(2); 136 const auto y = error<int, std::string>("an error"); 137 REQUIRE_EQ(lift_result_both(square<int>, to_upper_case<std::string>, x), (ok<int, std::string>(4))); 138 REQUIRE_EQ(lift_result_both(square<int>, to_upper_case<std::string>, y), (error<int, std::string>("AN ERROR"))); __anonf9e3fb340902(auto z) 139 REQUIRE_EQ(lift_result_both([](auto z) { return z * z; }, __anonf9e3fb340a02(auto z) 140 [](auto z) { return to_upper_case(z); }, 141 y), 142 (error<int, std::string>("AN ERROR"))); 143 } 144 145 TEST_CASE("result_test - unify_result") 146 { 147 using namespace fplus; 148 const auto x = ok<int, std::string>(2); 149 const auto y = error<int, std::string>("an error"); 150 const auto unify = [](const result<int, std::string>& r) -> std::string __anonf9e3fb340b02(const result<int, std::string>& r) 151 { 152 return unify_result(show<int>, to_upper_case<std::string>, r); 153 }; 154 REQUIRE_EQ(unify(x), "2"); 155 REQUIRE_EQ(unify(y), "AN ERROR"); 156 __anonf9e3fb340c02(auto r) 157 const auto unifyGeneric = [](auto r) { 158 return unify_result(show<typename decltype(r)::ok_t>, 159 to_upper_case<typename decltype(r)::error_t>, 160 r); 161 }; 162 REQUIRE_EQ(unifyGeneric(x), "2"); 163 REQUIRE_EQ(unifyGeneric(y), "AN ERROR"); 164 } 165 166 TEST_CASE("result_test - equality") 167 { 168 using namespace fplus; 169 IntResults results = {ok<int, std::string>(1), error<int>(std::string("no sqrt of negative numbers")), ok<int, std::string>(2)}; 170 171 REQUIRE(oks(results) == Ints({ 1,2 })); 172 REQUIRE(errors(results) == Strings({std::string("no sqrt of negative numbers")})); 173 174 REQUIRE((ok<int, std::string>(1)) == (ok<int, std::string>(1))); 175 REQUIRE((ok<int, std::string>(1)) != (ok<int, std::string>(2))); 176 REQUIRE((ok<int, std::string>(1)) != (error<int, std::string>(std::string("fail")))); 177 REQUIRE(error<int>(std::string("fail")) == (error<int>(std::string("fail")))); 178 REQUIRE(error<int>(std::string("fail 1")) != (error<int>(std::string("fail 2")))); 179 } 180 181 TEST_CASE("result_test - transform_and_keep_oks") 182 { 183 using namespace fplus; 184 Ints wholeNumbers = { -3, 4, 16, -1 }; 185 REQUIRE_EQ(transform_and_keep_oks(sqrtToResultInt, wholeNumbers) 186 , Ints({2,4})); 187 188 REQUIRE_EQ(transform_and_concat( 189 bind_1st_of_2(replicate<int>, std::size_t(3)), Ints{ 1,2 }) 190 , Ints({ 1,1,1,2,2,2 })); 191 } 192 193 TEST_CASE("result_test - show_result") 194 { 195 using namespace fplus; 196 REQUIRE_EQ(show_result(ok<int, std::string>(42)), std::string("Ok 42")); 197 REQUIRE_EQ(show_result(error<int, std::string>("fail")), std::string("Error fail")); 198 auto x = ok<int, std::string>(2); 199 REQUIRE_EQ((to_maybe<int, std::string>(x)), just(2)); 200 REQUIRE_EQ((from_maybe<std::string, int>(std::string("no error"), just(2))), x); 201 } 202 203 TEST_CASE("result_test - exceptions") 204 { 205 using namespace fplus; 206 std::string thrown_str; 207 try 208 { 209 throw_on_error(std::invalid_argument("exception string"), error<int, std::string>("failed")); 210 } 211 catch (const std::exception& e) 212 { 213 thrown_str = e.what(); 214 } 215 REQUIRE_EQ(thrown_str, std::string("exception string")); 216 thrown_str.clear(); 217 218 try 219 { 220 throw_type_on_error<std::invalid_argument>(error<int, std::string>("failed")); 221 } 222 catch (const std::exception& e) 223 { 224 thrown_str = e.what(); 225 } 226 REQUIRE_EQ(thrown_str, std::string("failed")); 227 thrown_str.clear(); 228 } 229 230 TEST_CASE("result_test - copy") 231 { 232 using namespace fplus; 233 result<int, std::string> result_4 = ok<int, std::string>(4); 234 result<int, std::string> result_4_copy(result_4); 235 result<int, std::string> result_4_copy_2 = error<int, std::string>("dummy"); 236 result_4_copy_2 = result_4_copy; 237 REQUIRE_EQ(result_4_copy_2, (ok<int, std::string>(4))); 238 239 result<int, std::string> result_int_string_error = error<int, std::string>("error"); 240 result<int, std::string> result_int_string_error_copy(result_int_string_error); 241 result<int, std::string> result_int_string_error_copy_2 = ok<int, std::string>(9999); 242 result_int_string_error_copy_2 = result_int_string_error_copy; 243 REQUIRE_EQ(result_int_string_error_copy_2, (error<int, std::string>("error"))); 244 } 245