diff --git a/init/builtins.cpp b/init/builtins.cpp index cc445be4c..0eb894bec 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -117,7 +117,7 @@ class ErrorIgnoreEnoent { android::base::GetMinimumLogSeverity() > android::base::DEBUG) {} template - operator android::base::expected>() { + operator android::base::expected>() { if (ignore_error_) { return {}; } diff --git a/libutils/Android.bp b/libutils/Android.bp index b0887c7d1..1f11f97fd 100644 --- a/libutils/Android.bp +++ b/libutils/Android.bp @@ -32,12 +32,14 @@ cc_library_headers { "libsystem_headers", "libcutils_headers", "libprocessgroup_headers", + "libbase_headers", ], export_header_lib_headers: [ "liblog_headers", "libsystem_headers", "libcutils_headers", "libprocessgroup_headers", + "libbase_headers", ], export_include_dirs: ["include"], @@ -300,13 +302,14 @@ cc_test { srcs: [ "BitSet_test.cpp", + "Errors_test.cpp", "FileMap_test.cpp", "LruCache_test.cpp", "Mutex_test.cpp", "SharedBuffer_test.cpp", "Singleton_test.cpp", - "String8_test.cpp", "String16_test.cpp", + "String8_test.cpp", "StrongPointer_test.cpp", "Timers_test.cpp", "Unicode_test.cpp", @@ -365,6 +368,7 @@ cc_test_library { "-Wall", "-Werror", ], + header_libs: ["libutils_headers"], } cc_test_library { @@ -377,6 +381,7 @@ cc_test_library { "-Werror", ], shared_libs: ["libutils_test_singleton1"], + header_libs: ["libutils_headers"], } cc_benchmark { diff --git a/libutils/Errors_test.cpp b/libutils/Errors_test.cpp new file mode 100644 index 000000000..873c99490 --- /dev/null +++ b/libutils/Errors_test.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/ErrorsMacros.h" + +#include + +#include + +using namespace android; + +using android::base::Error; +using android::base::Result; + +status_t success_or_fail(bool success) { + if (success) + return OK; + else + return PERMISSION_DENIED; +} + +TEST(errors, unwrap_or_return) { + auto f = [](bool success, int* val) -> status_t { + OR_RETURN(success_or_fail(success)); + *val = 10; + return OK; + }; + + int val; + status_t s = f(true, &val); + EXPECT_EQ(OK, s); + EXPECT_EQ(10, val); + + val = 0; // reset + status_t q = f(false, &val); + EXPECT_EQ(PERMISSION_DENIED, q); + EXPECT_EQ(0, val); +} + +TEST(errors, unwrap_or_return_result) { + auto f = [](bool success) -> Result { + OR_RETURN(success_or_fail(success)); + return "hello"; + }; + + auto r = f(true); + EXPECT_TRUE(r.ok()); + EXPECT_EQ("hello", *r); + + auto s = f(false); + EXPECT_FALSE(s.ok()); + EXPECT_EQ(PERMISSION_DENIED, s.error().code()); + EXPECT_EQ("PERMISSION_DENIED", s.error().message()); +} + +TEST(errors, unwrap_or_return_result_int) { + auto f = [](bool success) -> Result { + OR_RETURN(success_or_fail(success)); + return 10; + }; + + auto r = f(true); + EXPECT_TRUE(r.ok()); + EXPECT_EQ(10, *r); + + auto s = f(false); + EXPECT_FALSE(s.ok()); + EXPECT_EQ(PERMISSION_DENIED, s.error().code()); + EXPECT_EQ("PERMISSION_DENIED", s.error().message()); +} + +TEST(errors, unwrap_or_fatal) { + OR_FATAL(success_or_fail(true)); + + EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "PERMISSION_DENIED"); +} + +TEST(errors, result_in_status) { + auto f = [](bool success) -> Result { + if (success) + return "OK"; + else + return Error(PERMISSION_DENIED) << "custom error message"; + }; + + auto g = [&](bool success) -> status_t { + std::string val = OR_RETURN(f(success)); + EXPECT_EQ("OK", val); + return OK; + }; + + status_t a = g(true); + EXPECT_EQ(OK, a); + + status_t b = g(false); + EXPECT_EQ(PERMISSION_DENIED, b); +} diff --git a/libutils/include/utils/ErrorsMacros.h b/libutils/include/utils/ErrorsMacros.h new file mode 100644 index 000000000..048c5386f --- /dev/null +++ b/libutils/include/utils/ErrorsMacros.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Errors.h" + +// It would have been better if this file (ErrorsMacros.h) is entirely in utils/Errors.h. However +// that is infeasible as some (actually many) are using utils/Errors.h via the implicit include path +// `system/core/include` [1]. Since such users are not guaranteed to specify the dependency to +// libbase_headers, the following headers from libbase_headers can't be found. +// [1] build/soong/cc/config/global.go#commonGlobalIncludes +#include +#include + +#include + +namespace android { + +// StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating +// Result and Error template classes. This is required to distinguish status_t from +// other integer-based error code types like errno, and also to provide utility functions like +// print(). +struct StatusT { + StatusT() : val_(OK) {} + StatusT(status_t s) : val_(s) {} + const status_t& value() const { return val_; } + operator status_t() const { return val_; } + std::string print() const { return statusToString(val_); } + + status_t val_; +}; + +namespace base { + +// Specialization of android::base::OkOrFail for V = status_t. This is used to use the OR_RETURN +// and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h +// for the detailed contract. +template <> +struct OkOrFail { + // Tests if status_t is a success value of not. + static bool IsOk(const status_t& s) { return s == OK; } + + // Unwrapping status_t in the success case is just asserting that it is actually a success. + // We don't return OK because it would be redundant. + static void Unwrap([[maybe_unused]] status_t&& s) { assert(IsOk(s)); } + + // Consumes status_t when it's a fail value + static OkOrFail Fail(status_t&& s) { + assert(!IsOk(s)); + return OkOrFail{s}; + } + status_t val_; + + // And converts back into status_t. This is used when OR_RETURN is used in a function whose + // return type is status_t. + operator status_t() && { return val_; } + + // Or converts into Result. This is used when OR_RETURN is used in a function whose + // return type is Result. + template >> + operator Result() && { + return Error(std::move(val_)); + } + + operator Result() && { return Error(std::move(val_)); } + + // String representation of the error value. + static std::string ErrorMessage(const status_t& s) { return statusToString(s); } +}; + +} // namespace base +} // namespace android