xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/common/error_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_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