/* * Copyright 2020 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. */ #define LOG_TAG "FlattenableHelpersTest" #include #include #include #include #include #include #include #include namespace android { namespace { struct TestLightFlattenable : LightFlattenable { std::unique_ptr ptr; bool isFixedSize() const { return true; } size_t getFlattenedSize() const { return sizeof(int32_t); } status_t flatten(void* buffer, size_t size) const { FlattenableUtils::write(buffer, size, *ptr); return OK; } status_t unflatten(void const* buffer, size_t size) { int32_t value; FlattenableUtils::read(buffer, size, value); ptr = std::make_unique(value); return OK; } }; class FlattenableHelpersTest : public testing::Test { public: template void testWriteThenRead(const T& value, size_t bufferSize) { std::vector buffer(bufferSize); auto rawBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); auto rawReadBuffer = reinterpret_cast(buffer.data()); size = buffer.size(); T valueRead; ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); EXPECT_EQ(value, valueRead); } template void testTriviallyCopyable(const T& value) { testWriteThenRead(value, sizeof(T)); } template void testWriteThenRead(const T& value) { testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value)); } }; TEST_F(FlattenableHelpersTest, TriviallyCopyable) { testTriviallyCopyable(42); testTriviallyCopyable(1LL << 63); testTriviallyCopyable(false); testTriviallyCopyable(true); testTriviallyCopyable(std::optional()); testTriviallyCopyable(std::optional(4)); } TEST_F(FlattenableHelpersTest, String) { testWriteThenRead(std::string("Android")); testWriteThenRead(std::string()); } TEST_F(FlattenableHelpersTest, Vector) { testWriteThenRead(std::vector({1, 2, 3})); testWriteThenRead(std::vector()); } TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) { std::vector buffer; constexpr int kInternalValue = 16; { std::optional value = TestLightFlattenable{.ptr = std::make_unique(kInternalValue)}; buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); void* rawBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); } const void* rawReadBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); std::optional valueRead; ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); ASSERT_TRUE(valueRead.has_value()); EXPECT_EQ(kInternalValue, *valueRead->ptr); } TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) { std::vector buffer; { std::optional value; buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); void* rawBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); } const void* rawReadBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); std::optional valueRead; ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); ASSERT_FALSE(valueRead.has_value()); } // If a struct is both trivially copyable and light flattenable we should treat it // as LigthFlattenable. TEST_F(FlattenableHelpersTest, TriviallyCopyableAndLightFlattenableIsFlattenedAsLightFlattenable) { static constexpr int32_t kSizeTag = 1234567; static constexpr int32_t kFlattenTag = 987654; static constexpr int32_t kUnflattenTag = 5926582; struct LightFlattenableAndTriviallyCopyable : LightFlattenable { int32_t value; bool isFixedSize() const { return true; } size_t getFlattenedSize() const { return kSizeTag; } status_t flatten(void* buffer, size_t size) const { FlattenableUtils::write(buffer, size, kFlattenTag); return OK; } status_t unflatten(void const*, size_t) { value = kUnflattenTag; return OK; } }; { // Verify that getFlattenedSize uses the LightFlattenable overload LightFlattenableAndTriviallyCopyable foo; EXPECT_EQ(kSizeTag, FlattenableHelpers::getFlattenedSize(foo)); } { // Verify that flatten uses the LightFlattenable overload std::vector buffer(sizeof(int32_t)); auto rawBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); LightFlattenableAndTriviallyCopyable foo; ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, foo)); auto rawReadBuffer = reinterpret_cast(buffer.data()); int32_t value; FlattenableHelpers::unflatten(&rawReadBuffer, &size, &value); EXPECT_EQ(kFlattenTag, value); } { // Verify that unflatten uses the LightFlattenable overload std::vector buffer(sizeof(int32_t)); auto rawBuffer = reinterpret_cast(buffer.data()); size_t size = buffer.size(); int32_t value = 4; ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); auto rawReadBuffer = reinterpret_cast(buffer.data()); LightFlattenableAndTriviallyCopyable foo; FlattenableHelpers::unflatten(&rawReadBuffer, &size, &foo); EXPECT_EQ(kUnflattenTag, foo.value); } } } // namespace } // namespace android