Snap for 8570526 from 2b9f1ae9aed225b81ee6864d8c65df00d9c8cd15 to mainline-networking-release
Change-Id: I9f7b6b7e0a2d0fcc703b35bf1b89f3c13bc3c366
diff --git a/Android.bp b/Android.bp
index 461f1db..ef56692 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,6 +57,9 @@
native_bridge_supported: true,
export_include_dirs: ["include"],
+ header_libs: ["fmtlib_headers"],
+ export_header_lib_headers: ["fmtlib_headers"],
+
target: {
linux_bionic: {
enabled: true,
@@ -80,10 +83,12 @@
"chrono_utils.cpp",
"cmsg.cpp",
"file.cpp",
+ "hex.cpp",
"logging.cpp",
"mapped_file.cpp",
"parsebool.cpp",
"parsenetaddress.cpp",
+ "posix_strerror_r.cpp",
"process.cpp",
"properties.cpp",
"stringprintf.cpp",
@@ -146,7 +151,6 @@
"libbase_headers",
],
export_header_lib_headers: ["libbase_headers"],
- static_libs: ["fmtlib"],
whole_static_libs: ["fmtlib"],
export_static_lib_headers: ["fmtlib"],
apex_available: [
@@ -161,9 +165,7 @@
sdk_version: "current",
stl: "c++_static",
export_include_dirs: ["include"],
- static_libs: ["fmtlib_ndk"],
whole_static_libs: ["fmtlib_ndk"],
- export_static_lib_headers: ["fmtlib_ndk"],
}
// Tests
@@ -180,6 +182,7 @@
"expected_test.cpp",
"file_test.cpp",
"function_ref_test.cpp",
+ "hex_test.cpp",
"logging_splitters_test.cpp",
"logging_test.cpp",
"macros_test.cpp",
@@ -215,6 +218,7 @@
},
local_include_dirs: ["."],
shared_libs: ["libbase"],
+ static_libs: ["libgmock"],
compile_multilib: "both",
multilib: {
lib32: {
@@ -244,6 +248,37 @@
test_suites: ["device_tests"],
}
+// Can be removed when we move to c++20
+cc_test {
+ name: "libbase_result_constraint_test",
+ defaults: ["libbase_cflags_defaults"],
+ host_supported: true,
+ srcs: [
+ "result_test_constraint.cpp",
+ ],
+ target: {
+ android: {
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
+ },
+ cpp_std: "gnu++20",
+ local_include_dirs: ["."],
+ shared_libs: ["libbase"],
+ static_libs: ["libgmock"],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ test_suites: ["device-tests"],
+}
+
cc_benchmark {
name: "libbase_benchmark",
defaults: ["libbase_cflags_defaults"],
diff --git a/OWNERS b/OWNERS
index 97777f7..682a067 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1 @@
enh@google.com
-jmgao@google.com
-tomcherry@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 3659bee..a3b2bfc 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libbase_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libbase_test"
+ }
]
}
diff --git a/hex.cpp b/hex.cpp
new file mode 100644
index 0000000..a4b7715
--- /dev/null
+++ b/hex.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "android-base/hex.h"
+
+#include "android-base/logging.h"
+
+namespace android {
+namespace base {
+
+std::string HexString(const void* bytes, size_t len) {
+ CHECK(bytes != nullptr || len == 0) << bytes << " " << len;
+
+ // b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ const char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+
+} // namespace base
+} // namespace android
diff --git a/hex_test.cpp b/hex_test.cpp
new file mode 100644
index 0000000..ebf798c
--- /dev/null
+++ b/hex_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "android-base/hex.h"
+
+#include <gtest/gtest.h>
+
+TEST(hex, empty) {
+ ASSERT_EQ("", android::base::HexString(nullptr, 0));
+ ASSERT_EQ("", android::base::HexString(reinterpret_cast<void*>(int32_t(0)), 0));
+}
+
+TEST(hex, short) {
+ const int32_t kShortData = 0xDEADBEEF;
+ ASSERT_EQ("ef", android::base::HexString(&kShortData, 1));
+ ASSERT_EQ("efbe", android::base::HexString(&kShortData, 2));
+ ASSERT_EQ("efbead", android::base::HexString(&kShortData, 3));
+ ASSERT_EQ("efbeadde", android::base::HexString(&kShortData, 4));
+}
+
+TEST(hex, all) {
+ constexpr size_t kSize = 256;
+ uint8_t kLongData[kSize];
+ for (size_t i = 0; i < kSize; i++) {
+ kLongData[i] = i;
+ }
+
+ ASSERT_EQ(
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d"
+ "2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b"
+ "5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f80818283848586878889"
+ "8a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7"
+ "b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5"
+ "e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ android::base::HexString(&kLongData, kSize));
+}
diff --git a/include/android-base/endian.h b/include/android-base/endian.h
index 8fa6365..b47494b 100644
--- a/include/android-base/endian.h
+++ b/include/android-base/endian.h
@@ -25,18 +25,19 @@
#include <sys/endian.h>
-#elif defined(__GLIBC__)
+#elif defined(__GLIBC__) || defined(ANDROID_HOST_MUSL)
-/* glibc's <endian.h> is like bionic's <sys/endian.h>. */
+/* glibc and musl's <endian.h> are like bionic's <sys/endian.h>. */
#include <endian.h>
-/* glibc keeps htons and htonl in <netinet/in.h>. */
+/* glibc and musl keep htons and htonl in <netinet/in.h>. */
#include <netinet/in.h>
-/* glibc doesn't have the 64-bit variants. */
+/* glibc and musl don't have the 64-bit variants. */
#define htonq(x) htobe64(x)
#define ntohq(x) be64toh(x)
+#if defined(__GLIBC__)
/* glibc has different names to BSD for these. */
#define betoh16(x) be16toh(x)
#define betoh32(x) be32toh(x)
@@ -44,6 +45,7 @@
#define letoh16(x) le16toh(x)
#define letoh32(x) le32toh(x)
#define letoh64(x) le64toh(x)
+#endif
#else
diff --git a/include/android-base/errors.h b/include/android-base/errors.h
index 06f29fc..61fdbd8 100644
--- a/include/android-base/errors.h
+++ b/include/android-base/errors.h
@@ -29,6 +29,8 @@
#pragma once
+#include <assert.h>
+
#include <string>
namespace android {
@@ -41,3 +43,102 @@
} // namespace base
} // namespace android
+
+// Convenient macros for evaluating a statement, checking if the result is error, and returning it
+// to the caller.
+//
+// Usage with Result<T>:
+//
+// Result<Foo> getFoo() {...}
+//
+// Result<Bar> getBar() {
+// Foo foo = OR_RETURN(getFoo());
+// return Bar{foo};
+// }
+//
+// Usage with status_t:
+//
+// status_t getFoo(Foo*) {...}
+//
+// status_t getBar(Bar* bar) {
+// Foo foo;
+// OR_RETURN(getFoo(&foo));
+// *bar = Bar{foo};
+// return OK;
+// }
+//
+// Actually this can be used for any type as long as the OkOrFail<T> contract is satisfied. See
+// below.
+// If implicit conversion compilation errors occur involving a value type with a templated
+// forwarding ref ctor, compilation with cpp20 or explicitly converting to the desired
+// return type is required.
+#define OR_RETURN(expr) \
+ ({ \
+ decltype(expr)&& tmp = (expr); \
+ typedef android::base::OkOrFail<std::remove_reference_t<decltype(tmp)>> ok_or_fail; \
+ if (!ok_or_fail::IsOk(tmp)) { \
+ return ok_or_fail::Fail(std::move(tmp)); \
+ } \
+ ok_or_fail::Unwrap(std::move(tmp)); \
+ })
+
+// Same as OR_RETURN, but aborts if expr is a failure.
+#if defined(__BIONIC__)
+#define OR_FATAL(expr) \
+ ({ \
+ decltype(expr)&& tmp = (expr); \
+ typedef android::base::OkOrFail<std::remove_reference_t<decltype(tmp)>> ok_or_fail; \
+ if (!ok_or_fail::IsOk(tmp)) { \
+ __assert(__FILE__, __LINE__, ok_or_fail::ErrorMessage(tmp).c_str()); \
+ } \
+ ok_or_fail::Unwrap(std::move(tmp)); \
+ })
+#else
+#define OR_FATAL(expr) \
+ ({ \
+ decltype(expr)&& tmp = (expr); \
+ typedef android::base::OkOrFail<std::remove_reference_t<decltype(tmp)>> ok_or_fail; \
+ if (!ok_or_fail::IsOk(tmp)) { \
+ fprintf(stderr, "%s:%d: assertion \"%s\" failed", __FILE__, __LINE__, \
+ ok_or_fail::ErrorMessage(tmp).c_str()); \
+ abort(); \
+ } \
+ ok_or_fail::Unwrap(std::move(tmp)); \
+ })
+#endif
+
+namespace android {
+namespace base {
+
+// The OkOrFail contract for a type T. This must be implemented for a type T if you want to use
+// OR_RETURN(stmt) where stmt evalues to a value of type T.
+template <typename T, typename = void>
+struct OkOrFail {
+ // Checks if T is ok or fail.
+ static bool IsOk(const T&);
+
+ // Turns T into the success value.
+ template <typename U>
+ static U Unwrap(T&&);
+
+ // Moves T into OkOrFail<T>, so that we can convert it to other types
+ OkOrFail(T&& v);
+ OkOrFail() = delete;
+ OkOrFail(const T&) = delete;
+
+ // And there need to be one or more conversion operators that turns the error value of T into a
+ // target type. For example, for T = Result<V, E>, there can be ...
+ //
+ // // for the case where OR_RETURN is called in a function expecting E
+ // operator E()&& { return val_.error().code(); }
+ //
+ // // for the case where OR_RETURN is called in a function expecting Result<U, E>
+ // template <typename U>
+ // operator Result<U, E>()&& { return val_.error(); }
+
+ // Returns the string representation of the fail value.
+ static std::string ErrorMessage(const T& v);
+};
+
+} // namespace base
+} // namespace android
diff --git a/include/android-base/expected.h b/include/android-base/expected.h
index 11cf1ac..3b9d45f 100644
--- a/include/android-base/expected.h
+++ b/include/android-base/expected.h
@@ -37,7 +37,7 @@
//
// void test() {
// auto q = safe_divide(10, 0);
-// if (q) { printf("%f\n", q.value()); }
+// if (q.ok()) { printf("%f\n", q.value()); }
// else { printf("%s\n", q.error().c_str()); }
// }
//
diff --git a/include/android-base/hex.h b/include/android-base/hex.h
new file mode 100644
index 0000000..cbb26a8
--- /dev/null
+++ b/include/android-base/hex.h
@@ -0,0 +1,31 @@
+/*
+ * 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 <string>
+
+namespace android {
+namespace base {
+
+// Converts binary data into a hexString.
+//
+// Hex values are printed in order, e.g. 0xDEAD will result in 'adde' because
+// Android is little-endian.
+std::string HexString(const void* bytes, size_t len);
+
+} // namespace base
+} // namespace android
diff --git a/include/android-base/result-gmock.h b/include/android-base/result-gmock.h
new file mode 100644
index 0000000..1fd9f00
--- /dev/null
+++ b/include/android-base/result-gmock.h
@@ -0,0 +1,101 @@
+/*
+ * 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 <android-base/result.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+/*
+ * Matchers for android::base::Result<T> that produces human-readable test results.
+ *
+ * Example:
+ *
+ * Result<int> result = ...
+ *
+ * using namespace ::android::base::testing;
+ * using namespace ::testing;
+ *
+ * EXPECT_THAT(result, Ok());
+ * EXPECT_THAT(result, Not(Ok()));
+ * EXPECT_THAT(result, HasValue(5));
+ * EXPECT_THAT(result, HasError(WithCode(EBADF)));
+ * EXPECT_THAT(result, HasError(WithMessageMessage("expected error message")));
+ *
+ * // Advance usage
+ * EXPECT_THAT(result, AnyOf(Ok(), HasError(WithCode(EACCES)));
+ * EXPECT_THAT(result, HasError(WithCode(AnyOf(EBADF, EACCES))) << "Unexpected code from library";
+ */
+
+namespace android::base {
+
+template <typename T>
+inline void PrintTo(const Result<T>& result, std::ostream* os) {
+ if (result.ok()) {
+ *os << "OK: " << ::testing::PrintToString(result.value());
+ } else {
+ *os << "Error: " << result.error();
+ }
+}
+
+template <>
+inline void PrintTo(const Result<void>& result, std::ostream* os) {
+ if (result.ok()) {
+ *os << "OK";
+ } else {
+ *os << "Error: " << result.error();
+ }
+}
+
+namespace testing {
+
+MATCHER(Ok, "") {
+ if (arg.ok()) {
+ *result_listener << "result is OK";
+ return true;
+ }
+ *result_listener << "error is " << arg.error();
+ return false;
+}
+
+MATCHER_P(HasValue, value_matcher, "") {
+ if (arg.ok()) {
+ return ::testing::ExplainMatchResult(value_matcher, arg.value(), result_listener);
+ }
+ *result_listener << "error is " << arg.error();
+ return false;
+}
+
+MATCHER_P(HasError, error_matcher, "") {
+ if (!arg.ok()) {
+ return ::testing::ExplainMatchResult(error_matcher, arg.error(), result_listener);
+ }
+ *result_listener << "result is OK";
+ return false;
+}
+
+MATCHER_P(WithCode, code_matcher, "") {
+ *result_listener << "actual error is " << arg;
+ return ::testing::ExplainMatchResult(code_matcher, arg.code(), result_listener);
+}
+
+MATCHER_P(WithMessage, message_matcher, "") {
+ *result_listener << "actual error is " << arg;
+ return ::testing::ExplainMatchResult(message_matcher, arg.message(), result_listener);
+}
+
+} // namespace testing
+} // namespace android::base
diff --git a/include/android-base/result.h b/include/android-base/result.h
index 56a4f3e..9715a8c 100644
--- a/include/android-base/result.h
+++ b/include/android-base/result.h
@@ -14,125 +14,212 @@
* limitations under the License.
*/
-// This file contains classes for returning a successful result along with an optional
-// arbitrarily typed return value or for returning a failure result along with an optional string
-// indicating why the function failed.
-
-// There are 3 classes that implement this functionality and one additional helper type.
+// Result<T, E> is the type that is used to pass a success value of type T or an error code of type
+// E, optionally together with an error message. T and E can be any type. If E is omitted it
+// defaults to int, which is useful when errno(3) is used as the error code.
//
-// Result<T> either contains a member of type T that can be accessed using similar semantics as
-// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
-// Result<T>::error().
+// Passing a success value or an error value:
//
-// ResultError is a type that contains both a std::string describing the error and a copy of errno
-// from when the error occurred. ResultError can be used in an ostream directly to print its
-// string value.
-//
-// Result<void> is the correct return type for a function that either returns successfully or
-// returns an error value. Returning {} from a function that returns Result<void> is the
-// correct way to indicate that a function without a return type has completed successfully.
-//
-// A successful Result<T> is constructed implicitly from any type that can be implicitly converted
-// to T or from the constructor arguments for T. This allows you to return a type T directly from
-// a function that returns Result<T>.
-//
-// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
-// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
-// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
-// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
-// value can be directly specified via the Error() constructor.
-//
-// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
-// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
-//
-// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
-// In this case, the string that the ResultError takes is passed through the stream normally, but
-// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
-// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
-// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
-// used, the errno of the last one is respected.
-//
-// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
-// function that return Result<T> but you have a Result<U> and want to return its error. In this
-// case, you can return the .error() from the Result<U> to construct the Result<T>.
-
-// An example of how to use these is below:
-// Result<U> CalculateResult(const T& input) {
-// U output;
-// if (!SomeOtherCppFunction(input, &output)) {
-// return Errorf("SomeOtherCppFunction {} failed", input);
+// Result<std::string> readFile() {
+// std::string content;
+// if (base::ReadFileToString("path", &content)) {
+// return content; // ok case
+// } else {
+// return ErrnoError() << "failed to read"; // error case
// }
-// if (!c_api_function(output)) {
-// return ErrnoErrorf("c_api_function {} failed", output);
-// }
-// return output;
// }
//
-// auto output = CalculateResult(input);
-// if (!output) return Error() << "CalculateResult failed: " << output.error();
-// UseOutput(*output);
+// Checking the result and then unwrapping the value or propagating the error:
+//
+// Result<bool> hasAWord() {
+// auto content = readFile();
+// if (!content.ok()) {
+// return Error() << "failed to process: " << content.error();
+// }
+// return (*content.find("happy") != std::string::npos);
+// }
+//
+// Using custom error code type:
+//
+// enum class MyError { A, B }; // assume that this is the error code you already have
+//
+// // To use the error code with Result, define a wrapper class that provides the following
+// operations and use the wrapper class as the second type parameter (E) when instantiating
+// Result<T, E>
+//
+// 1. default constructor
+// 2. copy constructor / and move constructor if copying is expensive
+// 3. conversion operator to the error code type
+// 4. value() function that return the error code value
+// 5. print() function that gives a string representation of the error ode value
+//
+// struct MyErrorWrapper {
+// MyError val_;
+// MyErrorWrapper() : val_(/* reasonable default value */) {}
+// MyErrorWrapper(MyError&& e) : val_(std:forward<MyError>(e)) {}
+// operator const MyError&() const { return val_; }
+// MyError value() const { return val_; }
+// std::string print() const {
+// switch(val_) {
+// MyError::A: return "A";
+// MyError::B: return "B";
+// }
+// }
+// };
+//
+// #define NewMyError(e) Error<MyErrorWrapper>(MyError::e)
+//
+// Result<T, MyError> val = NewMyError(A) << "some message";
+//
+// Formatting the error message using fmtlib:
+//
+// Errorf("{} errors", num); // equivalent to Error() << num << " errors";
+// ErrnoErrorf("{} errors", num); // equivalent to ErrnoError() << num << " errors";
+//
+// Returning success or failure, but not the value:
+//
+// Result<void> doSomething() {
+// if (success) return {};
+// else return Error() << "error occurred";
+// }
+//
+// Extracting error code:
+//
+// Result<T> val = Error(3) << "some error occurred";
+// assert(3 == val.error().code());
+//
#pragma once
+#include <assert.h>
#include <errno.h>
#include <sstream>
#include <string>
+#include <type_traits>
+#include "android-base/errors.h"
#include "android-base/expected.h"
#include "android-base/format.h"
namespace android {
namespace base {
+// Errno is a wrapper class for errno(3). Use this type instead of `int` when instantiating
+// `Result<T, E>` and `Error<E>` template classes. This is required to distinguish errno from other
+// integer-based error code types like `status_t`.
+struct Errno {
+ Errno() : val_(0) {}
+ Errno(int e) : val_(e) {}
+ int value() const { return val_; }
+ operator int() const { return value(); }
+ std::string print() const { return strerror(value()); }
+
+ int val_;
+
+ // TODO(b/209929099): remove this conversion operator. This currently is needed to not break
+ // existing places where error().code() is used to construct enum values.
+ template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
+ operator E() const {
+ return E(val_);
+ }
+};
+
+template <typename E = Errno, bool include_message = true>
struct ResultError {
- template <typename T>
- ResultError(T&& message, int code) : message_(std::forward<T>(message)), code_(code) {}
+ template <typename T, typename P, typename = std::enable_if_t<std::is_convertible_v<P, E>>>
+ ResultError(T&& message, P&& code)
+ : message_(std::forward<T>(message)), code_(E(std::forward<P>(code))) {}
template <typename T>
// NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(message_, code_));
+ operator android::base::expected<T, ResultError<E>>() const {
+ return android::base::unexpected(ResultError<E>(message_, code_));
}
std::string message() const { return message_; }
- int code() const { return code_; }
+ const E& code() const { return code_; }
private:
std::string message_;
- int code_;
+ E code_;
};
-inline bool operator==(const ResultError& lhs, const ResultError& rhs) {
+template <typename E>
+struct ResultError<E, /* include_message */ false> {
+ template <typename P, typename = std::enable_if_t<std::is_convertible_v<P, E>>>
+ ResultError(P&& code) : code_(E(std::forward<P>(code))) {}
+
+ template <typename T>
+ operator android::base::expected<T, ResultError<E, false>>() const {
+ return android::base::unexpected(ResultError<E, false>(code_));
+ }
+
+ const E& code() const { return code_; }
+
+ private:
+ E code_;
+};
+
+template <typename E>
+inline bool operator==(const ResultError<E>& lhs, const ResultError<E>& rhs) {
return lhs.message() == rhs.message() && lhs.code() == rhs.code();
}
-inline bool operator!=(const ResultError& lhs, const ResultError& rhs) {
+template <typename E>
+inline bool operator!=(const ResultError<E>& lhs, const ResultError<E>& rhs) {
return !(lhs == rhs);
}
-inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
+template <typename E>
+inline std::ostream& operator<<(std::ostream& os, const ResultError<E>& t) {
os << t.message();
return os;
}
+namespace internal {
+// Stream class that does nothing and is has zero (actually 1) size. It is used instead of
+// std::stringstream when include_message is false so that we use less on stack.
+// sizeof(std::stringstream) is 280 on arm64.
+struct DoNothingStream {
+ template <typename T>
+ DoNothingStream& operator<<(T&&) {
+ return *this;
+ }
+
+ std::string str() const { return ""; }
+};
+} // namespace internal
+
+template <typename E = Errno, bool include_message = true,
+ typename = std::enable_if_t<!std::is_same_v<E, int>>>
class Error {
public:
- Error() : errno_(0), append_errno_(false) {}
+ Error() : code_(0), has_code_(false) {}
+ template <typename P, typename = std::enable_if_t<std::is_convertible_v<P, E>>>
// NOLINTNEXTLINE(google-explicit-constructor)
- Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
+ Error(P&& code) : code_(std::forward<P>(code)), has_code_(true) {}
- template <typename T>
+ template <typename T, typename P, typename = std::enable_if_t<std::is_convertible_v<E, P>>>
// NOLINTNEXTLINE(google-explicit-constructor)
- operator android::base::expected<T, ResultError>() {
- return android::base::unexpected(ResultError(str(), errno_));
+ operator android::base::expected<T, ResultError<P>>() const {
+ return android::base::unexpected(ResultError<P>(str(), static_cast<P>(code_)));
+ }
+
+ template <typename T, typename P, typename = std::enable_if_t<std::is_convertible_v<E, P>>>
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator android::base::expected<T, ResultError<P, false>>() const {
+ return android::base::unexpected(ResultError<P, false>(static_cast<P>(code_)));
}
template <typename T>
Error& operator<<(T&& t) {
+ static_assert(include_message, "<< not supported when include_message = false");
// NOLINTNEXTLINE(bugprone-suspicious-semicolon)
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
- errno_ = t.code();
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError<E>>) {
+ if (!has_code_) {
+ code_ = t.code();
+ }
return (*this) << t.message();
}
int saved = errno;
@@ -142,12 +229,13 @@
}
const std::string str() const {
+ static_assert(include_message, "str() not supported when include_message = false");
std::string str = ss_.str();
- if (append_errno_) {
+ if (has_code_) {
if (str.empty()) {
- return strerror(errno_);
+ return code_.print();
}
- return std::move(str) + ": " + strerror(errno_);
+ return std::move(str) + ": " + code_.print();
}
return str;
}
@@ -164,72 +252,183 @@
friend Error ErrnoErrorfImpl(const T&& fmt, const Args&... args);
private:
- Error(bool append_errno, int errno_to_append, const std::string& message)
- : errno_(errno_to_append), append_errno_(append_errno) {
+ Error(bool has_code, E code, const std::string& message) : code_(code), has_code_(has_code) {
(*this) << message;
}
- std::stringstream ss_;
- int errno_;
- const bool append_errno_;
+ std::conditional_t<include_message, std::stringstream, internal::DoNothingStream> ss_;
+ E code_;
+ const bool has_code_;
};
-inline Error ErrnoError() {
- return Error(errno);
+inline Error<Errno> ErrnoError() {
+ return Error<Errno>(Errno{errno});
}
-inline int ErrorCode(int code) {
+template <typename E>
+inline E ErrorCode(E code) {
return code;
}
// Return the error code of the last ResultError object, if any.
// Otherwise, return `code` as it is.
-template <typename T, typename... Args>
-inline int ErrorCode(int code, T&& t, const Args&... args) {
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
+template <typename T, typename E, typename... Args>
+inline E ErrorCode(E code, T&& t, const Args&... args) {
+ if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError<E>>) {
return ErrorCode(t.code(), args...);
}
return ErrorCode(code, args...);
}
template <typename T, typename... Args>
-inline Error ErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
+inline Error<Errno> ErrorfImpl(const T&& fmt, const Args&... args) {
+ return Error(false, ErrorCode(Errno{}, args...), fmt::format(fmt, args...));
}
template <typename T, typename... Args>
-inline Error ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
- return Error(true, errno, fmt::format(fmt, args...));
+inline Error<Errno> ErrnoErrorfImpl(const T&& fmt, const Args&... args) {
+ return Error<Errno>(true, Errno{errno}, fmt::format(fmt, args...));
}
#define Errorf(fmt, ...) android::base::ErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(FMT_STRING(fmt), ##__VA_ARGS__)
+template <typename T, typename E = Errno, bool include_message = true>
+using Result = android::base::expected<T, ResultError<E, include_message>>;
+
+// Specialization of android::base::OkOrFail<V> for V = Result<T, E>. See android-base/errors.h
+// for the contract.
+
+namespace impl {
+template <typename U>
+using Code = std::decay_t<decltype(std::declval<U>().error().code())>;
+
+template <typename U>
+using ErrorType = std::decay_t<decltype(std::declval<U>().error())>;
+
+template <typename U>
+constexpr bool IsNumeric = std::is_integral_v<U> || std::is_floating_point_v<U> ||
+ (std::is_enum_v<U> && std::is_convertible_v<U, size_t>);
+
+// This base class exists to take advantage of shadowing
+// We include the conversion in this base class so that if the conversion in NumericConversions
+// overlaps, we (arbitrarily) choose the implementation in NumericConversions due to shadowing.
template <typename T>
-using Result = android::base::expected<T, ResultError>;
+struct ConversionBase {
+ ErrorType<T> error_;
+ // T is a expected<U, ErrorType<T>>.
+ operator const T() const && {
+ return unexpected(std::move(error_));
+ }
+
+ operator const Code<T>() const && {
+ return error_.code();
+ }
+
+};
+
+// User defined conversions can be followed by numeric conversions
+// Although we template specialize for the exact code type, we need
+// specializations for conversions to all numeric types to avoid an
+// ambiguous conversion sequence.
+template <typename T, typename = void>
+struct NumericConversions : public ConversionBase<T> {};
+template <typename T>
+struct NumericConversions<T,
+ std::enable_if_t<impl::IsNumeric<impl::Code<T>>>
+ > : public ConversionBase<T>
+{
+#pragma push_macro("SPECIALIZED_CONVERSION")
+#define SPECIALIZED_CONVERSION(type)\
+ operator const expected<type, ErrorType<T>>() const &&\
+ { return unexpected(std::move(this->error_));}
+
+ SPECIALIZED_CONVERSION(int)
+ SPECIALIZED_CONVERSION(short int)
+ SPECIALIZED_CONVERSION(unsigned short int)
+ SPECIALIZED_CONVERSION(unsigned int)
+ SPECIALIZED_CONVERSION(long int)
+ SPECIALIZED_CONVERSION(unsigned long int)
+ SPECIALIZED_CONVERSION(long long int)
+ SPECIALIZED_CONVERSION(unsigned long long int)
+ SPECIALIZED_CONVERSION(bool)
+ SPECIALIZED_CONVERSION(char)
+ SPECIALIZED_CONVERSION(unsigned char)
+ SPECIALIZED_CONVERSION(signed char)
+ SPECIALIZED_CONVERSION(wchar_t)
+ SPECIALIZED_CONVERSION(char16_t)
+ SPECIALIZED_CONVERSION(char32_t)
+ SPECIALIZED_CONVERSION(float)
+ SPECIALIZED_CONVERSION(double)
+ SPECIALIZED_CONVERSION(long double)
+
+#undef SPECIALIZED_CONVERSION
+#pragma pop_macro("SPECIALIZED_CONVERSION")
+ // For debugging purposes
+ using IsNumericT = std::true_type;
+};
+
+#ifdef __cpp_concepts
+template<class U>
+concept Trivial = std::is_same_v<U, U>;
+#endif
+} // namespace impl
+
+template <typename T, typename E, bool include_message>
+struct OkOrFail<Result<T, E, include_message>>
+ : public impl::NumericConversions<Result<T, E, include_message>> {
+ using V = Result<T, E, include_message>;
+ using Err = impl::ErrorType<V>;
+ using C = impl::Code<V>;
+private:
+ OkOrFail(Err&& v): impl::NumericConversions<V>{std::move(v)} {}
+ OkOrFail(const OkOrFail& other) = delete;
+ OkOrFail(const OkOrFail&& other) = delete;
+public:
+ // Checks if V is ok or fail
+ static bool IsOk(const V& val) { return val.ok(); }
+
+ // Turns V into a success value
+ static T Unwrap(V&& val) { return std::move(val.value()); }
+
+ // Consumes V when it's a fail value
+ static const OkOrFail<V> Fail(V&& v) {
+ assert(!IsOk(v));
+ return OkOrFail<V>{std::move(v.error())};
+ }
+
+ // We specialize as much as possible to avoid ambiguous conversion with
+ // templated expected ctor
+ operator const Result<C, E, include_message>() const && {
+ return unexpected(std::move(this->error_));
+ }
+#ifdef __cpp_concepts
+ template <impl::Trivial U>
+#else
+ template <typename U>
+#endif
+ operator const Result<U, E, include_message>() const && {
+ return unexpected(std::move(this->error_));
+ }
+
+ static std::string ErrorMessage(const V& val) { return val.error().message(); }
+};
// Macros for testing the results of functions that return android::base::Result.
// These also work with base::android::expected.
+// For advanced matchers and customized error messages, see result-gtest.h.
-#define CHECK_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- CHECK(tmp.ok()) << tmp.error(); \
- } while (0)
+#define ASSERT_RESULT_OK(stmt) \
+ if (const auto& tmp = (stmt); !tmp.ok()) \
+ FAIL() << "Value of: " << #stmt << "\n" \
+ << " Actual: " << tmp.error().message() << "\n" \
+ << "Expected: is ok\n"
-#define ASSERT_RESULT_OK(stmt) \
- do { \
- const auto& tmp = (stmt); \
- ASSERT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-#define EXPECT_RESULT_OK(stmt) \
- do { \
- auto tmp = (stmt); \
- EXPECT_TRUE(tmp.ok()) << tmp.error(); \
- } while (0)
-
-// TODO: Maybe add RETURN_IF_ERROR() and ASSIGN_OR_RETURN()
+#define EXPECT_RESULT_OK(stmt) \
+ if (const auto& tmp = (stmt); !tmp.ok()) \
+ ADD_FAILURE() << "Value of: " << #stmt << "\n" \
+ << " Actual: " << tmp.error().message() << "\n" \
+ << "Expected: is ok\n"
} // namespace base
} // namespace android
diff --git a/include/android-base/scopeguard.h b/include/android-base/scopeguard.h
index 5a224d6..8293b2c 100644
--- a/include/android-base/scopeguard.h
+++ b/include/android-base/scopeguard.h
@@ -26,14 +26,14 @@
template <typename F>
class ScopeGuard {
public:
- ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
+ constexpr ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
- ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
+ constexpr ScopeGuard(ScopeGuard&& that) noexcept : f_(std::move(that.f_)), active_(that.active_) {
that.active_ = false;
}
template <typename Functor>
- ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ constexpr ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
that.active_ = false;
}
@@ -48,7 +48,7 @@
void Disable() { active_ = false; }
- bool active() const { return active_; }
+ constexpr bool active() const { return active_; }
private:
template <typename Functor>
diff --git a/include/android-base/silent_death_test.h b/include/android-base/silent_death_test.h
index 2aec890..261fa74 100644
--- a/include/android-base/silent_death_test.h
+++ b/include/android-base/silent_death_test.h
@@ -17,34 +17,113 @@
#pragma once
#include <signal.h>
-
#include <gtest/gtest.h>
+#include <array>
+#include <memory>
+
#if !defined(__BIONIC__)
#define sigaction64 sigaction
#endif
-// Disables debuggerd stack traces to speed up death tests and make them less
-// noisy in logcat.
+// INTRODUCTION
//
-// Use `using my_DeathTest = SilentDeathTest;` instead of inheriting from
-// testing::Test yourself.
-class SilentDeathTest : public testing::Test {
- protected:
- virtual void SetUp() {
- // Suppress debuggerd stack traces. Too slow.
- for (int signo : {SIGABRT, SIGBUS, SIGSEGV, SIGSYS}) {
+// It can be useful to disable debuggerd stack traces/tombstones in death tests.
+// Reasons include:
+//
+// 1. speeding up death tests
+// 2. reducing the noise in logcat (for humans)
+// 3. avoiding bots from thinking that expected deaths should be reported in
+// stability metrics/have bugs auto-filed
+//
+// When writing new death tests, inherit the test suite from SilentDeathTest
+// defined below.
+//
+// Only use ScopedSilentDeath in a test case/suite if changing the test base
+// class from testing::Test to SilentDeathTest adds additional complextity when
+// test suite code is shared between death and non-death tests.
+//
+// EXAMPLES
+//
+// For example, use SilentDeathTest for this simple case where there's no shared
+// setup or teardown:
+//
+// using FooDeathTest = SilentDeathTest;
+//
+// TEST(FooTest, DoesThis) {
+// // normal test
+// }
+//
+// TEST_F(FooDeathTest, DoesThat) {
+// // death test
+// }
+//
+// Alternatively, use ScopedSilentDeath if you already have a Test subclass for
+// shared setup or teardown:
+//
+// class FooTest : public testing::Test { ... /* shared setup/teardown */ };
+//
+// using FooDeathTest = FooTest;
+//
+// TEST_F(FooTest, DoesThis) {
+// // normal test
+// }
+//
+// TEST_F(FooDeathTest, DoesThat) {
+// ScopedSilentDeath _silentDeath;
+// // death test
+// }
+//
+// NOTES
+//
+// When writing death tests, consider using ASSERT_EXIT() and EXPECT_EXIT()
+// rather than the more obvious ASSERT_DEATH()/EXPECT_DEATH() macros... The
+// advantage is that you can specify a regular expression that you expect
+// the abort message to match, and can be explicit about what signal you expect
+// to die with, and you can also test for *successful* exits too. Examples:
+//
+// ASSERT_DEATH(foo(), "some text\\. some more\\.");
+//
+// ASSERT_EXIT(bar(), testing::ExitedWithCode(0), "Success");
+//
+// ASSERT_EXIT(baz(), testing::KilledBySignal(SIGABRT),
+// "expected detail message \\(blah\\)");
+//
+// As you can see the regular expression functionality is there for
+// ASSERT_DEATH() too, but it's important to realize that it's a regular
+// expression, so (as in the first and third examples), you'll need to quote
+// any metacharacters (and because it's a string literal, you'll either need
+// extra quoting or want to use a raw string).
+
+class ScopedSilentDeath {
+ public:
+ ScopedSilentDeath() {
+ for (int signo : SUPPRESSED_SIGNALS) {
struct sigaction64 action = {.sa_handler = SIG_DFL};
sigaction64(signo, &action, &previous_);
}
}
- virtual void TearDown() {
- for (int signo : {SIGABRT, SIGBUS, SIGSEGV, SIGSYS}) {
+ ~ScopedSilentDeath() {
+ for (int signo : SUPPRESSED_SIGNALS) {
sigaction64(signo, &previous_, nullptr);
}
}
private:
+ static constexpr std::array<int, 4> SUPPRESSED_SIGNALS = {SIGABRT, SIGBUS, SIGSEGV, SIGSYS};
+
struct sigaction64 previous_;
};
+
+class SilentDeathTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ silent_death_ = std::unique_ptr<ScopedSilentDeath>(new ScopedSilentDeath);
+ }
+
+ void TearDown() override { silent_death_.reset(); }
+
+ private:
+ std::unique_ptr<ScopedSilentDeath> silent_death_;
+};
diff --git a/include/android-base/strings.h b/include/android-base/strings.h
index 95d6077..e794540 100644
--- a/include/android-base/strings.h
+++ b/include/android-base/strings.h
@@ -104,5 +104,8 @@
[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
std::string_view to, bool all);
+// Converts an errno number to its error message string.
+std::string ErrnoNumberAsString(int errnum);
+
} // namespace base
} // namespace android
diff --git a/include/android-base/test_utils.h b/include/android-base/test_utils.h
index f3d7cb0..29dc394 100644
--- a/include/android-base/test_utils.h
+++ b/include/android-base/test_utils.h
@@ -18,6 +18,7 @@
#include <regex>
#include <string>
+#include <type_traits>
#include <android-base/file.h>
#include <android-base/macros.h>
@@ -53,34 +54,44 @@
CapturedStdout() : CapturedStdFd(STDOUT_FILENO) {}
};
-#define ASSERT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
+#define __LIBBASE_GENERIC_REGEX_SEARCH(__s, __pattern) \
+ (std::regex_search(__s, std::basic_regex<std::decay<decltype(__s[0])>::type>((__pattern))))
+
+#define ASSERT_MATCH(__string, __pattern) \
+ do { \
+ auto __s = (__string); \
+ if (!__LIBBASE_GENERIC_REGEX_SEARCH(__s, (__pattern))) { \
+ FAIL() << "regex mismatch: expected " << (__pattern) << " in:\n" << __s; \
+ } \
} while (0)
-#define ASSERT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- FAIL() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
+#define ASSERT_NOT_MATCH(__string, __pattern) \
+ do { \
+ auto __s = (__string); \
+ if (__LIBBASE_GENERIC_REGEX_SEARCH(__s, (__pattern))) { \
+ FAIL() << "regex mismatch: expected to not find " << (__pattern) << " in:\n" << __s; \
+ } \
} while (0)
-#define EXPECT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (!std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected " << (pattern) << " in:\n" << __s; \
- } \
+#define EXPECT_MATCH(__string, __pattern) \
+ do { \
+ auto __s = (__string); \
+ if (!__LIBBASE_GENERIC_REGEX_SEARCH(__s, (__pattern))) { \
+ ADD_FAILURE() << "regex mismatch: expected " << (__pattern) << " in:\n" << __s; \
+ } \
} while (0)
-#define EXPECT_NOT_MATCH(str, pattern) \
- do { \
- auto __s = (str); \
- if (std::regex_search(__s, std::regex((pattern)))) { \
- ADD_FAILURE() << "regex mismatch: expected to not find " << (pattern) << " in:\n" << __s; \
- } \
+#define EXPECT_NOT_MATCH(__string, __pattern) \
+ do { \
+ auto __s = (__string); \
+ if (__LIBBASE_GENERIC_REGEX_SEARCH(__s, (__pattern))) { \
+ ADD_FAILURE() << "regex mismatch: expected to not find " << (__pattern) << " in:\n" << __s; \
+ } \
} while (0)
+
+extern "C" void __hwasan_init() __attribute__((weak));
+static inline bool running_with_hwasan() {
+ return &__hwasan_init != 0;
+}
+
+#define SKIP_WITH_HWASAN if (running_with_hwasan()) GTEST_SKIP()
diff --git a/include/android-base/threads.h b/include/android-base/threads.h
index dba1fc6..dbf1b47 100644
--- a/include/android-base/threads.h
+++ b/include/android-base/threads.h
@@ -24,7 +24,7 @@
}
} // namespace android
-#if defined(__GLIBC__)
-// bionic has this Linux-specifix call, but glibc doesn't.
+#if defined(__GLIBC__) || defined(ANDROID_HOST_MUSL)
+// bionic has this Linux-specifix call, but glibc and musl don't.
extern "C" int tgkill(int tgid, int tid, int sig);
#endif
diff --git a/include/android-base/unique_fd.h b/include/android-base/unique_fd.h
index 9ceb5db..e929e4c 100644
--- a/include/android-base/unique_fd.h
+++ b/include/android-base/unique_fd.h
@@ -20,19 +20,25 @@
#include <errno.h>
#include <fcntl.h>
-#if !defined(_WIN32)
-#include <sys/socket.h>
-#endif
-
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
-// DO NOT INCLUDE OTHER LIBBASE HEADERS!
+// DO NOT INCLUDE OTHER LIBBASE HEADERS HERE!
// This file gets used in libbinder, and libbinder is used everywhere.
// Including other headers from libbase frequently results in inclusion of
// android-base/macros.h, which causes macro collisions.
+#if defined(__BIONIC__)
+#include <android/fdsan.h>
+#endif
+#if !defined(_WIN32)
+#include <sys/socket.h>
+#endif
+
+namespace android {
+namespace base {
+
// Container for a file descriptor that automatically closes the descriptor as
// it goes out of scope.
//
@@ -43,47 +49,14 @@
//
// return 0; // Descriptor is closed for you.
//
+// See also the Pipe()/Socketpair()/Fdopen()/Fdopendir() functions in this file
+// that provide interoperability with the libc functions with the same (but
+// lowercase) names.
+//
// unique_fd is also known as ScopedFd/ScopedFD/scoped_fd; mentioned here to help
// you find this class if you're searching for one of those names.
-
-#if defined(__BIONIC__)
-#include <android/fdsan.h>
-#endif
-
-namespace android {
-namespace base {
-
-struct DefaultCloser {
-#if defined(__BIONIC__)
- static void Tag(int fd, void* old_addr, void* new_addr) {
- if (android_fdsan_exchange_owner_tag) {
- uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(old_addr));
- uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(new_addr));
- android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
- }
- }
- static void Close(int fd, void* addr) {
- if (android_fdsan_close_with_tag) {
- uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
- reinterpret_cast<uint64_t>(addr));
- android_fdsan_close_with_tag(fd, tag);
- } else {
- close(fd);
- }
- }
-#else
- static void Close(int fd) {
- // Even if close(2) fails with EINTR, the fd will have been closed.
- // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
- // else's fd.
- // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
- ::close(fd);
- }
-#endif
-};
-
+//
+// unique_fd itself is a specialization of unique_fd_impl with a default closer.
template <typename Closer>
class unique_fd_impl final {
public:
@@ -175,11 +148,48 @@
}
};
+// The actual details of closing are factored out to support unusual cases.
+// Almost everyone will want this DefaultCloser, which handles fdsan on bionic.
+struct DefaultCloser {
+#if defined(__BIONIC__)
+ static void Tag(int fd, void* old_addr, void* new_addr) {
+ if (android_fdsan_exchange_owner_tag) {
+ uint64_t old_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(old_addr));
+ uint64_t new_tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(new_addr));
+ android_fdsan_exchange_owner_tag(fd, old_tag, new_tag);
+ }
+ }
+ static void Close(int fd, void* addr) {
+ if (android_fdsan_close_with_tag) {
+ uint64_t tag = android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_UNIQUE_FD,
+ reinterpret_cast<uint64_t>(addr));
+ android_fdsan_close_with_tag(fd, tag);
+ } else {
+ close(fd);
+ }
+ }
+#else
+ static void Close(int fd) {
+ // Even if close(2) fails with EINTR, the fd will have been closed.
+ // Using TEMP_FAILURE_RETRY will either lead to EBADF or closing someone
+ // else's fd.
+ // http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ ::close(fd);
+ }
+#endif
+};
+
using unique_fd = unique_fd_impl<DefaultCloser>;
#if !defined(_WIN32)
// Inline functions, so that they can be used header-only.
+
+// See pipe(2).
+// This helper hides the details of converting to unique_fd, and also hides the
+// fact that macOS doesn't support O_CLOEXEC or O_NONBLOCK directly.
template <typename Closer>
inline bool Pipe(unique_fd_impl<Closer>* read, unique_fd_impl<Closer>* write,
int flags = O_CLOEXEC) {
@@ -218,6 +228,8 @@
return true;
}
+// See socketpair(2).
+// This helper hides the details of converting to unique_fd.
template <typename Closer>
inline bool Socketpair(int domain, int type, int protocol, unique_fd_impl<Closer>* left,
unique_fd_impl<Closer>* right) {
@@ -230,11 +242,14 @@
return true;
}
+// See socketpair(2).
+// This helper hides the details of converting to unique_fd.
template <typename Closer>
inline bool Socketpair(int type, unique_fd_impl<Closer>* left, unique_fd_impl<Closer>* right) {
return Socketpair(AF_UNIX, type, 0, left, right);
}
+// See fdopen(3).
// Using fdopen with unique_fd correctly is more annoying than it should be,
// because fdopen doesn't close the file descriptor received upon failure.
inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
@@ -246,6 +261,7 @@
return file;
}
+// See fdopendir(3).
// Using fdopendir with unique_fd correctly is more annoying than it should be,
// because fdopen doesn't close the file descriptor received upon failure.
inline DIR* Fdopendir(unique_fd&& ufd) {
@@ -259,7 +275,20 @@
#endif // !defined(_WIN32)
-// A wrapper type that can be implicitly constructed from either int or unique_fd.
+// A wrapper type that can be implicitly constructed from either int or
+// unique_fd. This supports cases where you don't actually own the file
+// descriptor, and can't take ownership, but are temporarily acting as if
+// you're the owner.
+//
+// One example would be a function that needs to also allow
+// STDERR_FILENO, not just a newly-opened fd. Another example would be JNI code
+// that's using a file descriptor that's actually owned by a
+// ParcelFileDescriptor or whatever on the Java side, but where the JNI code
+// would like to enforce this weaker sense of "temporary ownership".
+//
+// If you think of unique_fd as being like std::string in that represents
+// ownership, borrowed_fd is like std::string_view (and int is like const
+// char*).
struct borrowed_fd {
/* implicit */ borrowed_fd(int fd) : fd_(fd) {} // NOLINT
template <typename T>
diff --git a/logging.cpp b/logging.cpp
index 4942e2f..e6c02ba 100644
--- a/logging.cpp
+++ b/logging.cpp
@@ -62,11 +62,9 @@
namespace base {
// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
-#if defined(__GLIBC__) || defined(_WIN32)
+#if !defined(__APPLE__) && !defined(__BIONIC__)
static const char* getprogname() {
-#if defined(__GLIBC__)
- return program_invocation_short_name;
-#elif defined(_WIN32)
+#ifdef _WIN32
static bool first = true;
static char progname[MAX_PATH] = {};
@@ -77,6 +75,8 @@
}
return progname;
+#else
+ return program_invocation_short_name;
#endif
}
#endif
@@ -100,7 +100,7 @@
#if defined(__linux__)
static int OpenKmsg() {
#if defined(__ANDROID__)
- // pick up 'file w /dev/kmsg' environment from daemon's init rc file
+ // pick up 'file /dev/kmsg w' environment from daemon's init rc file
const auto val = getenv("ANDROID_FILE__dev_kmsg");
if (val != nullptr) {
int fd;
diff --git a/mapped_file.cpp b/mapped_file.cpp
index fff3453..91d0b0f 100644
--- a/mapped_file.cpp
+++ b/mapped_file.cpp
@@ -63,8 +63,8 @@
}
return nullptr;
}
- void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
- file_offset, file_length);
+ void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ,
+ (file_offset >> 32), file_offset, file_length);
if (base == nullptr) {
CloseHandle(handle);
return nullptr;
diff --git a/posix_strerror_r.cpp b/posix_strerror_r.cpp
new file mode 100644
index 0000000..6428a98
--- /dev/null
+++ b/posix_strerror_r.cpp
@@ -0,0 +1,34 @@
+//
+// 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.
+//
+
+/* Undefine _GNU_SOURCE so that this compilation unit can access the
+ * posix version of strerror_r */
+#undef _GNU_SOURCE
+#include <string.h>
+
+namespace android {
+namespace base {
+
+extern "C" int posix_strerror_r(int errnum, char* buf, size_t buflen) {
+#ifdef _WIN32
+ return strerror_s(buf, buflen, errnum);
+#else
+ return strerror_r(errnum, buf, buflen);
+#endif
+}
+
+} // namespace base
+} // namespace android
diff --git a/result_test.cpp b/result_test.cpp
index c0ac0fd..19b48c3 100644
--- a/result_test.cpp
+++ b/result_test.cpp
@@ -15,15 +15,25 @@
*/
#include "android-base/result.h"
-
+#include <utils/ErrorsMacros.h>
#include "errno.h"
#include <istream>
+#include <memory>
#include <string>
+#include <type_traits>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "android-base/result-gmock.h"
+
using namespace std::string_literals;
+using ::testing::Eq;
+using ::testing::ExplainMatchResult;
+using ::testing::HasSubstr;
+using ::testing::Not;
+using ::testing::StartsWith;
namespace android {
namespace base {
@@ -183,6 +193,49 @@
EXPECT_EQ(error_text + ": " + strerror(test_errno), result2.error().message());
}
+enum class CustomError { A, B };
+
+struct CustomErrorWrapper {
+ CustomErrorWrapper() : val_(CustomError::A) {}
+ CustomErrorWrapper(const CustomError& e) : val_(e) {}
+ CustomError value() const { return val_; }
+ operator CustomError() const { return value(); }
+ std::string print() const {
+ switch (val_) {
+ case CustomError::A:
+ return "A";
+ case CustomError::B:
+ return "B";
+ }
+ }
+ CustomError val_;
+};
+
+#define NewCustomError(e) Error<CustomErrorWrapper>(CustomError::e)
+
+TEST(result, result_with_custom_errorcode) {
+ Result<void, CustomError> ok = {};
+ EXPECT_RESULT_OK(ok);
+ ok.value(); // should not crash
+ EXPECT_DEATH(ok.error(), "");
+
+ auto error_text = "test error"s;
+ Result<void, CustomError> err = NewCustomError(A) << error_text;
+
+ EXPECT_FALSE(err.ok());
+ EXPECT_FALSE(err.has_value());
+
+ EXPECT_EQ(CustomError::A, err.error().code());
+ EXPECT_EQ(error_text + ": A", err.error().message());
+}
+
+Result<std::string, CustomError> success_or_fail(bool success) {
+ if (success)
+ return "success";
+ else
+ return NewCustomError(A) << "fail";
+}
+
TEST(result, constructor_forwarding) {
auto result = Result<std::string>(std::in_place, 5, 'a');
@@ -192,6 +245,249 @@
EXPECT_EQ("aaaaa", *result);
}
+TEST(result, unwrap_or_return) {
+ auto f = [](bool success) -> Result<size_t, CustomError> {
+ return OR_RETURN(success_or_fail(success)).size();
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ EXPECT_EQ(strlen("success"), *r);
+
+ auto s = f(false);
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(CustomError::A, s.error().code());
+ EXPECT_EQ("fail: A", s.error().message());
+}
+
+TEST(result, unwrap_or_return_errorcode) {
+ auto f = [](bool success) -> CustomError {
+ // Note that we use the same OR_RETURN macro for different return types: Result<U, CustomError>
+ // and CustomError.
+ std::string val = OR_RETURN(success_or_fail(success));
+ EXPECT_EQ("success", val);
+ return CustomError::B;
+ };
+
+ auto r = f(true);
+ EXPECT_EQ(CustomError::B, r);
+
+ auto s = f(false);
+ EXPECT_EQ(CustomError::A, s);
+}
+
+TEST(result, unwrap_or_fatal) {
+ auto r = OR_FATAL(success_or_fail(true));
+ EXPECT_EQ("success", r);
+
+ EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "fail: A");
+}
+
+TEST(result, unwrap_ambiguous_int) {
+ const std::string firstSuccess{"a"};
+ constexpr int secondSuccess = 5;
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, StatusT> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT>("Fail", 10);
+ };
+ auto f = [&](bool success) -> Result<int, StatusT> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return secondSuccess;
+ };
+
+ auto r = f(true);
+ ASSERT_TRUE(r.ok());
+ EXPECT_EQ(r.value(), secondSuccess);
+ auto s = f(false);
+ ASSERT_TRUE(!s.ok());
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+TEST(result, unwrap_ambiguous_uint_conv) {
+ const std::string firstSuccess{"a"};
+ constexpr size_t secondSuccess = 5ull;
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, StatusT> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT>("Fail", 10);
+ };
+
+ auto f = [&](bool success) -> Result<size_t, StatusT> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return secondSuccess;
+ };
+
+ auto r = f(true);
+ ASSERT_TRUE(r.ok());
+ EXPECT_EQ(r.value(), secondSuccess);
+ auto s = f(false);
+ ASSERT_TRUE(!s.ok());
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+struct IntConst {
+ int val_;
+ template <typename T, typename = std::enable_if_t<std::is_convertible_v<T, int>>>
+ IntConst(T&& val) : val_(val) {}
+ operator status_t() {return val_;}
+};
+
+TEST(result, unwrap_ambiguous_constructible) {
+ constexpr int firstSuccess = 5;
+ constexpr int secondSuccess = 7;
+ struct A {
+ A (int val) : val_(val) {}
+ operator status_t() { return 0; }
+ int val_;
+ };
+ // If this returns Result<A, ...> instead of Result<IntConst, ...>,
+ // compilation fails unless we compile with c++20
+ auto enum_success_or_fail = [&](bool success) -> Result<IntConst, StatusT, false> {
+ if (success) return firstSuccess;
+ return ResultError<StatusT, false>(10);
+ };
+ auto f = [&](bool success) -> Result<IntConst, StatusT, false> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val.val_);
+ return secondSuccess;
+ };
+ auto r = f(true);
+ EXPECT_EQ(r.value().val_, secondSuccess);
+ auto s = f(false);
+ EXPECT_EQ(s.error().code(), 10);
+}
+
+struct Dangerous {};
+struct ImplicitFromDangerous {
+ ImplicitFromDangerous(Dangerous);
+};
+template <typename U>
+struct Templated {
+ U val_;
+ template <typename T, typename=std::enable_if_t<std::is_convertible_v<T, U>>>
+ Templated(T val) : val_(val) {}
+};
+
+
+TEST(result, dangerous_result_conversion) {
+ ResultError<Dangerous, false> error {Dangerous{}};
+ Result<Templated<Dangerous>, Dangerous, false> surprise {error};
+ EXPECT_TRUE(!surprise.ok());
+ Result<Templated<ImplicitFromDangerous>, Dangerous, false> surprise2 {error};
+ EXPECT_TRUE(!surprise2.ok());
+}
+
+TEST(result, generic_convertible) {
+ const std::string firstSuccess{"a"};
+ struct A {};
+ struct B {
+ operator A() {return A{};}
+ };
+
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, B> {
+ if (success) return firstSuccess;
+ return ResultError<B>("Fail", B{});
+ };
+ auto f = [&](bool success) -> Result<A, B> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return A{};
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ auto s = f(false);
+ EXPECT_TRUE(!s.ok());
+}
+
+TEST(result, generic_exact) {
+ const std::string firstSuccess{"a"};
+ struct A {};
+ auto enum_success_or_fail = [&](bool success) -> Result<std::string, A> {
+ if (success) return firstSuccess;
+ return ResultError<A>("Fail", A{});
+ };
+ auto f = [&](bool success) -> Result<A, A> {
+ auto val = OR_RETURN(enum_success_or_fail(success));
+ EXPECT_EQ(firstSuccess, val);
+ return A{};
+ };
+
+ auto r = f(true);
+ EXPECT_TRUE(r.ok());
+ auto s = f(false);
+ EXPECT_TRUE(!s.ok());
+}
+
+struct MyData {
+ const int data;
+ static int copy_constructed;
+ static int move_constructed;
+ explicit MyData(int d) : data(d) {}
+ MyData(const MyData& other) : data(other.data) { copy_constructed++; }
+ MyData(MyData&& other) : data(other.data) { move_constructed++; }
+ MyData& operator=(const MyData&) = delete;
+ MyData& operator=(MyData&&) = delete;
+};
+
+int MyData::copy_constructed = 0;
+int MyData::move_constructed = 0;
+
+TEST(result, unwrap_does_not_incur_additional_copying) {
+ MyData::copy_constructed = 0;
+ MyData::move_constructed = 0;
+ auto f = []() -> Result<MyData> { return MyData{10}; };
+
+ [&]() -> Result<void> {
+ int data = OR_RETURN(f()).data;
+ EXPECT_EQ(10, data);
+ EXPECT_EQ(0, MyData::copy_constructed);
+ // Moved once when MyData{10} is returned as Result<MyData> in the lambda f.
+ // Moved once again when the variable d is constructed from OR_RETURN.
+ EXPECT_EQ(2, MyData::move_constructed);
+ return {};
+ }();
+}
+
+TEST(result, supports_move_only_type) {
+ auto f = [](bool success) -> Result<std::unique_ptr<std::string>> {
+ if (success) return std::make_unique<std::string>("hello");
+ return Error() << "error";
+ };
+
+ auto g = [&](bool success) -> Result<std::unique_ptr<std::string>> {
+ auto r = OR_RETURN(f(success));
+ EXPECT_EQ("hello", *(r.get()));
+ return std::make_unique<std::string>("world");
+ };
+
+ auto s = g(true);
+ EXPECT_RESULT_OK(s);
+ EXPECT_EQ("world", *(s->get()));
+
+ auto t = g(false);
+ EXPECT_FALSE(t.ok());
+ EXPECT_EQ("error", t.error().message());
+}
+
+TEST(result, unique_ptr) {
+ using testing::Ok;
+
+ auto return_unique_ptr = [](bool success) -> Result<std::unique_ptr<int>> {
+ auto result = OR_RETURN(Result<std::unique_ptr<int>>(std::make_unique<int>(3)));
+ if (!success) {
+ return Error() << __func__ << " failed.";
+ }
+ return result;
+ };
+ Result<std::unique_ptr<int>> result1 = return_unique_ptr(false);
+ ASSERT_THAT(result1, Not(Ok()));
+ Result<std::unique_ptr<int>> result2 = return_unique_ptr(true);
+ ASSERT_THAT(result2, Ok());
+ EXPECT_EQ(**result2, 3);
+}
+
struct ConstructorTracker {
static size_t constructor_called;
static size_t copy_constructor_called;
@@ -418,5 +714,107 @@
outer.error().message());
}
+TEST(result, error_without_message) {
+ constexpr bool include_message = false;
+ Result<void, Errno, include_message> res = Error<Errno, include_message>(10);
+ EXPECT_FALSE(res.ok());
+ EXPECT_EQ(10, res.error().code());
+ EXPECT_EQ(sizeof(int), sizeof(res.error()));
+}
+
+namespace testing {
+
+class Listener : public ::testing::MatchResultListener {
+ public:
+ Listener() : MatchResultListener(&ss_) {}
+ ~Listener() = default;
+ std::string message() const { return ss_.str(); }
+
+ private:
+ std::stringstream ss_;
+};
+
+class ResultMatchers : public ::testing::Test {
+ public:
+ Result<int> result = 1;
+ Result<int> error = Error(EBADF) << "error message";
+ Listener listener;
+};
+
+TEST_F(ResultMatchers, ok_result) {
+ EXPECT_TRUE(ExplainMatchResult(Ok(), result, &listener));
+ EXPECT_THAT(listener.message(), Eq("result is OK"));
+}
+
+TEST_F(ResultMatchers, ok_error) {
+ EXPECT_FALSE(ExplainMatchResult(Ok(), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(error.error().message()));
+ EXPECT_THAT(listener.message(), HasSubstr(strerror(error.error().code())));
+}
+
+TEST_F(ResultMatchers, not_ok_result) {
+ EXPECT_FALSE(ExplainMatchResult(Not(Ok()), result, &listener));
+ EXPECT_THAT(listener.message(), Eq("result is OK"));
+}
+
+TEST_F(ResultMatchers, not_ok_error) {
+ EXPECT_TRUE(ExplainMatchResult(Not(Ok()), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(error.error().message()));
+ EXPECT_THAT(listener.message(), HasSubstr(strerror(error.error().code())));
+}
+
+TEST_F(ResultMatchers, has_value_result) {
+ EXPECT_TRUE(ExplainMatchResult(HasValue(*result), result, &listener));
+}
+
+TEST_F(ResultMatchers, has_value_wrong_result) {
+ EXPECT_FALSE(ExplainMatchResult(HasValue(*result + 1), result, &listener));
+}
+
+TEST_F(ResultMatchers, has_value_error) {
+ EXPECT_FALSE(ExplainMatchResult(HasValue(*result), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(error.error().message()));
+ EXPECT_THAT(listener.message(), HasSubstr(strerror(error.error().code())));
+}
+
+TEST_F(ResultMatchers, has_error_code_result) {
+ EXPECT_FALSE(ExplainMatchResult(HasError(WithCode(error.error().code())), result, &listener));
+ EXPECT_THAT(listener.message(), Eq("result is OK"));
+}
+
+TEST_F(ResultMatchers, has_error_code_wrong_code) {
+ EXPECT_FALSE(ExplainMatchResult(HasError(WithCode(error.error().code() + 1)), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("actual error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(strerror(error.error().code())));
+}
+
+TEST_F(ResultMatchers, has_error_code_correct_code) {
+ EXPECT_TRUE(ExplainMatchResult(HasError(WithCode(error.error().code())), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("actual error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(strerror(error.error().code())));
+}
+
+TEST_F(ResultMatchers, has_error_message_result) {
+ EXPECT_FALSE(
+ ExplainMatchResult(HasError(WithMessage(error.error().message())), result, &listener));
+ EXPECT_THAT(listener.message(), Eq("result is OK"));
+}
+
+TEST_F(ResultMatchers, has_error_message_wrong_message) {
+ EXPECT_FALSE(ExplainMatchResult(HasError(WithMessage("foo")), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("actual error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(error.error().message()));
+}
+
+TEST_F(ResultMatchers, has_error_message_correct_message) {
+ EXPECT_TRUE(ExplainMatchResult(HasError(WithMessage(error.error().message())), error, &listener));
+ EXPECT_THAT(listener.message(), StartsWith("actual error is"));
+ EXPECT_THAT(listener.message(), HasSubstr(error.error().message()));
+}
+
+} // namespace testing
} // namespace base
} // namespace android
diff --git a/result_test_constraint.cpp b/result_test_constraint.cpp
new file mode 100644
index 0000000..253a276
--- /dev/null
+++ b/result_test_constraint.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// Since result has c++20 conditional behavior, we compile with std=c++20 to
+// ensure functionality in both cases. Instead of duplicating the file, we will
+// include, since this test is not linked against anything.
+// This test can be removed when we move to c++20.
+#include "result_test.cpp"
diff --git a/strings.cpp b/strings.cpp
index 8f3c7d9..deb6e28 100644
--- a/strings.cpp
+++ b/strings.cpp
@@ -16,12 +16,18 @@
#include "android-base/strings.h"
+#include "android-base/stringprintf.h"
+
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
+// Wraps the posix version of strerror_r to make it available in translation units
+// that define _GNU_SOURCE.
+extern "C" int posix_strerror_r(int errnum, char* buf, size_t buflen);
+
namespace android {
namespace base {
@@ -152,5 +158,15 @@
return result;
}
+std::string ErrnoNumberAsString(int errnum) {
+ char buf[100];
+ buf[0] = '\0';
+ int strerror_err = posix_strerror_r(errnum, buf, sizeof(buf));
+ if (strerror_err < 0) {
+ return StringPrintf("Failed to convert errno %d to string: %d", errnum, strerror_err);
+ }
+ return buf;
+}
+
} // namespace base
} // namespace android
diff --git a/strings_test.cpp b/strings_test.cpp
index 7a57489..fb111b8 100644
--- a/strings_test.cpp
+++ b/strings_test.cpp
@@ -400,3 +400,7 @@
ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
}
+
+TEST(strings, ErrnoNumberAsString) {
+ EXPECT_EQ("No such file or directory", android::base::ErrnoNumberAsString(ENOENT));
+}
diff --git a/test_utils_test.cpp b/test_utils_test.cpp
index d08d5d6..10e8a2a 100644
--- a/test_utils_test.cpp
+++ b/test_utils_test.cpp
@@ -27,21 +27,33 @@
TEST(TestUtilsTest, AssertMatch) {
ASSERT_MATCH("foobar", R"(fo+baz?r)");
EXPECT_FATAL_FAILURE(ASSERT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+
+ ASSERT_MATCH(L"foobar", LR"(fo+baz?r)");
+ EXPECT_FATAL_FAILURE(ASSERT_MATCH(L"foobar", LR"(foobaz)"), "regex mismatch");
}
TEST(TestUtilsTest, AssertNotMatch) {
ASSERT_NOT_MATCH("foobar", R"(foobaz)");
EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+
+ ASSERT_NOT_MATCH(L"foobar", LR"(foobaz)");
+ EXPECT_FATAL_FAILURE(ASSERT_NOT_MATCH(L"foobar", LR"(foobar)"), "regex mismatch");
}
TEST(TestUtilsTest, ExpectMatch) {
EXPECT_MATCH("foobar", R"(fo+baz?r)");
EXPECT_NONFATAL_FAILURE(EXPECT_MATCH("foobar", R"(foobaz)"), "regex mismatch");
+
+ EXPECT_MATCH(L"foobar", LR"(fo+baz?r)");
+ EXPECT_NONFATAL_FAILURE(EXPECT_MATCH(L"foobar", LR"(foobaz)"), "regex mismatch");
}
TEST(TestUtilsTest, ExpectNotMatch) {
EXPECT_NOT_MATCH("foobar", R"(foobaz)");
EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
+
+ EXPECT_NOT_MATCH(L"foobar", LR"(foobaz)");
+ EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH(L"foobar", LR"(foobar)"), "regex mismatch");
}
TEST(TestUtilsTest, CaptureStdout_smoke) {
diff --git a/threads.cpp b/threads.cpp
index 48f6197..6d7d7c6 100644
--- a/threads.cpp
+++ b/threads.cpp
@@ -47,7 +47,7 @@
} // namespace base
} // namespace android
-#if defined(__GLIBC__)
+#if defined(__GLIBC__) || defined(ANDROID_HOST_MUSL)
int tgkill(int tgid, int tid, int sig) {
return syscall(__NR_tgkill, tgid, tid, sig);
}