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