• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ftl/optional.h>
18 #include <ftl/static_vector.h>
19 #include <ftl/string.h>
20 #include <ftl/unit.h>
21 #include <gtest/gtest.h>
22 
23 #include <cstdlib>
24 #include <functional>
25 #include <numeric>
26 #include <utility>
27 
28 using namespace std::placeholders;
29 using namespace std::string_literals;
30 
31 namespace android::test {
32 
33 using ftl::Optional;
34 using ftl::StaticVector;
35 
TEST(Optional,Construct)36 TEST(Optional, Construct) {
37   // Empty.
38   EXPECT_EQ(std::nullopt, Optional<int>());
39   EXPECT_EQ(std::nullopt, Optional<std::string>(std::nullopt));
40 
41   // Value.
42   EXPECT_EQ('?', Optional('?'));
43   EXPECT_EQ(""s, Optional(std::string()));
44 
45   // In place.
46   EXPECT_EQ("???"s, Optional<std::string>(std::in_place, 3u, '?'));
47   EXPECT_EQ("abc"s, Optional<std::string>(std::in_place, {'a', 'b', 'c'}));
48 
49   // Implicit downcast.
50   {
51     Optional opt = std::optional("test"s);
52     static_assert(std::is_same_v<decltype(opt), Optional<std::string>>);
53 
54     ASSERT_TRUE(opt);
55     EXPECT_EQ(opt.value(), "test"s);
56   }
57 }
58 
TEST(Optional,Transform)59 TEST(Optional, Transform) {
60   // Empty.
61   EXPECT_EQ(std::nullopt, Optional<int>().transform([](int) { return 0; }));
62 
63   // By value.
64   EXPECT_EQ(0, Optional(0).transform([](int x) { return x; }));
65   EXPECT_EQ(100, Optional(99).transform([](int x) { return x + 1; }));
66   EXPECT_EQ("0b100"s, Optional(4).transform(std::bind(ftl::to_string<int>, _1, ftl::Radix::kBin)));
67 
68   // By reference.
69   {
70     Optional opt = 'x';
71     EXPECT_EQ('z', opt.transform([](char& c) {
72       c = 'y';
73       return 'z';
74     }));
75 
76     EXPECT_EQ('y', opt);
77   }
78 
79   // By rvalue reference.
80   {
81     std::string out;
82     EXPECT_EQ("xyz"s, Optional("abc"s).transform([&out](std::string&& str) {
83       out = std::move(str);
84       return "xyz"s;
85     }));
86 
87     EXPECT_EQ(out, "abc"s);
88   }
89 
90   // No return value.
91   {
92     Optional opt = "food"s;
93     EXPECT_EQ(ftl::unit, opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); })));
94     EXPECT_EQ(opt, "foo"s);
95   }
96 
97   // Chaining.
98   EXPECT_EQ(14u, Optional(StaticVector{"upside"s, "down"s})
99                      .transform([](StaticVector<std::string, 3>&& v) {
100                        v.push_back("cake"s);
101                        return v;
102                      })
103                      .transform([](const StaticVector<std::string, 3>& v) {
104                        return std::accumulate(v.begin(), v.end(), std::string());
105                      })
106                      .transform([](const std::string& s) { return s.length(); }));
107 }
108 
109 namespace {
110 
parse_int(const std::string & str)111 Optional<int> parse_int(const std::string& str) {
112   if (const int i = std::atoi(str.c_str())) return i;
113   return std::nullopt;
114 }
115 
116 }  // namespace
117 
TEST(Optional,AndThen)118 TEST(Optional, AndThen) {
119   // Empty.
120   EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) -> Optional<int> { return 0; }));
121   EXPECT_EQ(std::nullopt, Optional<int>().and_then([](int) { return Optional<int>(); }));
122 
123   // By value.
124   EXPECT_EQ(0, Optional(0).and_then([](int x) { return Optional(x); }));
125   EXPECT_EQ(123, Optional("123").and_then(parse_int));
126   EXPECT_EQ(std::nullopt, Optional("abc").and_then(parse_int));
127 
128   // By reference.
129   {
130     Optional opt = 'x';
131     EXPECT_EQ('z', opt.and_then([](char& c) {
132       c = 'y';
133       return Optional('z');
134     }));
135 
136     EXPECT_EQ('y', opt);
137   }
138 
139   // By rvalue reference.
140   {
141     std::string out;
142     EXPECT_EQ("xyz"s, Optional("abc"s).and_then([&out](std::string&& str) {
143       out = std::move(str);
144       return Optional("xyz"s);
145     }));
146 
147     EXPECT_EQ(out, "abc"s);
148   }
149 
150   // Chaining.
151   using StringVector = StaticVector<std::string, 3>;
152   EXPECT_EQ(14u, Optional(StaticVector{"-"s, "1"s})
153                      .and_then([](StringVector&& v) -> Optional<StringVector> {
154                        if (v.push_back("4"s)) return v;
155                        return {};
156                      })
157                      .and_then([](const StringVector& v) -> Optional<std::string> {
158                        if (v.full()) return std::accumulate(v.begin(), v.end(), std::string());
159                        return {};
160                      })
161                      .and_then(parse_int)
162                      .and_then([](int i) {
163                        return i > 0 ? std::nullopt : std::make_optional(static_cast<unsigned>(-i));
164                      }));
165 }
166 
TEST(Optional,OrElse)167 TEST(Optional, OrElse) {
168   // Non-empty.
169   {
170     const Optional opt = false;
171     EXPECT_EQ(false, opt.or_else([] { return Optional(true); }));
172     EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); }));
173   }
174 
175   // Empty.
176   {
177     const Optional<int> opt;
178     EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; }));
179     EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); }));
180   }
181   {
182     bool empty = false;
183     EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> {
184       empty = true;
185       return std::nullopt;
186     }));
187     EXPECT_TRUE(empty);
188   }
189 
190   // Chaining.
191   using StringVector = StaticVector<std::string, 3>;
192   EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s})
193                      .and_then([](StringVector&& v) -> Optional<StringVector> {
194                        if (v.push_back("0"s)) return v;
195                        return {};
196                      })
197                      .or_else([] {
198                        return Optional(StaticVector{"9"s, "9"s, "9"s});
199                      })
200                      .transform([](const StringVector& v) {
201                        return std::accumulate(v.begin(), v.end(), std::string());
202                      })
203                      .and_then(parse_int)
204                      .or_else([] { return Optional(-1); }));
205 }
206 
207 // Comparison.
208 namespace {
209 
210 constexpr Optional<int> kOptional1 = 1;
211 constexpr Optional<int> kAnotherOptional1 = 1;
212 constexpr Optional<int> kOptional2 = 2;
213 constexpr Optional<int> kOptionalEmpty, kAnotherOptionalEmpty;
214 
215 constexpr std::optional<int> kStdOptional1 = 1;
216 
217 static_assert(kOptional1 == kAnotherOptional1);
218 
219 static_assert(kOptional1 != kOptional2);
220 static_assert(kOptional2 != kOptional1);
221 
222 static_assert(kOptional1 != kOptionalEmpty);
223 static_assert(kOptionalEmpty != kOptional1);
224 
225 static_assert(kOptionalEmpty == kAnotherOptionalEmpty);
226 
227 static_assert(kOptional1 == kStdOptional1);
228 static_assert(kStdOptional1 == kOptional1);
229 
230 static_assert(kOptional2 != kStdOptional1);
231 static_assert(kStdOptional1 != kOptional2);
232 
233 static_assert(kOptional2 != kOptionalEmpty);
234 static_assert(kOptionalEmpty != kOptional2);
235 
236 } // namespace
237 
238 }  // namespace android::test
239