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_bluetooth_sapphire/internal/host/common/error.h"
16
17 #include <sstream>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/uuid.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace bt {
23 namespace {
24
25 enum class TestError : uint8_t {
26 kSuccess = 0,
27 kFail1 = 1,
28 kFail2 = 2,
29 };
30
31 enum class TestErrorWithoutSuccess {
32 kFail0 = 0,
33 kFail1 = 1,
34 };
35
36 // Test detail::IsErrorV
37 static_assert(detail::IsErrorV<Error<TestError>>);
38 static_assert(!detail::IsErrorV<TestError>);
39
40 } // namespace
41
42 // This template specialization must be in ::bt for name lookup reasons
43 template <>
44 struct ProtocolErrorTraits<TestError> {
ToStringbt::ProtocolErrorTraits45 static std::string ToString(TestError code) {
46 switch (code) {
47 case TestError::kSuccess:
48 return "success (TestError 0)";
49 case TestError::kFail1:
50 return "fail 1 (TestError 1)";
51 case TestError::kFail2:
52 return "fail 2 (TestError 2)";
53 default:
54 return "unknown (TestError)";
55 }
56 }
57
is_successbt::ProtocolErrorTraits58 static constexpr bool is_success(TestError proto_code) {
59 return proto_code == TestError::kSuccess;
60 }
61 };
62
63 template <>
64 struct ProtocolErrorTraits<TestErrorWithoutSuccess> {
ToStringbt::ProtocolErrorTraits65 static std::string ToString(TestErrorWithoutSuccess code) {
66 switch (code) {
67 case TestErrorWithoutSuccess::kFail0:
68 return "fail 0 (TestErrorWithoutSuccess 0)";
69 case TestErrorWithoutSuccess::kFail1:
70 return "fail 1 (TestErrorWithoutSuccess 1)";
71 default:
72 return "unknown (TestError)";
73 }
74 }
75
76 // is_success() is omitted
77 };
78
79 namespace {
80
81 // Build an Error when |proto_code| is guaranteed to be an error. This could be
82 // consteval in C++20 so that if the code isn't an error, it doesn't compile.
MakeError(TestError proto_code)83 constexpr Error<TestError> MakeError(TestError proto_code) {
84 return ToResult(proto_code).error_value();
85 }
86
TEST(ErrorTest,ResultFromNonSuccessHostError)87 TEST(ErrorTest, ResultFromNonSuccessHostError) {
88 // Create a result that can hold TestError but which holds a HostError
89 constexpr fit::result result = ToResult<TestError>(HostError::kFailed);
90 ASSERT_TRUE(result.is_error());
91
92 // Unwrap the result then access Error::is(…)
93 constexpr Error error = result.error_value();
94 ASSERT_TRUE(error.is_host_error());
95 EXPECT_FALSE(error.is_protocol_error());
96 EXPECT_EQ(HostError::kFailed, error.host_error());
97 EXPECT_TRUE(error.is(HostError::kFailed));
98 EXPECT_FALSE(error.is(HostError::kTimedOut));
99
100 // Compare to protocol error
101 EXPECT_FALSE(error.is(TestError::kFail1));
102 EXPECT_FALSE(error.is(TestError::kSuccess));
103
104 // Compare result to error
105 EXPECT_EQ(error, result);
106 EXPECT_EQ(result, error);
107 }
108
TEST(ErrorTest,ResultFromSuccessHostError)109 TEST(ErrorTest, ResultFromSuccessHostError) {
110 constexpr fit::result result = ToResult<TestError>(TestError::kSuccess);
111 ASSERT_EQ(fit::ok(), result);
112
113 // Compare result to error
114 const Error error = MakeError(TestError::kFail1);
115 EXPECT_NE(error, result);
116 EXPECT_NE(result, error);
117 }
118
TEST(ErrorTest,ResultFromNonSuccessGeneralHostError)119 TEST(ErrorTest, ResultFromNonSuccessGeneralHostError) {
120 // Create a result that can hold and only holds a HostError
121 constexpr fit::result result = ToResult(HostError::kFailed);
122 ASSERT_TRUE(result.is_error());
123
124 // Unwrap the result then access Error::is(…)
125 constexpr Error general_error = result.error_value();
126 ASSERT_TRUE(general_error.is_host_error());
127 EXPECT_FALSE(general_error.is_protocol_error());
128 EXPECT_EQ(HostError::kFailed, general_error.host_error());
129 EXPECT_TRUE(general_error.is(HostError::kFailed));
130 EXPECT_FALSE(general_error.is(HostError::kTimedOut));
131
132 // Compare result to error
133 EXPECT_EQ(general_error, result);
134 EXPECT_EQ(result, general_error);
135
136 // Create a specific kind of Error from the only-HostError-holding Error
137 constexpr Error<TestError> specific_error = general_error;
138 EXPECT_TRUE(specific_error.is(HostError::kFailed));
139 EXPECT_EQ(general_error, specific_error);
140 EXPECT_EQ(specific_error, general_error);
141
142 // Test operator!=
143 constexpr Error different_specific_error = MakeError(TestError::kFail1);
144 EXPECT_NE(general_error, different_specific_error);
145 EXPECT_NE(different_specific_error, general_error);
146 }
147
TEST(ErrorTest,ResultFromNonSuccessProtocolError)148 TEST(ErrorTest, ResultFromNonSuccessProtocolError) {
149 constexpr fit::result result = ToResult(TestError::kFail1);
150 ASSERT_TRUE(result.is_error());
151
152 // Unwrap the result then access Error::is(…)
153 constexpr Error error = result.error_value();
154 ASSERT_TRUE(error.is_protocol_error());
155 EXPECT_FALSE(error.is_host_error());
156 EXPECT_EQ(TestError::kFail1, error.protocol_error());
157 EXPECT_TRUE(error.is(TestError::kFail1));
158 EXPECT_FALSE(error.is(TestError::kSuccess));
159 EXPECT_FALSE(error.is(TestError::kFail2));
160
161 // Compare to HostError
162 EXPECT_FALSE(error.is(HostError::kFailed));
163
164 // Compare result to error
165 EXPECT_EQ(error, result);
166 EXPECT_EQ(result, error);
167 }
168
TEST(ErrorTest,ResultFromSuccessProtocolError)169 TEST(ErrorTest, ResultFromSuccessProtocolError) {
170 constexpr fit::result result = ToResult(TestError::kSuccess);
171 ASSERT_EQ(fit::ok(), result);
172
173 // Compare result to error
174 const Error error = MakeError(TestError::kFail1);
175 EXPECT_NE(error, result);
176 EXPECT_NE(result, error);
177 }
178
TEST(ErrorTest,ResultFromNonSuccessProtocolErrorThatOnlyHoldsErrors)179 TEST(ErrorTest, ResultFromNonSuccessProtocolErrorThatOnlyHoldsErrors) {
180 // Use public ctor to construct the error directly.
181 constexpr Error<TestErrorWithoutSuccess> error(
182 TestErrorWithoutSuccess::kFail0);
183 EXPECT_TRUE(error.is(TestErrorWithoutSuccess::kFail0));
184 }
185
TEST(ErrorDeathTest,ReadingHostErrorThatIsNotPresentIsFatal)186 TEST(ErrorDeathTest, ReadingHostErrorThatIsNotPresentIsFatal) {
187 const Error error = MakeError(TestError::kFail1);
188 ASSERT_DEATH_IF_SUPPORTED([[maybe_unused]] auto _ = error.host_error(),
189 "host_error");
190 }
191
TEST(ErrorDeathTest,ReadingProtocolErrorThatIsNotPresentIsFatal)192 TEST(ErrorDeathTest, ReadingProtocolErrorThatIsNotPresentIsFatal) {
193 const Error<TestError> error(HostError::kFailed);
194 ASSERT_DEATH_IF_SUPPORTED([[maybe_unused]] auto _ = error.protocol_error(),
195 "protocol_error");
196 }
197
TEST(ErrorTest,ResultIsAnyOf)198 TEST(ErrorTest, ResultIsAnyOf) {
199 constexpr Error error = MakeError(TestError::kFail1);
200
201 // None of the arguments compare equal to error's contents
202 EXPECT_FALSE(error.is_any_of(
203 HostError::kFailed, TestError::kFail2, TestError::kSuccess));
204
205 // One argument matches
206 EXPECT_TRUE(error.is_any_of(
207 HostError::kFailed, TestError::kFail2, TestError::kFail1));
208 EXPECT_TRUE(error.is_any_of(
209 HostError::kFailed, TestError::kFail1, TestError::kFail2));
210 EXPECT_TRUE(error.is_any_of(TestError::kFail1));
211 }
212
TEST(ErrorTest,ErrorCanBeComparedInTests)213 TEST(ErrorTest, ErrorCanBeComparedInTests) {
214 const Error error = MakeError(TestError::kFail1);
215
216 // Compare to HostError
217 EXPECT_FALSE(error.is(HostError::kFailed));
218
219 // Use operator== through GTest
220 EXPECT_EQ(error, error);
221
222 // Use operator!= through GTest
223 EXPECT_NE(MakeError(TestError::kFail2), error);
224 EXPECT_NE(Error<>(HostError::kFailed), error);
225 EXPECT_NE(Error<TestError>(HostError::kFailed), error);
226 }
227
TEST(ErrorTest,ResultCanBeComparedInTests)228 TEST(ErrorTest, ResultCanBeComparedInTests) {
229 constexpr fit::result result = ToResult(TestError::kFail1);
230
231 // Use operator== through GTest
232 EXPECT_EQ(result, result);
233
234 // And explicitly
235 EXPECT_FALSE(result == ToResult<TestError>(HostError::kCanceled));
236 EXPECT_FALSE(result == ToResult(HostError::kCanceled));
237 EXPECT_FALSE(result == fit::ok());
238 EXPECT_FALSE(result == fit::result<Error<TestError>>(fit::ok()));
239
240 // Use operator!= through GTest
241 EXPECT_NE(ToResult<TestError>(HostError::kCanceled), result);
242 EXPECT_NE(ToResult(TestError::kFail2), result);
243
244 // Compare to a general result
245 EXPECT_NE(ToResult(HostError::kCanceled), result);
246 EXPECT_NE(fit::result<Error<NoProtocolError>>(fit::ok()), result);
247
248 // Compare results to fix::success
249 EXPECT_NE(fit::ok(), result);
250 EXPECT_EQ(fit::ok(), ToResult(TestError::kSuccess));
251
252 const fit::result<Error<TestError>, int> success_with_value = fit::ok(1);
253 const fit::result<Error<TestError>, int> error_with_value =
254 fit::error(MakeError(TestError::kFail1));
255 const fit::result<Error<TestError>, int> different_error_with_value =
256 fit::error(MakeError(TestError::kFail2));
257 EXPECT_EQ(success_with_value, success_with_value);
258 EXPECT_NE(success_with_value, error_with_value);
259 EXPECT_FALSE(success_with_value == error_with_value);
260 EXPECT_NE(error_with_value, different_error_with_value);
261
262 EXPECT_EQ(ToResult(TestError::kFail1).error_value(), error_with_value);
263 EXPECT_NE(ToResult(TestError::kFail2).error_value(), error_with_value);
264
265 const fit::result<Error<TestError>, int> error_with_value_holding_host_error =
266 fit::error(Error<TestError>(HostError::kFailed));
267
268 // ToResult(HostError) constructs a bt::Error<NoProtocolError> so comparisons
269 // must take this into account.
270 EXPECT_EQ(Error(HostError::kFailed), error_with_value_holding_host_error);
271 EXPECT_NE(Error(HostError::kFailed), error_with_value);
272 }
273
TEST(ErrorTest,VisitOnHostError)274 TEST(ErrorTest, VisitOnHostError) {
275 constexpr Error<TestError> error(HostError::kFailed);
276 ASSERT_TRUE(error.is_host_error());
277
278 bool host_visited = false;
279 bool proto_visited = false;
280 error.Visit([&host_visited](HostError) { host_visited = true; },
281 [&proto_visited](TestError) { proto_visited = true; });
282 EXPECT_TRUE(host_visited);
283 EXPECT_FALSE(proto_visited);
284 }
285
TEST(ErrorTest,VisitOnProtoError)286 TEST(ErrorTest, VisitOnProtoError) {
287 constexpr Error error = MakeError(TestError::kFail1);
288 ASSERT_TRUE(error.is_protocol_error());
289
290 bool host_visited = false;
291 bool proto_visited = false;
292 error.Visit([&host_visited](HostError) { host_visited = true; },
293 [&proto_visited](TestError) { proto_visited = true; });
294 EXPECT_FALSE(host_visited);
295 EXPECT_TRUE(proto_visited);
296 }
297
TEST(ErrorTest,HostErrorToString)298 TEST(ErrorTest, HostErrorToString) {
299 constexpr Error error(HostError::kFailed);
300 EXPECT_EQ(HostErrorToString(error.host_error()), error.ToString());
301 }
302
TEST(ErrorTest,GeneralHostErrorToString)303 TEST(ErrorTest, GeneralHostErrorToString) {
304 constexpr Error error(HostError::kFailed);
305 EXPECT_EQ(HostErrorToString(error.host_error()), error.ToString());
306 }
307
TEST(ErrorTest,ProtocolErrorToString)308 TEST(ErrorTest, ProtocolErrorToString) {
309 constexpr Error error = MakeError(TestError::kFail2);
310 EXPECT_EQ(ProtocolErrorTraits<TestError>::ToString(TestError::kFail2),
311 error.ToString());
312
313 // Test that GoogleTest's value printer converts to the same string
314 EXPECT_EQ(internal::ToString(error), ::testing::PrintToString(error));
315
316 // ostringstream::operator<< returns a ostream&, so test that our operator is
317 // compatible
318 std::ostringstream oss;
319 oss << error;
320 }
321
TEST(ErrorTest,ToStringOnResult)322 TEST(ErrorTest, ToStringOnResult) {
323 constexpr fit::result proto_error_result = ToResult(TestError::kFail2);
324 EXPECT_EQ("[result: error(fail 2 (TestError 2))]",
325 internal::ToString(proto_error_result));
326 constexpr fit::result<Error<TestError>> success_result = fit::ok();
327 EXPECT_EQ("[result: ok()]", internal::ToString(success_result));
328 constexpr fit::result<Error<TestError>, int> success_result_with_value =
329 fit::ok(1);
330 EXPECT_EQ("[result: ok(?)]", internal::ToString(success_result_with_value));
331 constexpr fit::result<Error<TestError>, UUID>
332 success_result_with_printable_value = fit::ok(UUID(uint16_t{}));
333 EXPECT_EQ("[result: ok(00000000-0000-1000-8000-00805f9b34fb)]",
334 internal::ToString(success_result_with_printable_value));
335
336 // Test that GoogleTest's value printer converts to the same string
337 EXPECT_EQ(internal::ToString(proto_error_result),
338 ::testing::PrintToString(proto_error_result));
339 EXPECT_EQ(internal::ToString(success_result),
340 ::testing::PrintToString(success_result));
341 EXPECT_EQ(internal::ToString(success_result_with_printable_value),
342 ::testing::PrintToString(success_result_with_printable_value));
343
344 // The value printer will try to stream types to the GoogleTest ostream if
345 // possible, so it may not always match bt::internal::ToString's output.
346 EXPECT_EQ("[result: ok(1)]",
347 ::testing::PrintToString(success_result_with_value));
348 }
349
TEST(ErrorTest,BtIsErrorMacroCompiles)350 TEST(ErrorTest, BtIsErrorMacroCompiles) {
351 const fit::result general_error = ToResult(HostError::kFailed);
352 EXPECT_TRUE(bt_is_error(general_error, ERROR, "ErrorTest", "error message"));
353 const fit::result<Error<TestError>, int> success_with_value = fit::ok(1);
354 EXPECT_FALSE(
355 bt_is_error(success_with_value, ERROR, "ErrorTest", "error message"));
356 const fit::result<Error<TestError>, int> error_with_value =
357 fit::error(MakeError(TestError::kFail1));
358 EXPECT_TRUE(
359 bt_is_error(error_with_value, ERROR, "ErrorTest", "error message"));
360 }
361
TEST(ErrorTest,BtIsErrorOnlyEvaluatesResultOnce)362 TEST(ErrorTest, BtIsErrorOnlyEvaluatesResultOnce) {
363 int result_count = 0;
364 auto result_func = [&]() {
365 result_count++;
366 return ToResult(TestError::kFail1);
367 };
368 bt_is_error(result_func(), ERROR, "ErrorTest", "error message");
369 EXPECT_EQ(result_count, 1);
370 }
371
TEST(ErrorTest,BtStrMacroOnResult)372 TEST(ErrorTest, BtStrMacroOnResult) {
373 constexpr fit::result proto_error_result = ToResult(TestError::kFail2);
374 EXPECT_EQ(internal::ToString(proto_error_result), bt_str(proto_error_result));
375 }
376
377 } // namespace
378 } // namespace bt
379