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 sqrtToMaybe = [](auto x)
__anona315a4a30202(auto x) 14     {
15         return x < 0.0f ? fplus::nothing<float>() :
16                 fplus::just(static_cast<float>(sqrt(static_cast<float>(x))));
17     };
18 
19     auto sqrtToMaybeInt = [](auto x)
__anona315a4a30302(auto x) 20     {
21         return x < 0 ? fplus::nothing<int>() :
22                 fplus::just(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::maybe<int>> IntMaybes;
31     typedef std::vector<int> Ints;
32 }
33 
34 struct foo {
foofoo35     explicit foo(int) { msgs_.push_back("ctor"); }
foofoo36     foo(const foo&) { msgs_.push_back("copyctor"); }
foofoo37     foo(foo&&) noexcept { msgs_.push_back("movector"); }
operator =foo38     foo& operator = (const foo&) { msgs_.push_back("assignment"); return *this; }
operator =foo39     foo& operator = (foo&&) { msgs_.push_back("moveassignment"); return *this; }
~foofoo40     ~foo() { msgs_.push_back("dtor"); }
41     static std::vector<std::string> msgs_;
42 };
43 std::vector<std::string> foo::msgs_;
44 
45 TEST_CASE("maybe_test - construction and assignment")
46 {
47     using namespace fplus;
48     foo::msgs_.clear();
49     REQUIRE_EQ(maybe<int>(4), just<int>(4));
50 
51     typedef std::vector<std::string> Strings;
52     REQUIRE_EQ(foo::msgs_, Strings({}));
53     maybe<foo> no_foo;
54     REQUIRE_EQ(foo::msgs_, Strings({}));
55     {
56         foo foo_source = foo(1);
57         maybe<foo> a_foo(foo_source);
58         REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor"}));
59     }
60     REQUIRE_EQ(foo::msgs_, Strings({"ctor", "copyctor", "dtor", "dtor"}));
61     foo::msgs_.clear();
62 }
63 
64 TEST_CASE("maybe_test - move semantics")
65 {
66     using namespace fplus;
67     foo::msgs_.clear();
68     typedef std::vector<std::string> Strings;
69     maybe<foo> foo_a(foo(1));
70     maybe<foo> foo_b(foo(2));
71     maybe<foo> foo_z(std::move(foo_a));
72     foo_z = std::move(foo_b);
73     REQUIRE_EQ(foo::msgs_, Strings({
74         "ctor", "movector", "dtor",
75         "ctor", "movector", "dtor",
76         "movector", "dtor", "movector"}));
77     foo::msgs_.clear();
78 }
79 
80 TEST_CASE("maybe_test - unsafe_get_just")
81 {
82     using namespace fplus;
83     fplus::maybe<int> m(4);
84     REQUIRE_EQ(m.unsafe_get_just(), 4);
85     m.unsafe_get_just() += 1;
86     REQUIRE_EQ(m.unsafe_get_just(), 5);
87 }
88 
89 TEST_CASE("maybe_test - as_just_if")
90 {
91     using namespace fplus;
92     REQUIRE_EQ(as_just_if(is_even<int>, 4), just<int>(4));
93     REQUIRE_EQ(as_just_if(is_even<int>, 5), nothing<int>());
94 }
95 
96 TEST_CASE("maybe_test - sequence")
97 {
98     using namespace fplus;
99     REQUIRE_EQ(maybe_to_seq(just(4)), std::vector<int>(1, 4));
100     REQUIRE_EQ(maybe_to_seq(nothing<int>()), std::vector<int>());
101 
102     REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>()), nothing<int>());
103     REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>(1, 4)), just(4));
104     REQUIRE_EQ(singleton_seq_as_maybe(std::vector<int>(2, 4)), nothing<int>());
105 }
106 
107 TEST_CASE("maybe_test - just_with_default")
108 {
109     using namespace fplus;
110     auto x = just<int>(2);
111     maybe<int> y = nothing<int>();
112     auto Or42 = bind_1st_of_2(just_with_default<int>, 42);
113     REQUIRE_EQ(Or42(x), 2);
114     REQUIRE_EQ(Or42(y), 42);
115 }
116 
117 TEST_CASE("maybe_test - lift")
118 {
119     using namespace fplus;
120     auto x = just<int>(2);
121     maybe<int> y = nothing<int>();
__anona315a4a30402(auto n) 122     auto squareGeneric = [](auto n) { return n * n; };
123 
124     REQUIRE_EQ(lift_maybe(square<int>, x), just(4));
125     REQUIRE_EQ(lift_maybe(square<int>, y), nothing<int>());
126     REQUIRE_EQ(lift_maybe(squareGeneric, x), just(4));
127     REQUIRE_EQ(lift_maybe(squareGeneric, y), nothing<int>());
128     auto SquareAndSquare = compose(square<int>, square<int>);
129     REQUIRE_EQ(lift_maybe(SquareAndSquare, x), just(16));
130 
131     REQUIRE_EQ(lift_maybe_def(3, square<int>, x), 4);
132     REQUIRE_EQ(lift_maybe_def(3, square<int>, y), 3);
133     REQUIRE_EQ(lift_maybe_def(3, squareGeneric, x), 4);
134     REQUIRE_EQ(lift_maybe_def(3, squareGeneric, y), 3);
135 
136     REQUIRE_EQ(lift_maybe_2(std::plus<int>(), x, x), just(4));
137     REQUIRE_EQ(lift_maybe_2(std::plus<int>(), x, y), y);
138     REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, x), y);
139     REQUIRE_EQ(lift_maybe_2(std::plus<>(), y, y), y);
140 
141     REQUIRE_EQ(lift_maybe_2_def(3, std::plus<int>(), x, x), 4);
142     REQUIRE_EQ(lift_maybe_2_def(3, std::plus<int>(), x, y), 3);
143     REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, x), 3);
144     REQUIRE_EQ(lift_maybe_2_def(3, std::plus<>(), y, y), 3);
145 }
146 
147 TEST_CASE("maybe_test - join_maybe")
148 {
149     using namespace fplus;
150     REQUIRE_EQ(join_maybe(just(just(2))), just(2));
151     REQUIRE_EQ(join_maybe(just(nothing<int>())), nothing<int>());
152     REQUIRE_EQ(join_maybe(nothing<maybe<int>>()), nothing<int>());
153 }
154 
155 TEST_CASE("maybe_test - and_then_maybe")
156 {
157     using namespace fplus;
158     REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, just(4)), just(2));
159     REQUIRE_EQ(and_then_maybe(sqrtToMaybeInt, nothing<int>()), nothing<int>());
160     const auto string_to_maybe_int = [](const auto& str)
__anona315a4a30502(const auto& str) 161     {
162         if (str == "42") return just<int>(42);
163         else return nothing<int>();
164     };
165     REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just<std::string>("3")), nothing<int>());
166     REQUIRE_EQ(and_then_maybe(string_to_maybe_int, just<std::string>("42")), just<int>(42));
167     REQUIRE_EQ(and_then_maybe(string_to_maybe_int, nothing<std::string>()), nothing<int>());
168 }
169 
170 TEST_CASE("maybe_test - compose")
171 {
172     using namespace fplus;
173     auto sqrtAndSqrt = compose_maybe(sqrtToMaybe, sqrtToMaybe);
174     auto sqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt);
175     REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtInt(256), just(2));
176     auto sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt = compose_maybe(sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt, sqrtToMaybeInt);
177     REQUIRE_EQ(sqrtIntAndSqrtIntAndSqrtIntAndSqrtInt(65536), just(2));
178 
179     auto LiftedIntToFloat = [](const maybe<int>& m) -> maybe<float>
__anona315a4a30602(const maybe<int>& m) 180     {
181         return lift_maybe(IntToFloat, m);
182     };
183     auto JustInt = just<int>;
184     auto IntToMaybeFloat = compose(JustInt, LiftedIntToFloat);
185     auto IntToFloatAndSqrtAndSqrt = compose_maybe(IntToMaybeFloat, sqrtAndSqrt);
186 
__anona315a4a30702(auto n) 187     auto squareMaybe = [](auto n) { return just<decltype(n * n)>(n * n); };
__anona315a4a30802(auto m, auto n) 188     auto plusMaybe = [](auto m, auto n) {
189         return just<decltype(m + n)>(m + n);
190     };
191     REQUIRE_EQ(compose_maybe(plusMaybe, squareMaybe)(2, 3), just(25));
192     REQUIRE(is_in_interval(1.41f, 1.42f, unsafe_get_just<float>
193             (IntToFloatAndSqrtAndSqrt(4))));
194 }
195 
196 TEST_CASE("maybe_test - equality")
197 {
198     using namespace fplus;
199     IntMaybes maybes = {just(1), nothing<int>(), just(2)};
200     REQUIRE(justs(maybes) == Ints({ 1,2 }));
201     REQUIRE(just(1) == just(1));
202     REQUIRE(just(1) != just(2));
203     REQUIRE(just(1) != nothing<int>());
204     REQUIRE(nothing<int>() == nothing<int>());
205 }
206 
207 TEST_CASE("maybe_test - transform_and_keep_justs")
208 {
209     using namespace fplus;
210     Ints wholeNumbers = { -3, 4, 16, -1 };
211     REQUIRE_EQ(transform_and_keep_justs(sqrtToMaybeInt, wholeNumbers)
212            , Ints({2,4}));
213     REQUIRE_EQ(transform_and_concat(
214             bind_1st_of_2(replicate<int>, std::size_t(3)), Ints{ 1,2 })
215            , Ints({ 1,1,1,2,2,2 }));
216 }
217 
218 TEST_CASE("maybe_test - show_maybe")
219 {
220     using namespace fplus;
221     REQUIRE_EQ(show_maybe(just<int>(42)), std::string("Just 42"));
222     REQUIRE_EQ(show_maybe(nothing<int>()), std::string("Nothing"));
223 }
224 
225 TEST_CASE("maybe_test - exceptions")
226 {
227     using namespace fplus;
228     std::string thrown_str;
229     try
230     {
231         throw_on_nothing(std::invalid_argument("raised"), nothing<int>());
232     }
233     catch (const std::exception& e)
234     {
235         thrown_str = e.what();
236     }
237     REQUIRE_EQ(thrown_str, std::string("raised"));
238 }
239 
240 TEST_CASE("maybe_test - copy")
241 {
242     using namespace fplus;
243     maybe<int> maybe_4(4);
244     maybe<int> maybe_4_copy(maybe_4);
245     maybe<int> maybe_4_copy_2;
246     maybe_4_copy_2 = maybe_4_copy;
247     REQUIRE_EQ(maybe_4_copy_2, just<int>(4));
248 }
249 
250 TEST_CASE("maybe_test - flatten")
251 {
252     using namespace fplus;
253     maybe<int> maybe_int_nothing;
254     maybe<int> maybe_int_nothing_copy(maybe_int_nothing);
255     maybe<int> maybe_int_nothing_copy_2;
256     maybe_int_nothing_copy_2 = maybe_int_nothing_copy;
257     REQUIRE_EQ(maybe_int_nothing_copy_2, nothing<int>());
258     REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>(maybe<int>(1))), maybe<int>(1));
259     REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>(maybe<int>())), nothing<int>());
260     REQUIRE_EQ(flatten_maybe(maybe<maybe<int>>()), nothing<int>());
261 }
262