Add BinderResult and expected<> gtest matchers
Add BinderResult which is short-hand for a Result with error-type
binder::Status, which is preferred type for implementing binder
interfaces.
Add gmock matchers which unwrap expected types in either the expected or
error directions for ease of testing.
Test: atest NativePermissionControllerTest with subsequent CL
Flag: EXEMPT: adding code without use
Bug: 338089555
Change-Id: I00f4f71e7d10cadfe6a0b2a628b887df9931e447
diff --git a/media/liberror/include/error/BinderResult.h b/media/liberror/include/error/BinderResult.h
new file mode 100644
index 0000000..1f1211c
--- /dev/null
+++ b/media/liberror/include/error/BinderResult.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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 <binder/Status.h>
+#include <error/expected_utils.h>
+#include <utils/Errors.h>
+
+namespace android {
+namespace error {
+
+/**
+ * A convenience short-hand for base::expected, where the error type is a binder::Status, for use
+ * when implementing binder services.
+ * Clients need to link against libbinder, since this library is header only.
+ */
+template <typename T>
+using BinderResult = base::expected<T, binder::Status>;
+
+inline base::unexpected<binder::Status> unexpectedExceptionCode(int32_t exceptionCode,
+ const char* s) {
+ return base::unexpected{binder::Status::fromExceptionCode(exceptionCode, s)};
+}
+
+inline base::unexpected<binder::Status> unexpectedServiceException(int32_t serviceSpecificCode,
+ const char* s) {
+ return base::unexpected{binder::Status::fromServiceSpecificError(serviceSpecificCode, s)};
+}
+
+} // namespace error
+} // namespace android
+
+inline std::string errorToString(const ::android::binder::Status& status) {
+ return std::string{status.toString8().c_str()};
+}
diff --git a/media/liberror/include/error/BinderStatusMatcher.h b/media/liberror/include/error/BinderStatusMatcher.h
new file mode 100644
index 0000000..11d9e65
--- /dev/null
+++ b/media/liberror/include/error/BinderStatusMatcher.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <ostream>
+
+#include <binder/Status.h>
+
+namespace android::error {
+
+class BinderStatusMatcher {
+ public:
+ using is_gtest_matcher = void;
+
+ explicit BinderStatusMatcher(binder::Status status) : status_(std::move(status)) {}
+
+ static BinderStatusMatcher hasException(binder::Status::Exception ex) {
+ return BinderStatusMatcher(binder::Status::fromExceptionCode(ex));
+ }
+
+ static BinderStatusMatcher isOk() { return BinderStatusMatcher(binder::Status::ok()); }
+
+ bool MatchAndExplain(const binder::Status& value,
+ ::testing::MatchResultListener* listener) const {
+ if (status_.exceptionCode() == value.exceptionCode() &&
+ status_.transactionError() == value.transactionError() &&
+ status_.serviceSpecificErrorCode() == value.serviceSpecificErrorCode()) {
+ return true;
+ }
+ *listener << "received binder status: " << value;
+ return false;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "contains binder status " << status_; }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "does not contain binder status " << status_;
+ }
+
+ private:
+ const binder::Status status_;
+};
+} // namespace android::error
diff --git a/media/liberror/include/error/ExpectedMatchers.h b/media/liberror/include/error/ExpectedMatchers.h
new file mode 100644
index 0000000..b81adbf
--- /dev/null
+++ b/media/liberror/include/error/ExpectedMatchers.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <ostream>
+#include <type_traits>
+
+namespace android::error {
+
+/**
+ * Example Usage:
+ * Given a function with signature
+ * Result<T, U> foo()
+ * Matchers can be used as follows:
+ * EXPECT_THAT(foo(), IsOkAnd(Eq(T{})));
+ * EXPECT_THAT(foo(), IsErrorAnd(Eq(U{})));
+ */
+template <typename ExpectedT>
+class IsOkAndImpl : public ::testing::MatcherInterface<ExpectedT> {
+ public:
+ using ValueT = std::remove_reference_t<ExpectedT>::value_type;
+
+ template <typename InnerMatcher>
+ explicit IsOkAndImpl(InnerMatcher innerMatcher)
+ : inner_matcher_(::testing::SafeMatcherCast<const ValueT&>(
+ std::forward<InnerMatcher>(innerMatcher))) {}
+
+ bool MatchAndExplain(ExpectedT val, ::testing::MatchResultListener* listener) const {
+ if (!val.has_value()) {
+ *listener << "which has error " << ::testing::PrintToString(val.error());
+ return false;
+ }
+ const auto res = inner_matcher_.MatchAndExplain(val.value(), listener);
+ if (!res) {
+ *listener << "which has value " << ::testing::PrintToString(val.value());
+ }
+ return res;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "contains expected value which ";
+ inner_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "does not contain expected, or contains expected value which ";
+ inner_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ ::testing::Matcher<const ValueT&> inner_matcher_;
+};
+
+template <typename InnerMatcher>
+class IsOkAnd {
+ public:
+ explicit IsOkAnd(InnerMatcher innerMatcher) : inner_matcher_(std::move(innerMatcher)) {}
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::Matcher<T>{new IsOkAndImpl<const T&>(inner_matcher_)};
+ }
+
+ private:
+ InnerMatcher inner_matcher_;
+};
+
+template <typename ExpectedT>
+class IsErrorAndImpl : public ::testing::MatcherInterface<ExpectedT> {
+ public:
+ using ErrorT = typename std::remove_reference_t<ExpectedT>::error_type;
+
+ template <typename InnerMatcher>
+ explicit IsErrorAndImpl(InnerMatcher innerMatcher)
+ : inner_matcher_(::testing::SafeMatcherCast<const ErrorT&>(
+ std::forward<InnerMatcher>(innerMatcher))) {}
+
+ bool MatchAndExplain(ExpectedT val, ::testing::MatchResultListener* listener) const {
+ if (val.has_value()) {
+ *listener << "which has value " << ::testing::PrintToString(val.value());
+ return false;
+ }
+
+ const auto res = inner_matcher_.MatchAndExplain(val.error(), listener);
+ if (!res) {
+ *listener << "which has error " << ::testing::PrintToString(val.error());
+ }
+ return res;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "contains error value which ";
+ inner_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "does not contain error value, or contains error value which ";
+ inner_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ ::testing::Matcher<const ErrorT&> inner_matcher_;
+};
+
+template <typename InnerMatcher>
+class IsErrorAnd {
+ public:
+ explicit IsErrorAnd(InnerMatcher innerMatcher) : inner_matcher_(std::move(innerMatcher)) {}
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::Matcher<T>{new IsErrorAndImpl<const T&>(inner_matcher_)};
+ }
+
+ private:
+ InnerMatcher inner_matcher_;
+};
+
+} // namespace android::error