// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. // 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/cxx/optional.hpp" #include "test.hpp" #include namespace { using namespace ::testing; /// @todo create a typed test with to check if our optional has the same behaviour as the std::optional /// @todo create a parameterized test with an additional non-POD class class Optional_test : public Test { public: struct TestClass { int64_t value; int64_t secondValue; }; void SetUp() override { } void TearDown() override { } iox::cxx::optional m_sutWithValue{0}; iox::cxx::optional m_sutNoValue{iox::cxx::nullopt_t()}; }; TEST_F(Optional_test, DefaultCTorHasValue) { ::testing::Test::RecordProperty("TEST_ID", "2fd1d7b0-4d71-4177-9562-b8d3144e08c2"); EXPECT_THAT(m_sutWithValue.has_value(), Eq(true)); } TEST_F(Optional_test, NulloptCTor) { ::testing::Test::RecordProperty("TEST_ID", "2e93637a-501a-4b34-a25e-01b3c7f99ccd"); EXPECT_THAT(m_sutNoValue.has_value(), Eq(false)); } TEST_F(Optional_test, emplaceWithoutPresetValue) { ::testing::Test::RecordProperty("TEST_ID", "54ce5155-eb13-4bd6-9736-a415e9002ae3"); m_sutNoValue.emplace(123); EXPECT_THAT(m_sutNoValue.has_value(), Eq(true)); EXPECT_THAT(m_sutNoValue.value(), Eq(123)); } TEST_F(Optional_test, emplaceWithPresetValue) { ::testing::Test::RecordProperty("TEST_ID", "04ebc62f-7a89-4fd9-bad1-1b8df44343f4"); m_sutWithValue.emplace(123); EXPECT_THAT(m_sutWithValue.has_value(), Eq(true)); EXPECT_THAT(m_sutWithValue.value(), Eq(123)); } TEST_F(Optional_test, value_or_NoValue) { ::testing::Test::RecordProperty("TEST_ID", "126c0def-f6c0-4913-b3b0-46f486e03b6b"); EXPECT_THAT(m_sutNoValue.value_or(1337), Eq(1337)); } TEST_F(Optional_test, value_or_WithValue) { ::testing::Test::RecordProperty("TEST_ID", "1cd0c905-ced9-404f-a83c-86406b223593"); m_sutWithValue.emplace(42); EXPECT_THAT(m_sutWithValue.value_or(1337), Eq(42)); } TEST_F(Optional_test, value) { ::testing::Test::RecordProperty("TEST_ID", "04e56e48-a7df-499b-9f72-1a8d5704c4c6"); m_sutWithValue = 1234; EXPECT_THAT(m_sutWithValue.value(), Eq(1234)); } TEST_F(Optional_test, const_value) { ::testing::Test::RecordProperty("TEST_ID", "ac529426-2780-4c66-ad33-8b745ab3cb29"); m_sutWithValue = 1234; EXPECT_THAT(const_cast*>(&m_sutWithValue)->value(), Eq(1234)); } TEST_F(Optional_test, resetWithValue) { ::testing::Test::RecordProperty("TEST_ID", "915d7952-6e7f-47c1-ad67-7b4b76de62ea"); m_sutWithValue.reset(); EXPECT_THAT(m_sutWithValue.has_value(), Eq(false)); } TEST_F(Optional_test, resetWithoutValue) { ::testing::Test::RecordProperty("TEST_ID", "78a058f5-4f23-4f37-9b9f-d6edfe0fc3bb"); m_sutNoValue.reset(); EXPECT_THAT(m_sutNoValue.has_value(), Eq(false)); } TEST_F(Optional_test, boolOperatorNoValue) { ::testing::Test::RecordProperty("TEST_ID", "b0102f46-626c-444f-bbc7-7fa9e1d3c89e"); EXPECT_THAT(static_cast(m_sutNoValue), Eq(false)); } TEST_F(Optional_test, boolOperatorWithValue) { ::testing::Test::RecordProperty("TEST_ID", "55f48d5b-71c2-4c09-b5a6-817b7205bc78"); EXPECT_THAT(static_cast(m_sutWithValue), Eq(true)); } TEST_F(Optional_test, ArrowOperator) { ::testing::Test::RecordProperty("TEST_ID", "f0cad5c5-e032-454c-8934-3ba9aaf3c641"); iox::cxx::optional sut{{0, 0}}; sut->value = 1234; EXPECT_THAT(sut->value, Eq(1234)); } TEST_F(Optional_test, ConstArrowOperator) { ::testing::Test::RecordProperty("TEST_ID", "515aab10-cf10-4c56-b160-a7ef9d33937f"); iox::cxx::optional sut{{0, 0}}; sut->value = 12345; EXPECT_THAT((*const_cast*>(&sut))->value, Eq(12345)); } TEST_F(Optional_test, DereferenceOperator) { ::testing::Test::RecordProperty("TEST_ID", "f8956c31-2f58-4f69-b548-10943d2edf3e"); *m_sutWithValue = 789; EXPECT_THAT(*m_sutWithValue, Eq(789)); } TEST_F(Optional_test, ConstDereferenceOperator) { ::testing::Test::RecordProperty("TEST_ID", "8da28aec-48f8-4d39-896f-5a443b9eb0ab"); *m_sutWithValue = 789; EXPECT_THAT(**const_cast*>(&m_sutWithValue), Eq(789)); } TEST_F(Optional_test, UserDefinedTypeAssignment) { ::testing::Test::RecordProperty("TEST_ID", "bf37ae0d-9b7d-4878-a6d0-42ab1bd67633"); iox::cxx::optional sut; sut = TestClass{1234, 22}; EXPECT_THAT(sut->value, Eq(1234)); } TEST_F(Optional_test, CompareWithEqualValue) { ::testing::Test::RecordProperty("TEST_ID", "5db271ca-8e86-45b0-be75-2e0ae535c780"); m_sutWithValue = 123; iox::cxx::optional sut; sut = 123; EXPECT_THAT(m_sutWithValue == sut, Eq(true)); } TEST_F(Optional_test, CompareWithEqualNullopt) { ::testing::Test::RecordProperty("TEST_ID", "56a140fc-d850-4a7e-97d6-595afe48c1f7"); iox::cxx::optional sut{iox::cxx::nullopt_t()}; EXPECT_THAT(m_sutNoValue == sut, Eq(true)); } TEST_F(Optional_test, CompareWithInequalValue) { ::testing::Test::RecordProperty("TEST_ID", "cfa1b454-982f-489b-93f6-a4fcd2f4be00"); m_sutWithValue = 123; iox::cxx::optional sut; sut = 1231; EXPECT_THAT(m_sutWithValue == sut, Eq(false)); } TEST_F(Optional_test, CompareWithNoValueWithValue) { ::testing::Test::RecordProperty("TEST_ID", "9a30d3c1-fb0e-4b3b-be87-ce9fd2858549"); iox::cxx::optional sut; sut = 1231; EXPECT_THAT(m_sutNoValue == sut, Eq(false)); } TEST_F(Optional_test, CompareWithNoValueWithNullopt) { ::testing::Test::RecordProperty("TEST_ID", "9f1eb75d-9fca-43fd-93e6-3fd448c3583b"); EXPECT_THAT(m_sutNoValue == iox::cxx::nullopt_t(), Eq(true)); } TEST_F(Optional_test, CompareWithValueWithNullopt) { ::testing::Test::RecordProperty("TEST_ID", "3dc645c7-7baf-4367-ae59-e18799bb910e"); EXPECT_THAT(m_sutWithValue == iox::cxx::nullopt_t(), Eq(false)); } TEST_F(Optional_test, NotCompareWithEqualValue) { ::testing::Test::RecordProperty("TEST_ID", "ce501d15-41ed-4666-b3ec-e22fc79c24de"); m_sutWithValue = 123; iox::cxx::optional sut; sut = 123; EXPECT_THAT(m_sutWithValue != sut, Eq(false)); } TEST_F(Optional_test, NotCompareWithNullopt) { ::testing::Test::RecordProperty("TEST_ID", "0668eb1b-bc73-4549-9829-ece80f3700cd"); EXPECT_THAT(m_sutWithValue != iox::cxx::nullopt_t(), Eq(true)); } TEST_F(Optional_test, CopyCTorWithValue) { ::testing::Test::RecordProperty("TEST_ID", "5a65a6dd-5442-4afa-8c78-6ad5d5ec10a6"); iox::cxx::optional sut{TestClass{4711, 1337}}; iox::cxx::optional sut2(sut); ASSERT_THAT(sut2.has_value(), Eq(true)); EXPECT_THAT(sut2->value, Eq(4711)); EXPECT_THAT(sut2->secondValue, Eq(1337)); } TEST_F(Optional_test, CopyCTorWithNoValue) { ::testing::Test::RecordProperty("TEST_ID", "597ca8af-264b-4261-9223-15854e7f351a"); iox::cxx::optional sut = iox::cxx::nullopt_t(); iox::cxx::optional sut2(sut); ASSERT_THAT(sut2.has_value(), Eq(false)); } TEST_F(Optional_test, CopyAssignmentWithValue) { ::testing::Test::RecordProperty("TEST_ID", "af7f0a3b-feef-49dc-9f4f-af1eb4af4ef1"); iox::cxx::optional sut2{TestClass{7474, 33331}}; { iox::cxx::optional sut{TestClass{4711, 1337}}; sut2 = sut; } ASSERT_THAT(sut2.has_value(), Eq(true)); EXPECT_THAT(sut2->value, Eq(4711)); EXPECT_THAT(sut2->secondValue, Eq(1337)); } TEST_F(Optional_test, CopyAssignmentNoValue) { ::testing::Test::RecordProperty("TEST_ID", "8e6e5a06-91ec-4adb-aa99-58d7d9424410"); iox::cxx::optional sut2{TestClass{7474, 33331}}; { iox::cxx::optional sut = iox::cxx::nullopt_t(); sut2 = sut; } ASSERT_THAT(sut2.has_value(), Eq(false)); } TEST_F(Optional_test, CopyAssignmentFromNoValueToNoValue) { ::testing::Test::RecordProperty("TEST_ID", "ef04eb32-4a55-4367-823c-7e075fecfc41"); iox::cxx::optional sut1; iox::cxx::optional sut2; sut2 = sut1; ASSERT_THAT(sut2.has_value(), Eq(false)); } TEST_F(Optional_test, MoveCTorWithValue) { ::testing::Test::RecordProperty("TEST_ID", "a7694c42-fb4d-4c53-930b-f0be78127027"); iox::cxx::optional sut{TestClass{4711, 1337}}; iox::cxx::optional sut2(std::move(sut)); ASSERT_THAT(sut2.has_value(), Eq(true)); EXPECT_THAT(sut2->value, Eq(4711)); EXPECT_THAT(sut2->secondValue, Eq(1337)); EXPECT_THAT(sut.has_value(), Eq(false)); } TEST_F(Optional_test, MoveCTorWithNoValue) { ::testing::Test::RecordProperty("TEST_ID", "039e0cd4-5825-41a3-be56-87922f429cc6"); iox::cxx::optional sut = iox::cxx::nullopt_t(); iox::cxx::optional sut2(std::move(sut)); ASSERT_THAT(sut2.has_value(), Eq(false)); EXPECT_THAT(sut.has_value(), Eq(false)); } TEST_F(Optional_test, MoveAssignmentWithValue) { ::testing::Test::RecordProperty("TEST_ID", "d671c710-c6e6-4f70-a05c-29134648f2df"); iox::cxx::optional sut2{TestClass{7718, 80091}}; { iox::cxx::optional sut{TestClass{4711, 1337}}; sut2 = std::move(sut); } ASSERT_THAT(sut2.has_value(), Eq(true)); EXPECT_THAT(sut2->value, Eq(4711)); EXPECT_THAT(sut2->secondValue, Eq(1337)); } TEST_F(Optional_test, MoveAssignmentWithNoValue) { ::testing::Test::RecordProperty("TEST_ID", "50435160-968f-4286-a1af-3a8ab42c50fb"); iox::cxx::optional sut2{{0, 0}}; sut2->value = 7718; sut2->secondValue = 80091; { iox::cxx::optional sut = iox::cxx::nullopt_t(); sut2 = std::move(sut); } ASSERT_THAT(sut2.has_value(), Eq(false)); } TEST_F(Optional_test, MoveAssignmentFromNoValueToNoValue) { ::testing::Test::RecordProperty("TEST_ID", "bcc0cbed-e969-43c8-963c-8370c9b48f55"); iox::cxx::optional sut1; iox::cxx::optional sut2; sut2 = std::move(sut1); ASSERT_THAT(sut2.has_value(), Eq(false)); } namespace { struct DTorTest { static uint64_t dtorCounter; ~DTorTest() { dtorCounter++; } }; uint64_t DTorTest::dtorCounter = 0; } // namespace TEST_F(Optional_test, Destructor) { ::testing::Test::RecordProperty("TEST_ID", "41a225e4-6964-45da-9f94-ca9c79685814"); { DTorTest::dtorCounter = 0; iox::cxx::optional sut{DTorTest()}; EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of temporary object only DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); } TEST_F(Optional_test, DestructorOnCopyCTor) { ::testing::Test::RecordProperty("TEST_ID", "42a8d5f6-2d7f-4ad8-8433-f4e4b82b4eec"); { iox::cxx::optional sut{DTorTest()}; { DTorTest::dtorCounter = 0; iox::cxx::optional sut2{sut}; EXPECT_THAT(DTorTest::dtorCounter, Eq(0)); } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut2 EXPECT_THAT(sut.has_value(), Eq(true)); DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut } TEST_F(Optional_test, DestructorOnCopyAssignment) { ::testing::Test::RecordProperty("TEST_ID", "49aae157-ea1f-4998-b4bd-07e5ade6ce02"); { iox::cxx::optional sut{DTorTest()}; { iox::cxx::optional sut2{DTorTest()}; DTorTest::dtorCounter = 0; sut = sut2; EXPECT_THAT(DTorTest::dtorCounter, Eq(0)); } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut2 EXPECT_THAT(sut.has_value(), Eq(true)); DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut } TEST_F(Optional_test, DestructorOnMoveCTor) { ::testing::Test::RecordProperty("TEST_ID", "95126b4b-7700-4537-b15f-c9b4697b2d28"); { iox::cxx::optional sut{DTorTest()}; { DTorTest::dtorCounter = 0; iox::cxx::optional sut2{std::move(sut)}; EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut EXPECT_THAT(sut.has_value(), Eq(false)); DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut2 DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(0)); // sut dtor already called } TEST_F(Optional_test, DestructorOnMoveAssignment) { ::testing::Test::RecordProperty("TEST_ID", "c9071ba6-71eb-4926-bf7b-8348c6543e59"); { iox::cxx::optional sut{DTorTest()}; { iox::cxx::optional sut2{DTorTest()}; DTorTest::dtorCounter = 0; sut = std::move(sut2); EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut2 DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(0)); } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut } TEST_F(Optional_test, DestructorOnEmplace) { ::testing::Test::RecordProperty("TEST_ID", "974aa499-8dc2-4b7d-8ed8-fea66f3f3358"); { iox::cxx::optional sut{DTorTest()}; { iox::cxx::optional sut2{DTorTest()}; DTorTest::dtorCounter = 0; sut2.emplace(sut.value()); EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of previous sut2 value DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut2 DTorTest::dtorCounter = 0; } EXPECT_THAT(DTorTest::dtorCounter, Eq(1)); // dtor of sut } TEST_F(Optional_test, MakeOptional) { ::testing::Test::RecordProperty("TEST_ID", "eda0230c-ea45-4303-8995-9fca39d1732e"); struct Make { Make() = default; Make(int a, int b) : a(a) , b(b) { } int a{0}; int b{0}; }; auto sut1 = iox::cxx::make_optional(123, 456); EXPECT_THAT(sut1->a, Eq(123)); EXPECT_THAT(sut1->b, Eq(456)); auto sut2 = iox::cxx::make_optional(); EXPECT_THAT(sut2->a, Eq(0)); EXPECT_THAT(sut2->b, Eq(0)); } TEST_F(Optional_test, AndThenWhenContainingValue) { ::testing::Test::RecordProperty("TEST_ID", "c28d8a22-f5f4-4c1b-9ac1-1f98c0eb3c46"); iox::cxx::optional sut(123); int value = 0; sut.and_then([&](int& v) { value = v; }); EXPECT_THAT(value, Eq(123)); } TEST_F(Optional_test, AndThenWhenNotContainingValue) { ::testing::Test::RecordProperty("TEST_ID", "8497ede2-e77b-4e13-b975-c23cc21ff32d"); iox::cxx::optional sut; int value = 0; sut.and_then([&](int&) { value = 42; }); EXPECT_THAT(value, Eq(0)); } TEST_F(Optional_test, OrElseWhenContainingValue) { ::testing::Test::RecordProperty("TEST_ID", "8d685d4c-1334-4d31-953e-e5acba4f6b0f"); iox::cxx::optional sut(123); int value = 0; sut.or_else([&] { value = 42; }); EXPECT_THAT(value, Eq(0)); } TEST_F(Optional_test, OrElseWhenNotContainingValue) { ::testing::Test::RecordProperty("TEST_ID", "b56cad5b-d728-4c74-81d6-3b65a970b96b"); iox::cxx::optional sut; int value = 0; sut.or_else([&] { value = 42; }); EXPECT_THAT(value, Eq(42)); } TEST_F(Optional_test, ReturningNulloptWithoutConstruction) { ::testing::Test::RecordProperty("TEST_ID", "689f4d36-5e66-4bae-8122-40bfc3f7c8f1"); auto val = []() -> iox::cxx::optional { return iox::cxx::nullopt; }(); EXPECT_THAT(val.has_value(), Eq(false)); } TEST_F(Optional_test, CopyConstructionWithElementWorks) { ::testing::Test::RecordProperty("TEST_ID", "5839d59d-b564-4d82-acee-b324903cd4f9"); const TestClass testClass{5, 6}; iox::cxx::optional sut(testClass); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->value, Eq(5)); EXPECT_THAT(sut->secondValue, Eq(6)); } constexpr char DEFAULT_STRING[]{"Live long and prosper"}; constexpr int8_t DEFAULT_INT{0}; constexpr int8_t DEFAULT_MULTIPLICATOR{2}; struct TestStructForInPlaceConstruction { TestStructForInPlaceConstruction() = default; ~TestStructForInPlaceConstruction() = default; TestStructForInPlaceConstruction(const TestStructForInPlaceConstruction&) = delete; TestStructForInPlaceConstruction(TestStructForInPlaceConstruction&&) = delete; TestStructForInPlaceConstruction& operator=(const TestStructForInPlaceConstruction&) = delete; TestStructForInPlaceConstruction& operator=(TestStructForInPlaceConstruction&&) = delete; TestStructForInPlaceConstruction(const int8_t& val) : val(val) { } TestStructForInPlaceConstruction(int8_t&& val) : val(DEFAULT_MULTIPLICATOR * val) { } TestStructForInPlaceConstruction(std::unique_ptr ptr) : ptr(std::move(ptr)) { } TestStructForInPlaceConstruction(const int8_t& val, std::unique_ptr ptr) : val(val) , ptr(std::move(ptr)) { } int8_t val{DEFAULT_INT}; std::unique_ptr ptr{new std::string(DEFAULT_STRING)}; }; TEST_F(Optional_test, InPlaceConstructionCtorCallsDefCtorWhenCalledWithoutArgs) { ::testing::Test::RecordProperty("TEST_ID", "64c0b0ff-7362-4e21-b7b7-189af9a3a060"); iox::cxx::optional sut(iox::cxx::in_place); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->val, Eq(DEFAULT_INT)); ASSERT_TRUE(sut->ptr); EXPECT_THAT(sut->ptr->c_str(), StrEq(DEFAULT_STRING)); } TEST_F(Optional_test, InPlaceConstructionCtorCallsCorrectCtorWhenCalledWithLVal) { ::testing::Test::RecordProperty("TEST_ID", "132aeab3-e370-4afb-a418-f115b97ce7a5"); constexpr int8_t VAL = 46; iox::cxx::optional sut(iox::cxx::in_place, VAL); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->val, Eq(VAL)); ASSERT_TRUE(sut->ptr); EXPECT_THAT(sut->ptr->c_str(), StrEq(DEFAULT_STRING)); } TEST_F(Optional_test, InPlaceConstructionCtorCallsCorrectCtorWhenCalledWithPodRVal) { ::testing::Test::RecordProperty("TEST_ID", "de7e36ea-44f9-4b82-9b0d-0bce8af2a10a"); int8_t val = 23; iox::cxx::optional sut(iox::cxx::in_place, std::move(val)); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->val, Eq(DEFAULT_MULTIPLICATOR * val)); ASSERT_TRUE(sut->ptr); EXPECT_THAT(sut->ptr->c_str(), StrEq(DEFAULT_STRING)); } TEST_F(Optional_test, InPlaceConstructionCtorCallsCorrectCtorWhenCalledWithComplexTypeRVal) { ::testing::Test::RecordProperty("TEST_ID", "2a43bdf4-dfdf-4b3b-908b-d162b13435a9"); constexpr char NEW_STRING[]{"Without followers, evil cannot spread"}; std::unique_ptr ptr(new std::string(NEW_STRING)); iox::cxx::optional sut(iox::cxx::in_place, std::move(ptr)); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->val, Eq(DEFAULT_INT)); ASSERT_TRUE(sut->ptr); EXPECT_THAT(sut->ptr->c_str(), StrEq(NEW_STRING)); } TEST_F(Optional_test, InPlaceConstructionCtorCallsCorrectCtorWhenCalledWithMixedArgs) { ::testing::Test::RecordProperty("TEST_ID", "49f1376c-6723-4231-83da-4682e89f1b6e"); constexpr int8_t VAL = 11; constexpr char NEW_STRING[]{"Insufficient facts always invite danger"}; std::unique_ptr ptr(new std::string(NEW_STRING)); iox::cxx::optional sut(iox::cxx::in_place, VAL, std::move(ptr)); ASSERT_TRUE(sut.has_value()); EXPECT_THAT(sut->val, Eq(VAL)); ASSERT_TRUE(sut->ptr); EXPECT_THAT(sut->ptr->c_str(), StrEq(NEW_STRING)); } } // namespace