• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "pw_unit_test/framework.h"
18 
19 namespace pw {
20 namespace {
21 
22 struct Defaults {
23   Defaults() = default;
24   Defaults(const Defaults&) = default;
25   Defaults(Defaults&&) = default;
26   Defaults& operator=(const Defaults&) = default;
27   Defaults& operator=(Defaults&&) = default;
28 };
29 
30 struct NoDefaultConstructor {
31   NoDefaultConstructor() = delete;
NoDefaultConstructorpw::__anond67fe1520111::NoDefaultConstructor32   NoDefaultConstructor(std::nullptr_t) {}
33 };
34 
35 struct NoCopy {
36   NoCopy(const NoCopy&) = delete;
37   NoCopy(NoCopy&&) = default;
38   NoCopy& operator=(const NoCopy&) = delete;
39   NoCopy& operator=(NoCopy&&) = default;
40 };
41 
42 struct NoCopyNoMove {
43   NoCopyNoMove(const NoCopyNoMove&) = delete;
44   NoCopyNoMove(NoCopyNoMove&&) = delete;
45   NoCopyNoMove& operator=(const NoCopyNoMove&) = delete;
46   NoCopyNoMove& operator=(NoCopyNoMove&&) = delete;
47 };
48 
49 struct NonTrivialDestructor {
~NonTrivialDestructorpw::__anond67fe1520111::NonTrivialDestructor50   ~NonTrivialDestructor() {}
51 };
52 
53 namespace test_constexpr {
54 // Expected and unexpected are constexpr types.
55 constexpr expected<int, int> kExpectedConstexpr1;
56 constexpr expected<int, int> kExpectedConstexpr2{5};
57 constexpr expected<int, int> kExpectedConstexpr3 = unexpected<int>(42);
58 constexpr unexpected<int> kExpectedConstexprUnexpected{50};
59 static_assert(kExpectedConstexpr1.has_value());
60 static_assert(kExpectedConstexpr1.value() == 0);
61 static_assert(kExpectedConstexpr2.has_value());
62 static_assert(kExpectedConstexpr2.value() == 5);
63 static_assert(!kExpectedConstexpr3.has_value());
64 static_assert(kExpectedConstexpr3.error() == 42);
65 static_assert(kExpectedConstexprUnexpected.error() == 50);
66 }  // namespace test_constexpr
67 
68 namespace test_default_construction {
69 // Default constructible if and only if T is default constructible.
70 static_assert(
71     std::is_default_constructible<expected<Defaults, Defaults>>::value);
72 static_assert(!std::is_default_constructible<
73               expected<NoDefaultConstructor, Defaults>>::value);
74 static_assert(std::is_default_constructible<
75               expected<Defaults, NoDefaultConstructor>>::value);
76 static_assert(!std::is_default_constructible<
77               expected<NoDefaultConstructor, NoDefaultConstructor>>::value);
78 // Never default constructible.
79 static_assert(!std::is_default_constructible<unexpected<Defaults>>::value);
80 static_assert(
81     !std::is_default_constructible<unexpected<NoDefaultConstructor>>::value);
82 }  // namespace test_default_construction
83 
84 namespace test_copy_construction {
85 // Copy constructible if and only if both types are copy constructible.
86 static_assert(std::is_copy_constructible<expected<Defaults, Defaults>>::value);
87 static_assert(!std::is_copy_constructible<expected<Defaults, NoCopy>>::value);
88 static_assert(!std::is_copy_constructible<expected<NoCopy, Defaults>>::value);
89 static_assert(!std::is_copy_constructible<expected<NoCopy, NoCopy>>::value);
90 // Copy constructible if and only if E is copy constructible.
91 static_assert(std::is_copy_constructible<unexpected<Defaults>>::value);
92 static_assert(!std::is_copy_constructible<unexpected<NoCopy>>::value);
93 }  // namespace test_copy_construction
94 
95 namespace test_copy_assignment {
96 // Copy assignable if and only if both types are copy assignable.
97 static_assert(std::is_copy_assignable<expected<Defaults, Defaults>>::value);
98 static_assert(!std::is_copy_assignable<expected<Defaults, NoCopy>>::value);
99 static_assert(!std::is_copy_assignable<expected<NoCopy, Defaults>>::value);
100 static_assert(!std::is_copy_assignable<expected<NoCopy, NoCopy>>::value);
101 // Copy assignable if and only if E is copy assignable.
102 static_assert(std::is_copy_assignable<unexpected<Defaults>>::value);
103 static_assert(!std::is_copy_assignable<unexpected<NoCopy>>::value);
104 }  // namespace test_copy_assignment
105 
106 namespace test_move_construction {
107 // Move constructible if and only if both types are move constructible.
108 static_assert(std::is_move_constructible<expected<Defaults, Defaults>>::value);
109 static_assert(
110     !std::is_move_constructible<expected<Defaults, NoCopyNoMove>>::value);
111 static_assert(
112     !std::is_move_constructible<expected<NoCopyNoMove, Defaults>>::value);
113 static_assert(
114     !std::is_move_constructible<expected<NoCopyNoMove, NoCopyNoMove>>::value);
115 // Move constructible if and only if E is move constructible.
116 static_assert(std::is_move_constructible<unexpected<Defaults>>::value);
117 static_assert(!std::is_move_constructible<unexpected<NoCopyNoMove>>::value);
118 }  // namespace test_move_construction
119 
120 namespace test_move_assignment {
121 // Move assignable if and only if both types are move assignable.
122 static_assert(std::is_move_assignable<expected<Defaults, Defaults>>::value);
123 static_assert(
124     !std::is_move_assignable<expected<Defaults, NoCopyNoMove>>::value);
125 static_assert(
126     !std::is_move_assignable<expected<NoCopyNoMove, Defaults>>::value);
127 static_assert(
128     !std::is_move_assignable<expected<NoCopyNoMove, NoCopyNoMove>>::value);
129 // Move assignable if and only if E is move assignable.
130 static_assert(std::is_move_assignable<unexpected<Defaults>>::value);
131 static_assert(!std::is_move_assignable<unexpected<NoCopyNoMove>>::value);
132 }  // namespace test_move_assignment
133 
134 namespace test_trivial_destructor {
135 // Destructor is trivial if and only if both types are trivially destructible.
136 static_assert(
137     std::is_trivially_destructible<expected<Defaults, Defaults>>::value);
138 static_assert(!std::is_trivially_destructible<
139               expected<NonTrivialDestructor, Defaults>>::value);
140 static_assert(!std::is_trivially_destructible<
141               expected<Defaults, NonTrivialDestructor>>::value);
142 static_assert(!std::is_trivially_destructible<
143               expected<NonTrivialDestructor, NonTrivialDestructor>>::value);
144 // Destructor is trivial if and only if E is trivially destructible.
145 static_assert(std::is_trivially_destructible<unexpected<Defaults>>::value);
146 static_assert(
147     !std::is_trivially_destructible<unexpected<NonTrivialDestructor>>::value);
148 }  // namespace test_trivial_destructor
149 
FailableFunction1(bool fail,int num)150 expected<int, const char*> FailableFunction1(bool fail, int num) {
151   if (fail) {
152     return unexpected<const char*>("FailableFunction1");
153   }
154   return num;
155 }
156 
FailableFunction2(bool fail,int num)157 expected<std::string, const char*> FailableFunction2(bool fail, int num) {
158   if (fail) {
159     return unexpected<const char*>("FailableFunction2");
160   }
161   return std::to_string(num);
162 }
163 
FailOnOdd(int x)164 expected<int, const char*> FailOnOdd(int x) {
165   if (x % 2) {
166     return unexpected<const char*>("odd");
167   }
168   return x;
169 }
170 
ItoaFailOnNegative(int x)171 expected<std::string, const char*> ItoaFailOnNegative(int x) {
172   if (x < 0) {
173     return unexpected<const char*>("negative");
174   }
175   return std::to_string(x);
176 }
177 
GetSecondChar(const std::string & s)178 expected<char, const char*> GetSecondChar(const std::string& s) {
179   if (s.size() < 2) {
180     return unexpected<const char*>("string too small");
181   }
182   return s[1];
183 }
184 
Decrement(int x)185 int Decrement(int x) { return x - 1; }
186 
187 template <class T, class E>
Consume(const expected<T,E> & e)188 expected<void, E> Consume(const expected<T, E>& e) {
189   return e.transform([](auto) {});
190 }
191 
TEST(ExpectedTest,HoldIntValueSuccess)192 TEST(ExpectedTest, HoldIntValueSuccess) {
193   auto x = FailableFunction1(false, 10);
194   ASSERT_TRUE(x.has_value());
195   EXPECT_EQ(x.value(), 10);
196   EXPECT_EQ(*x, 10);
197   EXPECT_EQ(x.value_or(33), 10);
198   EXPECT_EQ(x.error_or("no error"), std::string("no error"));
199 }
200 
TEST(ExpectedTest,HoldIntValueFail)201 TEST(ExpectedTest, HoldIntValueFail) {
202   auto x = FailableFunction1(true, 10);
203   ASSERT_FALSE(x.has_value());
204   EXPECT_EQ(x.error(), std::string("FailableFunction1"));
205   EXPECT_EQ(x.value_or(33), 33);
206   EXPECT_EQ(x.error_or("no error"), std::string("FailableFunction1"));
207 }
208 
TEST(ExpectedTest,HoldStringValueSuccess)209 TEST(ExpectedTest, HoldStringValueSuccess) {
210   auto x = FailableFunction2(false, 42);
211   ASSERT_TRUE(x.has_value());
212   EXPECT_EQ(x.value(), std::string("42"));
213   EXPECT_EQ(*x, std::string("42"));
214   EXPECT_EQ(x.value_or("33"), std::string("42"));
215   EXPECT_EQ(x.error_or("no error"), std::string("no error"));
216 }
217 
TEST(ExpectedTest,HoldStringValueFail)218 TEST(ExpectedTest, HoldStringValueFail) {
219   auto x = FailableFunction2(true, 42);
220   ASSERT_FALSE(x.has_value());
221   EXPECT_EQ(x.error(), std::string("FailableFunction2"));
222   EXPECT_EQ(x.value_or("33"), std::string("33"));
223   EXPECT_EQ(x.error_or("no error"), std::string("FailableFunction2"));
224 }
225 
TEST(ExpectedTest,MonadicOperation)226 TEST(ExpectedTest, MonadicOperation) {
227   auto f = [](expected<int, const char*> value) {
228     return value.and_then(FailOnOdd)
229         .transform(Decrement)
230         .transform(Decrement)
231         .and_then(ItoaFailOnNegative)
232         .and_then(GetSecondChar);
233   };
234   EXPECT_EQ(f(26).value_or(0), '4');
235   EXPECT_EQ(f(26).error_or(nullptr), nullptr);
236   EXPECT_EQ(f(25).value_or(0), 0);
237   EXPECT_EQ(f(25).error_or(nullptr), std::string("odd"));
238   EXPECT_EQ(f(0).value_or(0), 0);
239   EXPECT_EQ(f(0).error_or(nullptr), std::string("negative"));
240   EXPECT_EQ(f(4).value_or(0), 0);
241   EXPECT_EQ(f(4).error_or(nullptr), std::string("string too small"));
242   EXPECT_TRUE(Consume(f(26)).has_value());
243   EXPECT_EQ(Consume(f(25)).error_or(nullptr), std::string("odd"));
244   EXPECT_EQ(Consume(f(0)).error_or(nullptr), std::string("negative"));
245   EXPECT_EQ(Consume(f(4)).error_or(nullptr), std::string("string too small"));
246 }
247 
248 }  // namespace
249 }  // namespace pw
250