xref: /aosp_15_r20/external/pigweed/pw_result/expected_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_result/expected.h"
16 
17 #include <string>
18 
19 #include "pw_unit_test/framework.h"
20 
21 namespace pw {
22 namespace {
23 
24 struct Defaults {
25   Defaults() = default;
26   Defaults(const Defaults&) = default;
27   Defaults(Defaults&&) = default;
28   Defaults& operator=(const Defaults&) = default;
29   Defaults& operator=(Defaults&&) = default;
30 };
31 
32 struct NoDefaultConstructor {
33   NoDefaultConstructor() = delete;
NoDefaultConstructorpw::__anon1efd96b40111::NoDefaultConstructor34   NoDefaultConstructor(std::nullptr_t) {}
35 };
36 
37 struct NoCopy {
38   NoCopy(const NoCopy&) = delete;
39   NoCopy(NoCopy&&) = default;
40   NoCopy& operator=(const NoCopy&) = delete;
41   NoCopy& operator=(NoCopy&&) = default;
42 };
43 
44 struct NoCopyNoMove {
45   NoCopyNoMove(const NoCopyNoMove&) = delete;
46   NoCopyNoMove(NoCopyNoMove&&) = delete;
47   NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
48   NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
49 };
50 
51 struct NonTrivialDestructor {
~NonTrivialDestructorpw::__anon1efd96b40111::NonTrivialDestructor52   ~NonTrivialDestructor() {}
53 };
54 
55 namespace test_constexpr {
56 // Expected and unexpected are constexpr types.
57 constexpr expected<int, int> kExpectedConstexpr1;
58 constexpr expected<int, int> kExpectedConstexpr2{5};
59 constexpr expected<int, int> kExpectedConstexpr3 = unexpected<int>(42);
60 constexpr unexpected<int> kExpectedConstexprUnexpected{50};
61 static_assert(kExpectedConstexpr1.has_value());
62 static_assert(kExpectedConstexpr1.value() == 0);
63 static_assert(kExpectedConstexpr2.has_value());
64 static_assert(kExpectedConstexpr2.value() == 5);
65 static_assert(!kExpectedConstexpr3.has_value());
66 static_assert(kExpectedConstexpr3.error() == 42);
67 static_assert(kExpectedConstexprUnexpected.error() == 50);
68 }  // namespace test_constexpr
69 
70 namespace test_default_construction {
71 // Default constructible if and only if T is default constructible.
72 static_assert(
73     std::is_default_constructible<expected<Defaults, Defaults>>::value);
74 static_assert(!std::is_default_constructible<
75               expected<NoDefaultConstructor, Defaults>>::value);
76 static_assert(std::is_default_constructible<
77               expected<Defaults, NoDefaultConstructor>>::value);
78 static_assert(!std::is_default_constructible<
79               expected<NoDefaultConstructor, NoDefaultConstructor>>::value);
80 // Never default constructible.
81 static_assert(!std::is_default_constructible<unexpected<Defaults>>::value);
82 static_assert(
83     !std::is_default_constructible<unexpected<NoDefaultConstructor>>::value);
84 }  // namespace test_default_construction
85 
86 namespace test_copy_construction {
87 // Copy constructible if and only if both types are copy constructible.
88 static_assert(std::is_copy_constructible<expected<Defaults, Defaults>>::value);
89 static_assert(!std::is_copy_constructible<expected<Defaults, NoCopy>>::value);
90 static_assert(!std::is_copy_constructible<expected<NoCopy, Defaults>>::value);
91 static_assert(!std::is_copy_constructible<expected<NoCopy, NoCopy>>::value);
92 // Copy constructible if and only if E is copy constructible.
93 static_assert(std::is_copy_constructible<unexpected<Defaults>>::value);
94 static_assert(!std::is_copy_constructible<unexpected<NoCopy>>::value);
95 }  // namespace test_copy_construction
96 
97 namespace test_copy_assignment {
98 // Copy assignable if and only if both types are copy assignable.
99 static_assert(std::is_copy_assignable<expected<Defaults, Defaults>>::value);
100 static_assert(!std::is_copy_assignable<expected<Defaults, NoCopy>>::value);
101 static_assert(!std::is_copy_assignable<expected<NoCopy, Defaults>>::value);
102 static_assert(!std::is_copy_assignable<expected<NoCopy, NoCopy>>::value);
103 // Copy assignable if and only if E is copy assignable.
104 static_assert(std::is_copy_assignable<unexpected<Defaults>>::value);
105 static_assert(!std::is_copy_assignable<unexpected<NoCopy>>::value);
106 }  // namespace test_copy_assignment
107 
108 namespace test_move_construction {
109 // Move constructible if and only if both types are move constructible.
110 static_assert(std::is_move_constructible<expected<Defaults, Defaults>>::value);
111 static_assert(
112     !std::is_move_constructible<expected<Defaults, NoCopyNoMove>>::value);
113 static_assert(
114     !std::is_move_constructible<expected<NoCopyNoMove, Defaults>>::value);
115 static_assert(
116     !std::is_move_constructible<expected<NoCopyNoMove, NoCopyNoMove>>::value);
117 // Move constructible if and only if E is move constructible.
118 static_assert(std::is_move_constructible<unexpected<Defaults>>::value);
119 static_assert(!std::is_move_constructible<unexpected<NoCopyNoMove>>::value);
120 }  // namespace test_move_construction
121 
122 namespace test_move_assignment {
123 // Move assignable if and only if both types are move assignable.
124 static_assert(std::is_move_assignable<expected<Defaults, Defaults>>::value);
125 static_assert(
126     !std::is_move_assignable<expected<Defaults, NoCopyNoMove>>::value);
127 static_assert(
128     !std::is_move_assignable<expected<NoCopyNoMove, Defaults>>::value);
129 static_assert(
130     !std::is_move_assignable<expected<NoCopyNoMove, NoCopyNoMove>>::value);
131 // Move assignable if and only if E is move assignable.
132 static_assert(std::is_move_assignable<unexpected<Defaults>>::value);
133 static_assert(!std::is_move_assignable<unexpected<NoCopyNoMove>>::value);
134 }  // namespace test_move_assignment
135 
136 namespace test_trivial_destructor {
137 // Destructor is trivial if and only if both types are trivially destructible.
138 static_assert(
139     std::is_trivially_destructible<expected<Defaults, Defaults>>::value);
140 static_assert(!std::is_trivially_destructible<
141               expected<NonTrivialDestructor, Defaults>>::value);
142 static_assert(!std::is_trivially_destructible<
143               expected<Defaults, NonTrivialDestructor>>::value);
144 static_assert(!std::is_trivially_destructible<
145               expected<NonTrivialDestructor, NonTrivialDestructor>>::value);
146 // Destructor is trivial if and only if E is trivially destructible.
147 static_assert(std::is_trivially_destructible<unexpected<Defaults>>::value);
148 static_assert(
149     !std::is_trivially_destructible<unexpected<NonTrivialDestructor>>::value);
150 }  // namespace test_trivial_destructor
151 
FailableFunction1(bool fail,int num)152 expected<int, const char*> FailableFunction1(bool fail, int num) {
153   if (fail) {
154     return unexpected("FailableFunction1");
155   }
156   return num;
157 }
158 
FailableFunction2(bool fail,int num)159 expected<std::string, const char*> FailableFunction2(bool fail, int num) {
160   if (fail) {
161     return unexpected("FailableFunction2");
162   }
163   return std::to_string(num);
164 }
165 
FailOnOdd(int x)166 expected<int, const char*> FailOnOdd(int x) {
167   if (x % 2) {
168     return unexpected("odd");
169   }
170   return x;
171 }
172 
ItoaFailOnNegative(int x)173 expected<std::string, const char*> ItoaFailOnNegative(int x) {
174   if (x < 0) {
175     return unexpected("negative");
176   }
177   return std::to_string(x);
178 }
179 
GetSecondChar(const std::string & s)180 expected<char, const char*> GetSecondChar(const std::string& s) {
181   if (s.size() < 2) {
182     return unexpected("string too small");
183   }
184   return s[1];
185 }
186 
Decrement(int x)187 int Decrement(int x) { return x - 1; }
188 
189 template <class T, class E>
Consume(const expected<T,E> & e)190 expected<void, E> Consume(const expected<T, E>& e) {
191   return e.transform([](auto) {});
192 }
193 
TEST(ExpectedTest,HoldIntValueSuccess)194 TEST(ExpectedTest, HoldIntValueSuccess) {
195   auto x = FailableFunction1(false, 10);
196   ASSERT_TRUE(x.has_value());
197   EXPECT_EQ(x.value(), 10);
198   EXPECT_EQ(*x, 10);
199   EXPECT_EQ(x.value_or(33), 10);
200   EXPECT_EQ(x.error_or("no error"), std::string("no error"));
201 }
202 
TEST(ExpectedTest,HoldIntValueFail)203 TEST(ExpectedTest, HoldIntValueFail) {
204   auto x = FailableFunction1(true, 10);
205   ASSERT_FALSE(x.has_value());
206   EXPECT_EQ(x.error(), std::string("FailableFunction1"));
207   EXPECT_EQ(x.value_or(33), 33);
208   EXPECT_EQ(x.error_or("no error"), std::string("FailableFunction1"));
209 }
210 
TEST(ExpectedTest,HoldStringValueSuccess)211 TEST(ExpectedTest, HoldStringValueSuccess) {
212   auto x = FailableFunction2(false, 42);
213   ASSERT_TRUE(x.has_value());
214   EXPECT_EQ(x.value(), std::string("42"));
215   EXPECT_EQ(*x, std::string("42"));
216   EXPECT_EQ(x.value_or("33"), std::string("42"));
217   EXPECT_EQ(x.error_or("no error"), std::string("no error"));
218 }
219 
TEST(ExpectedTest,HoldStringValueFail)220 TEST(ExpectedTest, HoldStringValueFail) {
221   auto x = FailableFunction2(true, 42);
222   ASSERT_FALSE(x.has_value());
223   EXPECT_EQ(x.error(), std::string("FailableFunction2"));
224   EXPECT_EQ(x.value_or("33"), std::string("33"));
225   EXPECT_EQ(x.error_or("no error"), std::string("FailableFunction2"));
226 }
227 
TEST(ExpectedTest,MonadicOperation)228 TEST(ExpectedTest, MonadicOperation) {
229   auto f = [](expected<int, const char*> value) {
230     return value.and_then(FailOnOdd)
231         .transform(Decrement)
232         .transform(Decrement)
233         .and_then(ItoaFailOnNegative)
234         .and_then(GetSecondChar);
235   };
236   EXPECT_EQ(f(26).value_or(0), '4');
237   EXPECT_EQ(f(26).error_or(nullptr), nullptr);
238   EXPECT_EQ(f(25).value_or(0), 0);
239   EXPECT_EQ(f(25).error_or(nullptr), std::string("odd"));
240   EXPECT_EQ(f(0).value_or(0), 0);
241   EXPECT_EQ(f(0).error_or(nullptr), std::string("negative"));
242   EXPECT_EQ(f(4).value_or(0), 0);
243   EXPECT_EQ(f(4).error_or(nullptr), std::string("string too small"));
244   EXPECT_TRUE(Consume(f(26)).has_value());
245   EXPECT_EQ(Consume(f(25)).error_or(nullptr), std::string("odd"));
246   EXPECT_EQ(Consume(f(0)).error_or(nullptr), std::string("negative"));
247   EXPECT_EQ(Consume(f(4)).error_or(nullptr), std::string("string too small"));
248 }
249 
250 }  // namespace
251 }  // namespace pw
252