/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include "UserTypes.h" namespace UnitTest { template std::pair to_std_pair(const AZStd::pair& azPair) { return std::make_pair(azPair.first, azPair.second); } template std::pair to_std_pair(AZStd::pair&& azPair) { return std::make_pair(AZStd::move(azPair.first), AZStd::move(azPair.second)); } ////////////////////////////////////////////////////////////////////////// // Fixtures // Fixture for non-typed tests class TupleTest : public AllocatorsFixture { protected: void SetUp() override { AllocatorsFixture::SetUp(); } void TearDown() override { AllocatorsFixture::TearDown(); } struct MoveOnlyType { AZ_CLASS_ALLOCATOR(MoveOnlyType, AZ::SystemAllocator, 0); MoveOnlyType() = delete; MoveOnlyType(AZStd::tuple tuplePair) : m_valueInt(AZStd::move(AZStd::get<0>(tuplePair))) , m_constString(AZStd::move(AZStd::get<1>(tuplePair))) { } AZ::s32 m_valueInt; AZStd::string_view m_constString; }; }; // Fixture for typed tests template class TupleTypedTest : public AllocatorsFixture { protected: void SetUp() override { AllocatorsFixture::SetUp(); } void TearDown() override { AllocatorsFixture::TearDown(); } }; using TupleTestTypes = ::testing::Types< AZStd::tuple<>, AZStd::tuple>, AZStd::tuple, AZStd::tuple, AZStd::tuple >; TYPED_TEST_CASE(TupleTypedTest, TupleTestTypes); ////////////////////////////////////////////////////////////////////////// // Tests for constructors namespace Constructor { // Construct empty TEST_F(TupleTest, DefaultConstruct) { AZStd::tuple> testTuple; EXPECT_TRUE(AZStd::get<0>(testTuple).empty()); EXPECT_FLOAT_EQ(0.0f, AZStd::get<1>(testTuple)); EXPECT_TRUE(AZStd::get<2>(testTuple).empty()); } TEST_F(TupleTest, InitializerConstruct) { AZStd::tuple testTuple{ AZStd::string("Test"), 4.0f }; EXPECT_EQ("Test", AZStd::get<0>(testTuple)); EXPECT_FLOAT_EQ(4.0f, AZStd::get<1>(testTuple)); } TEST_F(TupleTest, ConvertingConstruct) { AZStd::tuple> testTuple("Test", { { "Arg1", "Arg2" } }); EXPECT_EQ("Test", AZStd::get<0>(testTuple)); EXPECT_EQ(2, AZStd::get<1>(testTuple).size()); EXPECT_EQ("Arg1", AZStd::get<1>(testTuple)[0]); EXPECT_EQ("Arg2", AZStd::get<1>(testTuple)[1]); } TEST_F(TupleTest, PairCopyConstruct) { AZStd::vector testStringVector{ "Test1", "Test2" }; auto testPair = std::make_pair(AZStd::string("Test"), AZStd::vector{ "Test1", "Test2" }); AZStd::tuple> testTuple(testPair); EXPECT_EQ(testPair.first, AZStd::get<0>(testTuple)); EXPECT_EQ(testPair.second, AZStd::get<1>(testTuple)); } TEST_F(TupleTest, PairMoveConstruct) { AZStd::vector testStringVector{ "Test1", "Test2" }; auto testPair = std::make_pair(AZStd::string("Test"), testStringVector); AZStd::tuple> testTuple(AZStd::move(testPair)); EXPECT_EQ("Test", AZStd::get<0>(testTuple)); EXPECT_EQ(testStringVector, AZStd::get<1>(testTuple)); } TEST_F(TupleTest, TupleCopyConstruct) { AZStd::tuple> testTuple("Test", 16.0, AZStd::vector{5}); AZStd::tuple> copyTuple(testTuple); EXPECT_EQ(testTuple, copyTuple); } TEST_F(TupleTest, TupleMoveConstruct) { AZStd::tuple> testTuple("Test", 16.0, AZStd::vector{5}); AZStd::tuple> moveTuple(testTuple); EXPECT_EQ("Test", AZStd::get<0>(moveTuple)); EXPECT_EQ(16.0, AZStd::get<1>(moveTuple)); EXPECT_EQ(AZStd::vector{5}, AZStd::get<2>(moveTuple)); } } ////////////////////////////////////////////////////////////////////////// // Tests for assignment operator namespace Assignment { // Test copy assign TEST_F(TupleTest, CopyAssignTest) { AZStd::tuple source(AZStd::make_any("Test")); AZStd::tuple dest; EXPECT_TRUE(AZStd::get<0>(source).is()); EXPECT_EQ("Test", AZStd::any_cast(AZStd::get<0>(source))); EXPECT_TRUE(AZStd::get<0>(dest).empty()); dest = source; EXPECT_TRUE(AZStd::get<0>(dest).is()); EXPECT_EQ(AZStd::any_cast(AZStd::get<0>(source)), AZStd::any_cast(AZStd::get<0>(dest))); } // Test move assign TEST_F(TupleTest, MoveAssignTest) { AZStd::tuple source(AZStd::make_any("Test")); AZStd::tuple dest; EXPECT_TRUE(AZStd::get<0>(source).is()); EXPECT_EQ("Test", AZStd::any_cast(AZStd::get<0>(source))); EXPECT_TRUE(AZStd::get<0>(dest).empty()); dest = AZStd::move(source); EXPECT_TRUE(AZStd::get<0>(source).empty()); EXPECT_FALSE(AZStd::get<0>(dest).empty()); } } namespace FreeFunctions { TEST_F(TupleTest, MakeTupleTest) { auto testTuple = AZStd::make_tuple(AZStd::string("Test"), AZStd::make_unique("UniqueTest")); EXPECT_EQ("Test", AZStd::get<0>(testTuple)); ASSERT_NE(nullptr, AZStd::get<1>(testTuple)); EXPECT_EQ("UniqueTest", *AZStd::get<1>(testTuple)); } TEST_F(TupleTest, TieTest) { int valueInt = 0; double valueDouble = 0.0; auto testTuple = AZStd::make_tuple(17, 42.52); AZStd::tie(valueInt, valueDouble) = testTuple; EXPECT_EQ(17, valueInt); EXPECT_DOUBLE_EQ(valueDouble, 42.52); auto ignoreTuple = AZStd::make_tuple(true, 1, 15.0f); bool valueBool = false; AZStd::tie(valueBool, AZStd::ignore, AZStd::ignore) = ignoreTuple; EXPECT_TRUE(valueBool); } TEST_F(TupleTest, ForwardAsTupleTest) { MoveOnlyType testResult(AZStd::forward_as_tuple(15, AZStd::string_view("LinkerLiteral"))); EXPECT_EQ(15, testResult.m_valueInt); EXPECT_EQ("LinkerLiteral", testResult.m_constString); } TEST_F(TupleTest, TupleCatTest) { AZStd::tuple intFloatDoubleTuple{ 1, 2.0f, 4.0 }; AZStd::tuple<> emptyTuple; AZStd::tuple stringStringTuple("First", "Second"); auto resultTuple = AZStd::tuple_cat(intFloatDoubleTuple, emptyTuple, stringStringTuple); EXPECT_EQ(5, AZStd::tuple_size::value); } TEST_F(TupleTest, TupleGetTest) { AZStd::tuple intFloatDoubleTuple{ 1, 2.0f, 4.0 }; EXPECT_EQ(AZStd::get<0>(intFloatDoubleTuple), AZStd::get(intFloatDoubleTuple)); EXPECT_EQ(AZStd::get<1>(intFloatDoubleTuple), AZStd::get(intFloatDoubleTuple)); EXPECT_EQ(AZStd::get<2>(intFloatDoubleTuple), AZStd::get(intFloatDoubleTuple)); const AZStd::tuple stringFloatIntTuple("Test", 2.0f, 8); EXPECT_EQ(AZStd::get<0>(stringFloatIntTuple), AZStd::get(stringFloatIntTuple)); EXPECT_EQ(AZStd::get<1>(stringFloatIntTuple), AZStd::get(stringFloatIntTuple)); EXPECT_EQ(AZStd::get<2>(stringFloatIntTuple), AZStd::get(stringFloatIntTuple)); AZStd::tuple rValueTuple("FirstTest", false, "SecondTest"); EXPECT_EQ(AZStd::get<1>(AZStd::move(rValueTuple)), AZStd::get(AZStd::move(rValueTuple))); // Type template version of get is only supported for types that are in the template exactly once // It is a static assert to attempt to access an element using AZStd::get as there are two of those types in the tuple EXPECT_NE(AZStd::get<0>(AZStd::move(AZStd::move(rValueTuple))), AZStd::get<2>(AZStd::move(rValueTuple))); } TEST_F(TupleTest, TupleHashTest) { AZStd::string testString("Test1"); AZStd::string superDuperString("SuperDuperString"); AZStd::unordered_map, AZStd::string> tupleToBoolMap; tupleToBoolMap.emplace(AZStd::make_tuple(testString, 234, true), "MappedValue"); tupleToBoolMap.insert({ AZStd::make_tuple(superDuperString, -8345, false), "SecondHallPass" }); EXPECT_EQ(2, tupleToBoolMap.size()); auto foundIt = tupleToBoolMap.find(AZStd::make_tuple(testString, 234, true)); ASSERT_NE(tupleToBoolMap.end(), foundIt); EXPECT_EQ("MappedValue", foundIt->second); foundIt = tupleToBoolMap.find(AZStd::make_tuple(superDuperString, -8345, false)); ASSERT_NE(tupleToBoolMap.end(), foundIt); EXPECT_EQ("SecondHallPass", foundIt->second); // Not found case foundIt = tupleToBoolMap.find(AZStd::make_tuple(superDuperString, -8345, true)); EXPECT_EQ(tupleToBoolMap.end(), foundIt); foundIt = tupleToBoolMap.find(AZStd::make_tuple(superDuperString, -8344, false)); EXPECT_EQ(tupleToBoolMap.end(), foundIt); foundIt = tupleToBoolMap.find(AZStd::make_tuple("SuperDuperString1", -8345, false)); EXPECT_EQ(tupleToBoolMap.end(), foundIt); } } namespace AssignTest { struct BaseIntWrapper { explicit BaseIntWrapper(AZ::s32 num = 0) : m_num(num) {} virtual ~BaseIntWrapper() = default; AZ::s32 m_num; }; struct DerivedIntWrapper : BaseIntWrapper { explicit DerivedIntWrapper(AZ::s32 num = 0) : BaseIntWrapper(num) {} }; struct IntAssignWrapper { IntAssignWrapper() = default; IntAssignWrapper& operator=(int) { return *this; } }; TEST_F(TupleTest, CopyPair_AssignTest) { AZStd::pair pair0(2U, 'a'); AZStd::tuple tuple0; tuple0 = to_std_pair(pair0); EXPECT_EQ(2U, AZStd::get<0>(tuple0)); EXPECT_EQ(AZ::s16('a'), AZStd::get<1>(tuple0)); } TEST_F(TupleTest, MovePair_AssignTest) { AZStd::pair> pair0(2U, AZStd::make_unique(3)); AZStd::tuple> tuple0; tuple0 = to_std_pair(AZStd::move(pair0)); EXPECT_EQ(2U, AZStd::get<0>(tuple0)); EXPECT_EQ(3, AZStd::get<1>(tuple0)->m_num); } TEST_F(TupleTest, ConversionCopy_AssignTest) { { AZStd::tuple tuple0(2U); AZStd::tuple tuple1; tuple1 = tuple0; EXPECT_EQ(2U, AZStd::get<0>(tuple1)); } { AZStd::tuple tuple0(2U, 'a'); AZStd::tuple tuple1; tuple1 = tuple0; EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); } { AZStd::tuple tuple0(2U, 'a', DerivedIntWrapper(3)); AZStd::tuple tuple1; tuple1 = tuple0; EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); EXPECT_EQ(3, AZStd::get<2>(tuple1).m_num); } { DerivedIntWrapper derived0(3); DerivedIntWrapper derived1(2); AZStd::tuple tuple0(2U, 'a', derived1); AZStd::tuple tuple1(1, 'b', derived0); tuple1 = tuple0; EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); EXPECT_EQ(2, AZStd::get<2>(tuple1).m_num); } { AZ::s32 x = 42; AZ::s32 y = 43; AZStd::tuple tuple0(AZStd::move(x)); AZStd::tuple tuple1(y); tuple0 = tuple1; EXPECT_EQ(43, AZStd::get<0>(tuple0)); EXPECT_EQ(&x, &AZStd::get<0>(tuple0)); } } TEST_F(TupleTest, ConversionMove_AssignTest) { { AZStd::tuple tuple0(2U); AZStd::tuple tuple1; tuple1 = AZStd::move(tuple0); EXPECT_EQ(2U, AZStd::get<0>(tuple1)); } { AZStd::tuple tuple0(2U, 'a'); AZStd::tuple tuple1; tuple1 = AZStd::move(tuple0); EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); } { AZStd::tuple tuple0(2U, 'a', DerivedIntWrapper(3)); AZStd::tuple tuple1; tuple1 = AZStd::move(tuple0); EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); EXPECT_EQ(3, AZStd::get<2>(tuple1).m_num); } { DerivedIntWrapper derived0(3); DerivedIntWrapper derived1(2); AZStd::tuple tuple0(2U, 'a', derived1); AZStd::tuple tuple1(1, 'b', derived0); tuple1 = AZStd::move(tuple0); EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); EXPECT_EQ(2, AZStd::get<2>(tuple1).m_num); } { AZStd::tuple> tuple0(2U, 'a', AZStd::make_unique(3)); AZStd::tuple> tuple1; tuple1 = AZStd::move(tuple0); EXPECT_EQ(2U, AZStd::get<0>(tuple1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(tuple1)); EXPECT_EQ(3, AZStd::get<2>(tuple1)->m_num); } { AZ::s32 x = 42; AZ::s32 y = 43; AZStd::tuple tuple0(AZStd::move(x), IntAssignWrapper{}); AZStd::tuple tuple1(AZStd::move(y), 44); tuple0 = AZStd::move(tuple1); EXPECT_EQ(43, AZStd::get<0>(tuple0)); EXPECT_EQ(&x, &AZStd::get<0>(tuple0)); } } struct NonAssignable { private: NonAssignable& operator=(const NonAssignable&) = delete; NonAssignable& operator=(NonAssignable&&) = delete; }; struct CopyAssignable { CopyAssignable& operator=(const CopyAssignable&) = default; CopyAssignable& operator=(CopyAssignable&&) = delete; }; AZ_TEST_STATIC_ASSERT((AZStd::is_copy_assignable::value)); struct MoveAssignable { MoveAssignable& operator=(MoveAssignable&&) { return *this; } private: MoveAssignable& operator=(const MoveAssignable&) = delete; }; TEST_F(TupleTest, Copy_AssignTest) { { AZStd::tuple t0(2); AZStd::tuple t; t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); } { AZStd::tuple t0(2, 'a'); AZStd::tuple t; t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); EXPECT_EQ('a', AZStd::get<1>(t)); } { using T = AZStd::tuple; const T t0(2, 'a', "some text"); T t; t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); EXPECT_EQ('a', AZStd::get<1>(t)); EXPECT_EQ("some text", AZStd::get<2>(t)); } { // test reference assignment. using T = AZStd::tuple; AZ::s32 x = 42; AZ::s32 y = 100; AZ::s32 x2 = -1; AZ::s32 y2 = 500; T t(x, AZStd::move(y)); T t2(x2, AZStd::move(y2)); t = t2; EXPECT_EQ(x2, AZStd::get<0>(t)); EXPECT_EQ(&x, &AZStd::get<0>(t)); EXPECT_EQ(y2, AZStd::get<1>(t)); EXPECT_EQ(&y, &AZStd::get<1>(t)); } } struct CountAssign { CountAssign() = default; CountAssign& operator=(const CountAssign &) { ++copied; return *this; } CountAssign& operator=(CountAssign&&) { ++moved; return *this; } static void reset() { copied = moved = 0; } static AZ::s32 copied; static AZ::s32 moved; }; AZ::s32 CountAssign::copied = 0; AZ::s32 CountAssign::moved = 0; struct MoveOnly { MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; MoveOnly(MoveOnly&& other) : m_num(AZStd::move(other.m_num)) {} MoveOnly& operator=(MoveOnly&& other) { m_num = other.m_num; return *this; } MoveOnly(AZ::s32 num) : m_num(num) {} friend bool operator==(AZ::s32 lhs, const MoveOnly& rhs) { return lhs == rhs.m_num; } friend bool operator==(const MoveOnly& lhs, AZ::s32 rhs) { return lhs.m_num == rhs; } AZ::s32 m_num; }; TEST_F(TupleTest, Move_AssignTest) { { using T = AZStd::tuple<>; T t0; T t; t = AZStd::move(t0); } { using T = AZStd::tuple; T t0(MoveOnly(0)); T t; t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); } { using T = AZStd::tuple; T t0(MoveOnly(0), MoveOnly(1)); T t; t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(1, AZStd::get<1>(t)); } { using T = AZStd::tuple; T t0(MoveOnly(0), MoveOnly(1), MoveOnly(2)); T t; t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(1, AZStd::get<1>(t)); EXPECT_EQ(2, AZStd::get<2>(t)); } { // test reference assignment. using T = AZStd::tuple; AZ::s32 x = 42; AZ::s32 y = 100; AZ::s32 x2 = -1; AZ::s32 y2 = 500; T t(x, AZStd::move(y)); T t2(x2, AZStd::move(y2)); t = AZStd::move(t2); EXPECT_EQ(x2, AZStd::get<0>(t)); EXPECT_EQ(&x, &AZStd::get<0>(t)); EXPECT_EQ(y2, AZStd::get<1>(t)); EXPECT_EQ(&y, &AZStd::get<1>(t)); } { // test reference assignment. using T = AZStd::tuple; AZ::s32 x = 42; AZ::s32 y = 100; AZ::s32 x2 = -1; AZ::s32 y2 = 500; T t(x, AZStd::move(y)); T t2(x2, AZStd::move(y2)); t = AZStd::move(t2); EXPECT_EQ(x2, AZStd::get<0>(t)); EXPECT_EQ(&x, &AZStd::get<0>(t)); EXPECT_EQ(y2, AZStd::get<1>(t)); EXPECT_EQ(&y, &AZStd::get<1>(t)); } } } namespace ConstructTest { struct Empty {}; struct Explicit { explicit Explicit(AZ::s32 x) : m_value(x) {} AZ::s32 m_value; }; struct Implicit { Implicit(AZ::s32 x) : m_value(x) {} int m_value; }; struct BaseIntWrapper { BaseIntWrapper(AZ::s32 num = 0) : m_num(num) {} virtual ~BaseIntWrapper() = default; AZ::s32 m_num; }; struct DerivedIntWrapper : BaseIntWrapper { explicit DerivedIntWrapper(AZ::s32 num = 0) : BaseIntWrapper(num) {} }; struct IntAssignWrapper { IntAssignWrapper() = default; IntAssignWrapper& operator=(int) { return *this; } }; struct DefaultOnly { DefaultOnly() = default; friend bool operator==(const DefaultOnly&, const DefaultOnly&) { return true; } }; struct NoDefault { private: NoDefault() = delete; public: explicit NoDefault(int) {} }; struct IllFormedDefault { IllFormedDefault(AZ::s32 x) : value(x) {} template IllFormedDefault() { AZ_TEST_STATIC_ASSERT(Pred); } AZ::s32 value; }; struct ConstructsWithTupleLeaf { ConstructsWithTupleLeaf() {} ConstructsWithTupleLeaf(const ConstructsWithTupleLeaf &) { GTEST_NONFATAL_FAILURE_("Copy Constructor should not be invoked"); } ConstructsWithTupleLeaf(ConstructsWithTupleLeaf &&) {} template ConstructsWithTupleLeaf(T) { static_assert((!AZStd::is_same::value), "Constructor instantiated for type other than int"); } }; struct MoveOnly { MoveOnly() = default; MoveOnly(const MoveOnly&) = delete; MoveOnly(MoveOnly&& other) : m_num(AZStd::move(other.m_num)) {} MoveOnly& operator=(MoveOnly&& other) { m_num = other.m_num; return *this; } MoveOnly(AZ::s32 num) : m_num(num) {} friend bool operator==(AZ::s32 lhs, const MoveOnly& rhs) { return lhs == rhs.m_num; } friend bool operator==(const MoveOnly& lhs, AZ::s32 rhs) { return lhs.m_num == rhs; } AZ::s32 m_num; }; struct ImplicitConstexpr { constexpr ImplicitConstexpr(int i) : m_num(i) {} friend constexpr bool operator==(const ImplicitConstexpr& x, const ImplicitConstexpr& y) { return x.m_num == y.m_num; } int m_num; }; struct ExplicitConstexpr { constexpr explicit ExplicitConstexpr(int i) : m_num(i) {} friend constexpr bool operator==(const ExplicitConstexpr& x, const ExplicitConstexpr& y) { return x.m_num == y.m_num; } int m_num; }; TEST_F(TupleTest, Default_ConstructTest) { { AZStd::tuple<> t; (void)t; } { AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); } { AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(nullptr, AZStd::get<1>(t)); } { AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(nullptr, AZStd::get<1>(t)); EXPECT_EQ("", AZStd::get<2>(t)); } { AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(nullptr, AZStd::get<1>(t)); EXPECT_EQ("", AZStd::get<2>(t)); EXPECT_EQ(DefaultOnly(), AZStd::get<3>(t)); } { AZ_TEST_STATIC_ASSERT((!std::is_default_constructible>::value)); AZ_TEST_STATIC_ASSERT((!std::is_default_constructible>::value)); AZ_TEST_STATIC_ASSERT((!std::is_default_constructible>::value)); } { const AZStd::tuple<> t; (void)t; } { const AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); } { const AZStd::tuple t; EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(nullptr, AZStd::get<1>(t)); } { // Check that the SFINAE on the default constructor is not evaluated when // it isn't needed. If the default constructor is evaluated then this test // should fail to compile. IllFormedDefault v(0); AZStd::tuple t(v); } } TEST_F(TupleTest, IsDestructible_ConstructTest) { AZ_TEST_STATIC_ASSERT((std::is_trivially_destructible >::value)); AZ_TEST_STATIC_ASSERT((std::is_trivially_destructible >::value)); AZ_TEST_STATIC_ASSERT((std::is_trivially_destructible >::value)); AZ_TEST_STATIC_ASSERT((!std::is_trivially_destructible >::value)); AZ_TEST_STATIC_ASSERT((!std::is_trivially_destructible >::value)); } TEST_F(TupleTest, CopyPair_ConstructTest) { { using T0 = AZStd::pair; using T1 = AZStd::tuple; T0 t0(2, 'a'); T1 t1 = to_std_pair(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s16('a'), AZStd::get<1>(t1)); } { using P0 = AZStd::pair; using T1 = AZStd::tuple; const P0 p0(2, 'a'); const T1 t1 = to_std_pair(p0); EXPECT_EQ(AZStd::get<0>(p0), AZStd::get<0>(t1)); EXPECT_EQ(AZStd::get<1>(p0), AZStd::get<1>(t1)); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s16('a'), AZStd::get<1>(t1)); } } TEST_F(TupleTest, MovePair_ConstructTest) { { using T0 = AZStd::pair>; using T1 = AZStd::tuple>; T0 t0(2, AZStd::make_unique(3)); T1 t1 = to_std_pair(AZStd::move(t0)); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(3, AZStd::get<1>(t1)->m_num); } } TEST_F(TupleTest, ConversionCopy_ConstructTest) { { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2); T1 t1 = t0; EXPECT_EQ(2, AZStd::get<0>(t1)); } { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a'); T1 t1 = t0; EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); } { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a', DerivedIntWrapper(3)); T1 t1(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(3, AZStd::get<2>(t1).m_num); } { DerivedIntWrapper d(3); using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a', d); T1 t1(t0); d.m_num = 2; EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(2, AZStd::get<2>(t1).m_num); } { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a', 3); T1 t1(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(3, AZStd::get<2>(t1).m_num); } { const AZStd::tuple t1(42); AZStd::tuple t2(t1); EXPECT_EQ(42, AZStd::get<0>(t2).m_value); } { const AZStd::tuple t1(42); AZStd::tuple t2 = t1; EXPECT_EQ(42, AZStd::get<0>(t2).m_value); } } TEST_F(TupleTest, ConversionMove_ConstructTest) { { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2); T1 t1 = AZStd::move(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); } { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a'); T1 t1 = AZStd::move(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); } { using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a', DerivedIntWrapper(3)); T1 t1 = AZStd::move(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(3, AZStd::get<2>(t1).m_num); } { DerivedIntWrapper d(3); using T0 = AZStd::tuple; using T1 = AZStd::tuple; T0 t0(2, 'a', d); T1 t1 = AZStd::move(t0); d.m_num = 2; EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(2, AZStd::get<2>(t1).m_num); } { using T0 = AZStd::tuple>; using T1 = AZStd::tuple>; T0 t0(2, 'a', AZStd::make_unique(3)); T1 t1 = AZStd::move(t0); EXPECT_EQ(2, AZStd::get<0>(t1)); EXPECT_EQ(AZ::s32('a'), AZStd::get<1>(t1)); EXPECT_EQ(3, AZStd::get<2>(t1)->m_num); } { AZStd::tuple t1(42); AZStd::tuple t2(AZStd::move(t1)); EXPECT_EQ(42, AZStd::get<0>(t2).m_value); } { AZStd::tuple t1(42); AZStd::tuple t2 = AZStd::move(t1); EXPECT_EQ(42, AZStd::get<0>(t2).m_value); } } TEST_F(TupleTest, Copy_ConstructTest) { { using T = AZStd::tuple<>; T t0; T t = t0; ((void)t); // Prevent unused warning } { using T = AZStd::tuple; T t0(2); T t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); } { using T = AZStd::tuple; T t0(2, 'a'); T t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); EXPECT_EQ('a', AZStd::get<1>(t)); } { using T = AZStd::tuple; const T t0(2, 'a', "some text"); T t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); EXPECT_EQ('a', AZStd::get<1>(t)); EXPECT_EQ("some text", AZStd::get<2>(t)); } { using T = AZStd::tuple; const T t0(2); const T t = t0; EXPECT_EQ(2, AZStd::get<0>(t)); } { using T = AZStd::tuple; const T t0; const T t = t0; const Empty e = AZStd::get<0>(t); ((void)e); // Prevent unused warning } } TEST_F(TupleTest, Move_ConstructTest) { { using T = AZStd::tuple<>; T t0; T t = AZStd::move(t0); ((void)t); // Prevent unused warning } { using T = AZStd::tuple; T t0(MoveOnly(0)); T t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); } { using T = AZStd::tuple; T t0(MoveOnly(0), MoveOnly(1)); T t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(1, AZStd::get<1>(t)); } { using T = AZStd::tuple; T t0(MoveOnly(0), MoveOnly(1), MoveOnly(2)); T t = AZStd::move(t0); EXPECT_EQ(0, AZStd::get<0>(t)); EXPECT_EQ(1, AZStd::get<1>(t)); EXPECT_EQ(2, AZStd::get<2>(t)); } { using d_t = AZStd::tuple; d_t d((ConstructsWithTupleLeaf())); d_t d2(static_cast(d)); } } TEST_F(TupleTest, TemplateDepth_ConstructTest) { AZStd::array source; AZStd::tuple> resultTuple(source); } } namespace ApplyTest { static AZ::s32 const_sum_fn() { return 0; } template static AZ::s32 const_sum_fn(FirstInt x1, Ints... rest) { return static_cast(x1 + const_sum_fn(rest...)); } struct ConstSumT { ConstSumT() = default; template int operator()(Ints... values) const { return const_sum_fn(values...); } }; TEST_F(TupleTest, ConstEvaluation_ApplyTest) { ConstSumT sum_obj{}; { using Tup = AZStd::tuple<>; using Fn = AZ::s32(&)(); const Tup t; EXPECT_EQ(0, AZStd::apply(static_cast(const_sum_fn), t)); EXPECT_EQ(0, AZStd::apply(sum_obj, t)); } { using Tup = AZStd::tuple; using Fn = AZ::s32(&)(AZ::s32); const Tup t(42); EXPECT_EQ(42, AZStd::apply(static_cast(const_sum_fn<>), t)); EXPECT_EQ(42, AZStd::apply(sum_obj, t)); } { using Tup = AZStd::tuple; using Fn = AZ::s32(&)(AZ::s32, AZ::s64); const Tup t(42, 101); EXPECT_EQ(143, AZStd::apply(static_cast(const_sum_fn), t)); EXPECT_EQ(143, AZStd::apply(sum_obj, t)); } { using Tup = AZStd::pair; using Fn = AZ::s32(&)(AZ::s32, AZ::s64); const Tup t(42, 101); EXPECT_EQ(143, AZStd::apply(static_cast(const_sum_fn), t)); EXPECT_EQ(143, AZStd::apply(sum_obj, t)); } { using Tup = std::tuple; using Fn = AZ::s32(&)(AZ::s32, AZ::s64, AZ::s32); const Tup t(42, 101, -1); EXPECT_EQ(142, AZStd::apply(static_cast(const_sum_fn), t)); EXPECT_EQ(142, AZStd::apply(sum_obj, t)); } { using Tup = AZStd::array; using Fn = AZ::s32(&)(AZ::s32, AZ::s32, AZ::s32); const Tup t = { { 42, 101, -1 } }; AZ_TEST_STATIC_ASSERT(AZStd::tuple_size::value == 3); EXPECT_EQ(42, AZStd::get<0>(t)); EXPECT_EQ(142, AZStd::apply(static_cast(const_sum_fn), t)); EXPECT_EQ(142, AZStd::apply(sum_obj, t)); } } static int my_int = 42; template struct index {}; void RawFunc(index<0>) {} int RawFunc(index<1>) { return 0; } int & RawFunc(index<2>) { return static_cast(my_int); } int const & RawFunc(index<3>) { return static_cast(my_int); } int volatile & RawFunc(index<4>) { return static_cast(my_int); } int const volatile & RawFunc(index<5>) { return static_cast(my_int); } int && RawFunc(index<6>) { return static_cast(my_int); } int const && RawFunc(index<7>) { return static_cast(my_int); } int volatile && RawFunc(index<8>) { return static_cast(my_int); } int const volatile && RawFunc(index<9>) { return static_cast(my_int); } int * RawFunc(index<10>) { return static_cast(&my_int); } int const * RawFunc(index<11>) { return static_cast(&my_int); } int volatile * RawFunc(index<12>) { return static_cast(&my_int); } int const volatile * RawFunc(index<13>) { return static_cast(&my_int); } template void ReturnTypeTest() { using RawInvokeResult = decltype(RawFunc(index{})); AZ_TEST_STATIC_ASSERT((AZStd::is_same::value)); using FnType = RawInvokeResult(*) (index); FnType fn = RawFunc; (void)fn; AZStd::tuple> t; (void)t; using InvokeResult = decltype(AZStd::apply(fn, t)); AZ_TEST_STATIC_ASSERT((AZStd::is_same::value)); } TEST_F(TupleTest, ReturnType_ApplyTest) { ReturnTypeTest<0, void>(); ReturnTypeTest<1, int>(); ReturnTypeTest<2, int &>(); ReturnTypeTest<3, int const &>(); ReturnTypeTest<4, int volatile &>(); ReturnTypeTest<5, int const volatile &>(); ReturnTypeTest<6, int &&>(); ReturnTypeTest<7, int const &&>(); ReturnTypeTest<8, int volatile &&>(); ReturnTypeTest<9, int const volatile &&>(); ReturnTypeTest<10, int *>(); ReturnTypeTest<11, int const *>(); ReturnTypeTest<12, int volatile *>(); ReturnTypeTest<13, int const volatile *>(); } } namespace ExtendedApplyTest { class ExtendedTupleTest : public AllocatorsFixture { protected: void SetUp() override { AllocatorsFixture::SetUp(); } void TearDown() override { AllocatorsFixture::TearDown(); } static void SetUpTestCase() { ExtendedTupleTest::m_count = 0; } public: static AZ::s32 m_count; }; AZ::s32 ExtendedTupleTest::m_count = 0; struct A_int_0 { A_int_0() : obj1(0) {} A_int_0(AZ::s32 x) : obj1(x) {} AZ::s32 mem1() { return ++ExtendedTupleTest::m_count; } AZ::s32 mem2() const { return ++ExtendedTupleTest::m_count; } const AZ::s32 obj1; A_int_0& operator=(const A_int_0&) = delete; }; struct A_int_1 { A_int_1() {} A_int_1(AZ::s32) {} AZ::s32 mem1(AZ::s32 x) { return ExtendedTupleTest::m_count += x; } AZ::s32 mem2(AZ::s32 x) const { return ExtendedTupleTest::m_count += x; } }; struct A_int_2 { A_int_2() {} A_int_2(AZ::s32) {} AZ::s32 mem1(AZ::s32 x, AZ::s32 y) { return ExtendedTupleTest::m_count += (x + y); } AZ::s32 mem2(AZ::s32 x, AZ::s32 y) const { return ExtendedTupleTest::m_count += (x + y); } }; template struct A_wrap { A_wrap() {} A_wrap(AZ::s32 x) : m_a(x) {} A & operator*() { return m_a; } A const & operator*() const { return m_a; } A m_a; }; using A_wrap_0 = A_wrap; using A_wrap_1 = A_wrap; using A_wrap_2 = A_wrap; template struct A_base : A { A_base() : A() {} A_base(AZ::s32 x) : A(x) {} }; using A_base_0 = A_base; using A_base_1 = A_base; using A_base_2 = A_base; template < class Tuple, class ConstTuple , class TuplePtr, class ConstTuplePtr , class TupleWrap, class ConstTupleWrap , class TupleBase, class ConstTupleBase > void test_ext_int_0() { ExtendedTupleTest::m_count = 0; using T = A_int_0; using Wrap = A_wrap_0; using Base = A_base_0; using mem1_t = AZ::s32(T::*)(); mem1_t mem1 = &T::mem1; using mem2_t = AZ::s32(T::*)() const; mem2_t mem2 = &T::mem2; using obj1_t = AZ::s32 const T::*; obj1_t obj1 = &T::obj1; // member function w/ref { T a; Tuple t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem1, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/pointer { T a; TuplePtr t{ { &a } }; EXPECT_EQ(1, AZStd::apply(mem1, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/base { Base a; TupleBase t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem1, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/wrap { Wrap a; TupleWrap t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem1, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/ref { T const a; ConstTuple t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem2, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/pointer { T const a; ConstTuplePtr t{ { &a } }; EXPECT_EQ(1, AZStd::apply(mem2, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/base { Base const a; ConstTupleBase t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem2, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/wrapper { Wrap const a; ConstTupleWrap t{ { a } }; EXPECT_EQ(1, AZStd::apply(mem2, t)); EXPECT_EQ(1, ExtendedTupleTest::m_count); } // member object w/ref { T a{ 42 }; Tuple t{ { a } }; EXPECT_EQ(42, AZStd::apply(obj1, t)); } // member object w/pointer { T a{ 42 }; TuplePtr t{ { &a } }; EXPECT_EQ(42, AZStd::apply(obj1, t)); } // member object w/base { Base a{ 42 }; TupleBase t{ { a } }; EXPECT_EQ(42, AZStd::apply(obj1, t)); } // member object w/wrapper { Wrap a{ 42 }; TupleWrap t{ { a } }; EXPECT_EQ(42, AZStd::apply(obj1, t)); } } template < class Tuple, class ConstTuple , class TuplePtr, class ConstTuplePtr , class TupleWrap, class ConstTupleWrap , class TupleBase, class ConstTupleBase > void test_ext_int_1() { ExtendedTupleTest::m_count = 0; typedef A_int_1 T; typedef A_wrap_1 Wrap; typedef A_base_1 Base; using mem1_t = AZ::s32(T::*)(AZ::s32); mem1_t mem1 = &T::mem1; using mem2_t = AZ::s32(T::*)(AZ::s32) const; mem2_t mem2 = &T::mem2; // member function w/ref { T a; Tuple t{ a, 2 }; EXPECT_EQ(2, AZStd::apply(mem1, t)); EXPECT_EQ(2, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/pointer { T a; TuplePtr t{ &a, 3 }; EXPECT_EQ(3, AZStd::apply(mem1, t)); EXPECT_EQ(3, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/base { Base a; TupleBase t{ a, 4 }; EXPECT_EQ(4, AZStd::apply(mem1, t)); EXPECT_EQ(4, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/wrap { Wrap a; TupleWrap t{ a, 5 }; EXPECT_EQ(5, AZStd::apply(mem1, t)); EXPECT_EQ(5, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/ref { T const a; ConstTuple t{ a, 6 }; EXPECT_EQ(6, AZStd::apply(mem2, t)); EXPECT_EQ(6, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/pointer { T const a; ConstTuplePtr t{ &a, 7 }; EXPECT_EQ(7, AZStd::apply(mem2, t)); EXPECT_EQ(7, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/base { Base const a; ConstTupleBase t{ a, 8 }; EXPECT_EQ(8, AZStd::apply(mem2, t)); EXPECT_EQ(8, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/wrapper { Wrap const a; ConstTupleWrap t{ a, 9 }; EXPECT_EQ(9, AZStd::apply(mem2, t)); EXPECT_EQ(9, ExtendedTupleTest::m_count); } } template < class Tuple, class ConstTuple , class TuplePtr, class ConstTuplePtr , class TupleWrap, class ConstTupleWrap , class TupleBase, class ConstTupleBase > void test_ext_int_2() { ExtendedTupleTest::m_count = 0; typedef A_int_2 T; typedef A_wrap_2 Wrap; typedef A_base_2 Base; using mem1_t = AZ::s32(T::*)(AZ::s32, AZ::s32); mem1_t mem1 = &T::mem1; using mem2_t = AZ::s32(T::*)(AZ::s32, int) const; mem2_t mem2 = &T::mem2; // member function w/ref { T a; Tuple t{ a, 1, 1 }; EXPECT_EQ(2, AZStd::apply(mem1, t)); EXPECT_EQ(2, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/pointer { T a; TuplePtr t{ &a, 1, 2 }; EXPECT_EQ(3, AZStd::apply(mem1, t)); EXPECT_EQ(3, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/base { Base a; TupleBase t{ a, 2, 2 }; EXPECT_EQ(4, AZStd::apply(mem1, t)); EXPECT_EQ(4, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // member function w/wrap { Wrap a; TupleWrap t{ a, 2, 3 }; EXPECT_EQ(5, AZStd::apply(mem1, t)); EXPECT_EQ(5, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/ref { T const a; ConstTuple t{ a, 3, 3 }; EXPECT_EQ(6, AZStd::apply(mem2, t)); EXPECT_EQ(6, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/pointer { T const a; ConstTuplePtr t{ &a, 3, 4 }; EXPECT_EQ(7, AZStd::apply(mem2, t)); EXPECT_EQ(7, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/base { Base const a; ConstTupleBase t{ a, 4, 4 }; EXPECT_EQ(8, AZStd::apply(mem2, t)); EXPECT_EQ(8, ExtendedTupleTest::m_count); } ExtendedTupleTest::m_count = 0; // const member function w/wrapper { Wrap const a; ConstTupleWrap t{ a, 4, 5 }; EXPECT_EQ(9, AZStd::apply(mem2, t)); EXPECT_EQ(9, ExtendedTupleTest::m_count); } } TEST_F(ExtendedTupleTest, Int0_ExtendedApplyTest) { test_ext_int_0< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); test_ext_int_0< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); test_ext_int_0< AZStd::array, AZStd::array , AZStd::array, AZStd::array , AZStd::array, AZStd::array , AZStd::array, AZStd::array >(); } TEST_F(ExtendedTupleTest, Int1_ExtendedApplyTest) { test_ext_int_1< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); test_ext_int_1< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); test_ext_int_1< AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair >(); test_ext_int_1< AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair , AZStd::pair, AZStd::pair >(); } TEST_F(ExtendedTupleTest, Int2_ExtendedApplyTest) { test_ext_int_2< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); test_ext_int_2< AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple , AZStd::tuple, AZStd::tuple >(); } } }