// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. // // 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. // // SPDX-License-Identifier: Apache-2.0 #include "iceoryx_hoofs/internal/relocatable_pointer/relocatable_ptr.hpp" #include "test.hpp" #include #include namespace { using namespace ::testing; using namespace iox::rp; // Needed especially for void implementation tests where we cannot // construct a corresponding object of type void to point to. template inline T* nonNullPtr() { static T t{}; return &t; } template <> inline void* nonNullPtr() { static int t; return &t; } template <> inline const void* nonNullPtr() { static int t; return &t; } template inline T* otherNonNullPtr() { static T t{}; return &t; } template <> inline void* otherNonNullPtr() { static int t; return &t; } template <> inline const void* otherNonNullPtr() { static int t; return &t; } // does P wrap a T? template constexpr auto getReturns() { using R = decltype(std::declval

().get()); return std::is_same::value; }; // does the conversion operator of P return a T? template constexpr auto conversionReturns() { using R = decltype(std::declval

().operator T()); return std::is_same::value; }; // does the arrow operator of P return a T? template constexpr auto arrowReturns() { using R = decltype(std::declval

().operator->()); return std::is_same::value; }; // does the dereferencing operator of P return a T? template constexpr auto dereferencingReturns() { using R = decltype(std::declval

().operator*()); return std::is_same::value; }; struct Data { Data(uint32_t value = 0) : value(value) { } uint32_t value; }; class RelocatableType { public: RelocatableType(int value) : data(value) , rp(&this->data) { } void clear() { void* p = this; // make opaque to suppress warning // the memset is safe since it is operating on primitive types std::memset(p, 0, sizeof(RelocatableType)); } RelocatableType(const RelocatableType&) = delete; RelocatableType& operator=(const RelocatableType&) = delete; RelocatableType(RelocatableType&&) = delete; RelocatableType& operator=(RelocatableType&&) = delete; int data; iox::rp::relocatable_ptr rp; }; // Not all tests make sense to be run as typed tests // due to interface / behaviour differences, // e.g. dereferencing for void // Tests for all template types template class Relocatable_ptr_typed_test : public Test { public: void SetUp() override { internal::CaptureStderr(); } void TearDown() override { std::string output = internal::GetCapturedStderr(); if (Test::HasFailure()) { std::cout << output << std::endl; } } using DataType = T; }; // Other tests not specific to a template type class Relocatable_ptr_test : public Test { public: void SetUp() override { internal::CaptureStderr(); } void TearDown() override { std::string output = internal::GetCapturedStderr(); if (Test::HasFailure()) { std::cout << output << std::endl; } } }; typedef ::testing::Types TestTypes; TYPED_TEST_SUITE(Relocatable_ptr_typed_test, TestTypes); TYPED_TEST(Relocatable_ptr_typed_test, wrappedPointerTypeIsCorrect) { ::testing::Test::RecordProperty("TEST_ID", "12de29e3-673c-487c-9808-67e5c3e25c73"); using T = typename TestFixture::DataType; using P = typename iox::rp::relocatable_ptr::ptr_t; constexpr bool pointerTypeIsCorrect = std::is_same::value; EXPECT_TRUE(pointerTypeIsCorrect); } TYPED_TEST(Relocatable_ptr_typed_test, defaulCtorCreatesNullpointer) { ::testing::Test::RecordProperty("TEST_ID", "6823533f-9594-4f53-9493-d80a73706013"); using T = typename TestFixture::DataType; iox::rp::relocatable_ptr rp; EXPECT_EQ(rp.get(), nullptr); } TYPED_TEST(Relocatable_ptr_typed_test, copyCtorOfNullptrWorks) { ::testing::Test::RecordProperty("TEST_ID", "de23e14d-1c06-4005-ba61-45e5aeedaf47"); using T = typename TestFixture::DataType; iox::rp::relocatable_ptr rp1; iox::rp::relocatable_ptr rp2(rp1); EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), nullptr); } TYPED_TEST(Relocatable_ptr_typed_test, moveCtorOfNullptrWorks) { ::testing::Test::RecordProperty("TEST_ID", "f0ecd49e-c165-4e25-985c-5bc44a072f2e"); using T = typename TestFixture::DataType; iox::rp::relocatable_ptr rp1; iox::rp::relocatable_ptr rp2(std::move(rp1)); EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), nullptr); } TYPED_TEST(Relocatable_ptr_typed_test, copyAssignmentOfNullptrWorks) { ::testing::Test::RecordProperty("TEST_ID", "635665f9-70a1-405e-981c-155c0f2f81a7"); using T = typename TestFixture::DataType; // we cannot construct an actual object if T = void // and it is not necessary fro most tests, we just need some non-nullptr T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1; iox::rp::relocatable_ptr rp2(p); rp2 = rp1; EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), nullptr); } TYPED_TEST(Relocatable_ptr_typed_test, moveAssignmentOfNullptrWorks) { ::testing::Test::RecordProperty("TEST_ID", "b15da71c-bb71-4059-a3fb-7d5d8f8020a6"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1; iox::rp::relocatable_ptr rp2(p); rp2 = std::move(rp1); EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), nullptr); } TYPED_TEST(Relocatable_ptr_typed_test, nonNullPointerConstructionWorks) { ::testing::Test::RecordProperty("TEST_ID", "6258b81c-97b4-4d5b-9543-ca7e2fc8e6f0"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp(p); EXPECT_EQ(rp.get(), p); } TYPED_TEST(Relocatable_ptr_typed_test, copyCtorWorks) { ::testing::Test::RecordProperty("TEST_ID", "453b6c32-e5ba-4b15-86af-d7601ee5b97e"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2(rp1); EXPECT_EQ(rp1.get(), p); EXPECT_EQ(rp2.get(), p); } TYPED_TEST(Relocatable_ptr_typed_test, moveCtorWorks) { ::testing::Test::RecordProperty("TEST_ID", "42c3a34d-4dc8-4bf6-bf1e-1fc742570ee2"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2(std::move(rp1)); EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), p); } TYPED_TEST(Relocatable_ptr_typed_test, copyAssignmentWorks) { ::testing::Test::RecordProperty("TEST_ID", "431354d5-400d-49cd-8554-4ee797661cf7"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2; rp2 = rp1; EXPECT_EQ(rp1.get(), p); EXPECT_EQ(rp2.get(), p); } TYPED_TEST(Relocatable_ptr_typed_test, moveAssignmentWorks) { ::testing::Test::RecordProperty("TEST_ID", "5041782e-2c57-4739-ab21-2d7c1dfc3991"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2; rp2 = std::move(rp1); EXPECT_EQ(rp1.get(), nullptr); EXPECT_EQ(rp2.get(), p); } // regular get is tested with the ctor TYPED_TEST(Relocatable_ptr_typed_test, constGetWorks) { ::testing::Test::RecordProperty("TEST_ID", "1b478221-d1fb-44aa-905e-7f40d961eaff"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); const iox::rp::relocatable_ptr rp(p); EXPECT_EQ(rp.get(), p); constexpr bool isConst = getReturns(); EXPECT_TRUE(isConst); } TYPED_TEST(Relocatable_ptr_typed_test, conversionToRawPointerWorks) { ::testing::Test::RecordProperty("TEST_ID", "d7e46b17-62bc-4b05-a827-b91d2a4b9f23"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp(p); T* q = rp; EXPECT_EQ(q, p); constexpr bool isNotConst = conversionReturns(); EXPECT_TRUE(isNotConst); } TYPED_TEST(Relocatable_ptr_typed_test, conversionToConstRawPointerWorks) { ::testing::Test::RecordProperty("TEST_ID", "82f40725-67d9-4f80-812a-1fcbdd1cbf60"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); const iox::rp::relocatable_ptr rp(p); const T* q = rp; EXPECT_EQ(q, p); constexpr bool isConst = conversionReturns(); EXPECT_TRUE(isConst); } TYPED_TEST(Relocatable_ptr_typed_test, arrowOperatorWorks) { ::testing::Test::RecordProperty("TEST_ID", "6f3a7428-fd7b-4a98-b3ce-90ac73655ac8"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp(p); EXPECT_EQ(rp.operator->(), p); constexpr bool isNotConst = arrowReturns(); EXPECT_TRUE(isNotConst); } TYPED_TEST(Relocatable_ptr_typed_test, arrowOperatorConstWorks) { ::testing::Test::RecordProperty("TEST_ID", "38bf2bb7-6550-4534-b0fe-6ec07632c6d3"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); const iox::rp::relocatable_ptr rp(p); EXPECT_EQ(rp.operator->(), p); constexpr bool isConst = arrowReturns(); EXPECT_TRUE(isConst); } TYPED_TEST(Relocatable_ptr_typed_test, nullptrIsEqualToNullptr) { ::testing::Test::RecordProperty("TEST_ID", "45d24a0b-5a46-4a10-bbb8-7f8b7647a992"); using T = typename TestFixture::DataType; iox::rp::relocatable_ptr rp1; iox::rp::relocatable_ptr rp2; EXPECT_TRUE(rp1 == rp2); EXPECT_TRUE(rp2 == rp1); EXPECT_TRUE(rp1 == nullptr); EXPECT_TRUE(nullptr == rp2); EXPECT_FALSE(rp1 != rp2); EXPECT_FALSE(rp2 != rp1); EXPECT_FALSE(rp1 != nullptr); EXPECT_FALSE(nullptr != rp2); } TYPED_TEST(Relocatable_ptr_typed_test, nullptrIsNotEqualToNonNullptr) { ::testing::Test::RecordProperty("TEST_ID", "a3fd804d-9e53-4fef-9b34-bb43bd778c02"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2; EXPECT_FALSE(rp1 == rp2); EXPECT_FALSE(rp2 == rp1); EXPECT_FALSE(rp1 == nullptr); EXPECT_FALSE(nullptr == rp1); EXPECT_TRUE(rp1 != rp2); EXPECT_TRUE(rp2 != rp1); EXPECT_TRUE(rp1 != nullptr); EXPECT_TRUE(nullptr != rp1); } TYPED_TEST(Relocatable_ptr_typed_test, equalNonNullptrComparisonWorks) { ::testing::Test::RecordProperty("TEST_ID", "c3ed8892-db76-4d62-8c04-51819696c7dc"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp1(p); iox::rp::relocatable_ptr rp2(p); EXPECT_TRUE(rp1 == rp2); EXPECT_TRUE(rp2 == rp1); EXPECT_TRUE(rp1 == p); EXPECT_TRUE(p == rp2); EXPECT_FALSE(rp1 != rp2); EXPECT_FALSE(rp2 != rp1); EXPECT_FALSE(p != rp2); EXPECT_FALSE(rp1 != p); } TYPED_TEST(Relocatable_ptr_typed_test, nonEqualNonNullptrComparisonWorks) { ::testing::Test::RecordProperty("TEST_ID", "1cad3023-dd21-4242-a461-10f06cdea765"); using T = typename TestFixture::DataType; T* p1 = nonNullPtr(); T* p2 = otherNonNullPtr(); iox::rp::relocatable_ptr rp1(p1); iox::rp::relocatable_ptr rp2(p2); EXPECT_FALSE(rp1 == rp2); EXPECT_FALSE(rp2 == rp1); EXPECT_FALSE(p1 == rp2); EXPECT_FALSE(rp1 == p2); EXPECT_TRUE(rp1 != rp2); EXPECT_TRUE(rp2 != rp1); EXPECT_TRUE(p1 != rp2); EXPECT_TRUE(rp1 != p2); } TYPED_TEST(Relocatable_ptr_typed_test, negativeNullPointerCheckWithIfWorks) { ::testing::Test::RecordProperty("TEST_ID", "77225288-be1d-4105-b2fb-7f5f452cad89"); using T = typename TestFixture::DataType; T* p = nonNullPtr(); iox::rp::relocatable_ptr rp(p); if (rp) { SUCCEED(); } else { FAIL(); } } TYPED_TEST(Relocatable_ptr_typed_test, positiveNullPointerCheckWithIfWorks) { ::testing::Test::RecordProperty("TEST_ID", "4a57801b-3f52-4027-a2f1-474274e83515"); using T = typename TestFixture::DataType; iox::rp::relocatable_ptr rp; if (rp) { FAIL(); } else { SUCCEED(); } } TEST_F(Relocatable_ptr_test, dereferencingWorks) { ::testing::Test::RecordProperty("TEST_ID", "ea67f218-6ff8-4a82-a81e-52ae988546dc"); int x = 666; iox::rp::relocatable_ptr rp(&x); EXPECT_EQ(*rp, x); constexpr bool isNotConst = dereferencingReturns(); EXPECT_TRUE(isNotConst); } TEST_F(Relocatable_ptr_test, dereferencingConstWorks) { ::testing::Test::RecordProperty("TEST_ID", "64a7e44e-b9eb-428a-bd50-3bd9e14400bc"); int x = 314; const iox::rp::relocatable_ptr rp(&x); EXPECT_EQ(*rp, x); constexpr bool isConst = dereferencingReturns(); EXPECT_TRUE(isConst); } TEST_F(Relocatable_ptr_test, dereferencingComplexTypeWorks) { ::testing::Test::RecordProperty("TEST_ID", "e4a2bda1-c3f2-424e-b6dd-a4da6703b699"); Data x(69); iox::rp::relocatable_ptr rp(&x); EXPECT_EQ((*rp).value, x.value); EXPECT_EQ(rp->value, x.value); } TEST_F(Relocatable_ptr_test, dereferencingConstComplexTypeWorks) { ::testing::Test::RecordProperty("TEST_ID", "b60f0fd5-ff9b-40a5-ad0d-d13965eff578"); Data x(42); const iox::rp::relocatable_ptr rp(&x); EXPECT_EQ((*rp).value, x.value); EXPECT_EQ(rp->value, x.value); } // Checks whether copying a structure ontaining a relocatable_ptr // to internal data works as expected. // This means that the structure is properly copied by memcpy // and the copy works at the destination and does not depend on the source. // To verify this, we set the source to 0 after the copy. TEST_F(Relocatable_ptr_test, relocationWorks) { ::testing::Test::RecordProperty("TEST_ID", "b1b85836-2a4f-4859-a8f9-796e20fbb735"); using T = RelocatableType; using storage_t = std::aligned_storage::type; storage_t sourceStorage, destStorage; void* sourcePtr = new (&sourceStorage) T(37); void* destPtr = &destStorage; T* source = reinterpret_cast(sourcePtr); T* dest = reinterpret_cast(destPtr); EXPECT_EQ(source->data, 37); EXPECT_EQ(*source->rp, 37); // structure is relocated by memcopy std::memcpy(destPtr, sourcePtr, sizeof(T)); source->clear(); EXPECT_EQ(source->data, 0); EXPECT_EQ(dest->data, 37); // points to relocated data automatically EXPECT_EQ(*dest->rp, 37); dest->data = 73; EXPECT_EQ(source->data, 0); EXPECT_EQ(*dest->rp, 73); source->~T(); dest->~T(); } } // namespace