Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,348 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/allocator.h>
#include <AzCore/std/allocator_static.h>
#include <AzCore/std/allocator_ref.h>
#include <AzCore/std/allocator_stack.h>
#include <AzCore/std/allocator_traits.h>
#include <AzCore/Memory/SystemAllocator.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
/**
* Test for AZSTD provided default allocators.
*/
/// Default allocator.
class AllocatorDefaultTest
: public AllocatorsTestFixture
{
public:
void run()
{
const char name[] = "My test allocator";
AZStd::allocator myalloc(name);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), name) == 0);
const char newName[] = "My new test allocator";
myalloc.set_name(newName);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), newName) == 0);
AZStd::allocator::pointer_type data = myalloc.allocate(100, 1);
AZ_TEST_ASSERT(data != 0);
myalloc.deallocate(data, 100, 1);
data = myalloc.allocate(50, 128);
AZ_TEST_ASSERT(data != 0);
myalloc.deallocate(data, 50, 128);
AZStd::allocator myalloc2;
AZ_TEST_ASSERT(myalloc == myalloc2); // always true
AZ_TEST_ASSERT(!(myalloc != myalloc2)); // always false
}
};
TEST_F(AllocatorDefaultTest, Test)
{
run();
}
TEST_F(AllocatorDefaultTest, AllocatorTraitsExistForAZStdAllocator)
{
using AZStdAllocatorTraits = AZStd::allocator_traits<AZStd::allocator>;
static_assert(AZStd::is_same<AZStdAllocatorTraits::allocator_type, AZStd::allocator>::value, "Allocator trait allocator_type is not the same as AZStd::allocator");
static_assert(AZStd::is_same<AZStdAllocatorTraits::value_type, uint8_t>::value, "Allocator trait value_type is not the same as uint8_t");
static_assert(AZStd::is_same<AZStdAllocatorTraits::pointer, void*>::value, "Allocator trait pointer is not the same as void*");
static_assert(AZStd::is_same<AZStdAllocatorTraits::const_pointer, const uint8_t*>::value, "Allocator trait const_pointer is not the same as const uint8_t*");
static_assert(AZStd::is_same<AZStdAllocatorTraits::void_pointer, void*>::value, "Allocator trait void_pointer is not the same as void*");
static_assert(AZStd::is_same<AZStdAllocatorTraits::const_void_pointer, const void*>::value, "Allocator trait const_void_pointer is not the same as void*");
static_assert(AZStd::is_same<AZStdAllocatorTraits::difference_type, ptrdiff_t>::value, "Allocator trait difference_type is not the same as ptrdiff_t");
static_assert(AZStd::is_same<AZStdAllocatorTraits::size_type, size_t>::value, "Allocator trait size_type is not the same as size_t");
static_assert(AZStd::is_same<AZStdAllocatorTraits::propagate_on_container_copy_assignment, false_type>::value, "Allocator trait propagate_on_container_copy_assignment is not the same as false_type");
static_assert(AZStd::is_same<AZStdAllocatorTraits::propagate_on_container_move_assignment, false_type>::value, "Allocator trait propagate_on_container_move_assignment is not the same as false_type");
static_assert(AZStd::is_same<AZStdAllocatorTraits::propagate_on_container_swap, false_type>::value, "Allocator trait propagate_on_container_swap is not the same as false_type");
static_assert(AZStd::is_same<AZStdAllocatorTraits::is_always_equal, false_type>::value, "Allocator trait is_always_equal is not the same as false_type");
static_assert(AZStd::is_same<typename AZStdAllocatorTraits::template rebind_alloc<int32_t>, AZStd::allocator>::value, "Rebind alloc for AZStd::allocator should return AZStd::allocator");
static_assert(AZStd::is_same<typename AZStdAllocatorTraits::template rebind_traits<int32_t>::allocator_type, AZStd::allocator>::value, "Rebind traits allocator_type should still be AZStd::allocator");
}
TEST_F(AllocatorDefaultTest, AllocatorTraitsAllocateAndDeallocateSucceeds)
{
using AZStdAllocatorTraits = AZStd::allocator_traits<AZStd::allocator>;
AZStd::allocator testAllocator("trait allocator");
typename AZStdAllocatorTraits::pointer data = AZStdAllocatorTraits::allocate(testAllocator, 50, 128);
EXPECT_NE(nullptr, data);
AZStdAllocatorTraits::deallocate(testAllocator, data, 50, 128);
}
TEST_F(AllocatorDefaultTest, AllocatorTraitsConstructAndDestroySucceeds)
{
static int32_t constructedCount;
struct TestAllocated
{
TestAllocated(int32_t value)
: m_value(value)
{
++constructedCount;
}
~TestAllocated()
{
--constructedCount;
}
int32_t m_value{};
};
using AZStdAllocatorTraits = AZStd::allocator_traits<AZStd::allocator>;
AZStd::allocator testAllocator("trait allocator");
typename AZStdAllocatorTraits::pointer data = AZStdAllocatorTraits::allocate(testAllocator, sizeof(TestAllocated), alignof(TestAllocated));
EXPECT_NE(nullptr, data);
auto testPtr = static_cast<TestAllocated*>(data);
AZStdAllocatorTraits::construct(testAllocator, testPtr, 42);
EXPECT_EQ(1, constructedCount);
EXPECT_EQ(42, testPtr->m_value);
AZStdAllocatorTraits::destroy(testAllocator, testPtr);
EXPECT_EQ(0, constructedCount);
AZStdAllocatorTraits::deallocate(testAllocator, data, sizeof(TestAllocated), alignof(TestAllocated));
}
TEST_F(AllocatorDefaultTest, AllocatorTraitsMaxSizeCompilesWithoutErrors)
{
using AZStdAllocatorTraits = AZStd::allocator_traits<AZStd::allocator>;
AZStd::allocator testAllocator("trait allocator");
typename AZStdAllocatorTraits::size_type maxSize = AZStdAllocatorTraits::max_size(testAllocator);
EXPECT_EQ(testAllocator.get_max_size(), maxSize);
}
TEST_F(AllocatorDefaultTest, AllocatorTraitsSelectOnContainerCopyConstructionCompilesWithoutErrors)
{
using AZStdAllocatorTraits = AZStd::allocator_traits<AZStd::allocator>;
AZStd::allocator testAllocator("trait allocator");
AZStd::allocator copiedAllocator = AZStdAllocatorTraits::select_on_container_copy_construction(testAllocator);
EXPECT_EQ(testAllocator, copiedAllocator);
}
/// Static buffer allocator.
TEST(Allocator, StaticBuffer)
{
const int bufferSize = 500;
typedef static_buffer_allocator<bufferSize, 4> buffer_alloc_type;
const char name[] = "My test allocator";
buffer_alloc_type myalloc(name);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), name) == 0);
const char newName[] = "My new test allocator";
myalloc.set_name(newName);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), newName) == 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == AZStd::size_t(bufferSize));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
buffer_alloc_type::pointer_type data = myalloc.allocate(100, 1);
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == bufferSize - 100);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 100);
myalloc.deallocate(data, 100, 1); // we can free the last allocation only
AZ_TEST_ASSERT(myalloc.get_max_size() == bufferSize);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
data = myalloc.allocate(100, 1);
myalloc.allocate(3, 1);
myalloc.deallocate(data); // can't free allocation which is not the last.
AZ_TEST_ASSERT(myalloc.get_max_size() == bufferSize - 103);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 103);
myalloc.reset();
AZ_TEST_ASSERT(myalloc.get_max_size() == AZStd::size_t(bufferSize));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
data = myalloc.allocate(50, 64);
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(((AZStd::size_t)data & 63) == 0);
AZ_TEST_ASSERT(myalloc.get_max_size() <= bufferSize - 50);
AZ_TEST_ASSERT(myalloc.get_allocated_size() >= 50);
buffer_alloc_type myalloc2;
AZ_TEST_ASSERT(myalloc == myalloc);
AZ_TEST_ASSERT((myalloc2 != myalloc));
}
/// Static pool allocator.
TEST(Allocator, StaticPool)
{
const int numNodes = 100;
const char name[] = "My test allocator";
const char newName[] = "My new test allocator";
typedef static_pool_allocator<int, numNodes> int_node_pool_type;
int_node_pool_type myalloc(name);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), name) == 0);
myalloc.set_name(newName);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), newName) == 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == sizeof(int) * numNodes);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
int* data = reinterpret_cast<int*>(myalloc.allocate(sizeof(int), 1));
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == (numNodes - 1) * sizeof(int));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == sizeof(int));
myalloc.deallocate(data, sizeof(int), 1);
AZ_TEST_ASSERT(myalloc.get_max_size() == sizeof(int) * numNodes);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
for (int i = 0; i < numNodes; ++i)
{
data = reinterpret_cast<int*>(myalloc.allocate(sizeof(int), 1));
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == (numNodes - (i + 1)) * sizeof(int));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == (i + 1) * sizeof(int));
}
myalloc.reset();
AZ_TEST_ASSERT(myalloc.get_max_size() == numNodes * sizeof(int));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
AZ_TEST_ASSERT(myalloc == myalloc);
//////////////////////////////////////////////////////////////////////////
// static pool allocator
// Generally we can't use more then 16 byte alignment on the stack.
// Some platforms might fail. Which is ok, higher alignment should be handled by US. Or not on the stack.
const int dataAlignment = 16;
typedef aligned_storage<sizeof(int), dataAlignment>::type aligned_int_type;
typedef static_pool_allocator<aligned_int_type, numNodes> aligned_int_node_pool_type;
aligned_int_node_pool_type myaligned_pool;
aligned_int_type* aligned_data = reinterpret_cast<aligned_int_type*>(myaligned_pool.allocate(sizeof(aligned_int_type), dataAlignment));
AZ_TEST_ASSERT(aligned_data != 0);
AZ_TEST_ASSERT(((AZStd::size_t)aligned_data & (dataAlignment - 1)) == 0);
AZ_TEST_ASSERT(myaligned_pool.get_max_size() == (numNodes - 1) * sizeof(aligned_int_type));
AZ_TEST_ASSERT(myaligned_pool.get_allocated_size() == sizeof(aligned_int_type));
myaligned_pool.deallocate(aligned_data, sizeof(aligned_int_type), dataAlignment); // Make sure we free what we have allocated.
//////////////////////////////////////////////////////////////////////////
}
/// Reference allocator.
TEST(Allocator, Reference)
{
const int bufferSize = 500;
typedef static_buffer_allocator<bufferSize, 4> buffer_alloc_type;
buffer_alloc_type shared_allocator("Shared allocator");
typedef allocator_ref<buffer_alloc_type> ref_allocator_type;
const char name1[] = "Ref allocator1";
ref_allocator_type ref_allocator1(shared_allocator, name1);
const char name2[] = "Ref allocator2";
ref_allocator_type ref_allocator2(shared_allocator, name2);
AZ_TEST_ASSERT(strcmp(ref_allocator1.get_name(), name1) == 0);
AZ_TEST_ASSERT(strcmp(ref_allocator2.get_name(), name2) == 0);
const char newName1[] = "Ref new allocator1";
ref_allocator1.set_name(newName1);
AZ_TEST_ASSERT(strcmp(ref_allocator1.get_name(), newName1) == 0);
const char newName2[] = "Ref new allocator2";
ref_allocator2.set_name(newName2);
AZ_TEST_ASSERT(strcmp(ref_allocator2.get_name(), newName2) == 0);
AZ_TEST_ASSERT(ref_allocator2.get_allocator() == ref_allocator1.get_allocator());
ref_allocator_type::pointer_type data1 = ref_allocator1.allocate(10, 1);
AZ_TEST_ASSERT(data1 != 0);
AZ_TEST_ASSERT(ref_allocator1.get_max_size() == bufferSize - 10);
AZ_TEST_ASSERT(ref_allocator1.get_allocated_size() == 10);
AZ_TEST_ASSERT(shared_allocator.get_max_size() == bufferSize - 10);
AZ_TEST_ASSERT(shared_allocator.get_allocated_size() == 10);
ref_allocator_type::pointer_type data2 = ref_allocator2.allocate(10, 1);
AZ_TEST_ASSERT(data2 != 0);
AZ_TEST_ASSERT(ref_allocator2.get_max_size() <= bufferSize - 20);
AZ_TEST_ASSERT(ref_allocator2.get_allocated_size() >= 20);
AZ_TEST_ASSERT(shared_allocator.get_max_size() <= bufferSize - 20);
AZ_TEST_ASSERT(shared_allocator.get_allocated_size() >= 20);
shared_allocator.reset();
data1 = ref_allocator1.allocate(10, 32);
AZ_TEST_ASSERT(data1 != 0);
AZ_TEST_ASSERT(ref_allocator1.get_max_size() <= bufferSize - 10);
AZ_TEST_ASSERT(ref_allocator1.get_allocated_size() >= 10);
AZ_TEST_ASSERT(shared_allocator.get_max_size() <= bufferSize - 10);
AZ_TEST_ASSERT(shared_allocator.get_allocated_size() >= 10);
data2 = ref_allocator2.allocate(10, 32);
AZ_TEST_ASSERT(data2 != 0);
AZ_TEST_ASSERT(ref_allocator1.get_max_size() <= bufferSize - 20);
AZ_TEST_ASSERT(ref_allocator1.get_allocated_size() >= 20);
AZ_TEST_ASSERT(shared_allocator.get_max_size() <= bufferSize - 20);
AZ_TEST_ASSERT(shared_allocator.get_allocated_size() >= 20);
AZ_TEST_ASSERT(ref_allocator1 == ref_allocator2);
AZ_TEST_ASSERT(!(ref_allocator1 != ref_allocator2));
}
/// Stack buffer allocator.
TEST(Allocator, Stack)
{
size_t bufferSize = 500;
const char name[] = "My test allocator";
stack_allocator myalloc(alloca(bufferSize), bufferSize, name);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), name) == 0);
const char newName[] = "My new test allocator";
myalloc.set_name(newName);
AZ_TEST_ASSERT(strcmp(myalloc.get_name(), newName) == 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == AZStd::size_t(bufferSize));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
stack_allocator::pointer_type data = myalloc.allocate(100, 1);
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(myalloc.get_max_size() == bufferSize - 100);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 100);
myalloc.deallocate(data, 100, 1); // this allocator doesn't free data
AZ_TEST_ASSERT(myalloc.get_max_size() == bufferSize - 100);
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 100);
myalloc.reset();
AZ_TEST_ASSERT(myalloc.get_max_size() == AZStd::size_t(bufferSize));
AZ_TEST_ASSERT(myalloc.get_allocated_size() == 0);
data = myalloc.allocate(50, 64);
AZ_TEST_ASSERT(data != 0);
AZ_TEST_ASSERT(((AZStd::size_t)data & 63) == 0);
AZ_TEST_ASSERT(myalloc.get_max_size() <= bufferSize - 50);
AZ_TEST_ASSERT(myalloc.get_allocated_size() >= 50);
AZ_STACK_ALLOCATOR(myalloc2, 200); // test the macro declaration
AZ_TEST_ASSERT(myalloc2.get_max_size() == 200);
AZ_TEST_ASSERT(myalloc == myalloc);
AZ_TEST_ASSERT((myalloc2 != myalloc));
}
}
+940
View File
@@ -0,0 +1,940 @@
/*
* 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.
*
*/
// Written with help from libcxx's any tests
// https://github.com/llvm-mirror/libcxx/tree/7175a079211ec78c8232d9d55fa4c1f9eeae803d/test/std/experimental/any
#include <AzCore/std/any.h>
#include "UserTypes.h"
using AZStd::any;
using AZStd::any_cast;
using AZStd::any_numeric_cast;
namespace UnitTest
{
using Small0 = CreationCounter<8, 0>;
using Small1 = CreationCounter<8, 1>;
using Large0 = CreationCounter<256, 0>;
using Large1 = CreationCounter<256, 1>;
using Align0 = CreationCounter<8, 0, 64>;
using Align1 = CreationCounter<8, 1, 64>;
}
// Specialize for the type's we're using to get comprehensible output
namespace testing
{
namespace internal
{
#define DEF_GTN_SINGLE(Struct) template<> std::string GetTypeName<::UnitTest::Struct>() { return #Struct; }
#define DEF_GTN_PAIR(Struct1, Struct2) template<> std::string GetTypeName<AZStd::pair<::UnitTest::Struct1, ::UnitTest::Struct2>>() { return #Struct1 ", " #Struct2; }
DEF_GTN_SINGLE(Small0);
DEF_GTN_SINGLE(Large0);
DEF_GTN_SINGLE(Align0);
DEF_GTN_PAIR(Small0, Small1);
DEF_GTN_PAIR(Large0, Large1);
DEF_GTN_PAIR(Align0, Align1);
DEF_GTN_PAIR(Small0, Large0);
DEF_GTN_PAIR(Small0, Align0);
DEF_GTN_PAIR(Large0, Small0);
DEF_GTN_PAIR(Large0, Align0);
DEF_GTN_PAIR(Align0, Small0);
DEF_GTN_PAIR(Align0, Large0);
#undef DEF_GTN_SINGLE
#undef DEF_GTN_PAIR
}
}
namespace UnitTest
{
//////////////////////////////////////////////////////////////////////////
// Fixtures
// Fixture for non-typed tests
class AnyTest
: public AllocatorsFixture
{ };
// Fixture for tests with 1 type
template<typename TestStruct>
class AnySizedTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
TestStruct::Reset();
}
};
using AnySizedTestTypes = ::testing::Types<Small0, Large0, Align0>;
TYPED_TEST_CASE(AnySizedTest, AnySizedTestTypes);
// Fixture for tests with 2 types (for converting between types)
template <typename StructPair>
class AnyConversionTest
: public AllocatorsFixture
{
public:
using LHS = typename StructPair::first_type;
using RHS = typename StructPair::second_type;
void SetUp() override
{
AllocatorsFixture::SetUp();
LHS::Reset();
RHS::Reset();
}
};
using AnyConversionTestTypes = ::testing::Types <
AZStd::pair<Small0, Small1>, // Small -> Small
AZStd::pair<Large0, Large1>, // Large -> Large
AZStd::pair<Align0, Align1>, // Align -> Align
AZStd::pair<Small0, Large0>, // Small -> Large
AZStd::pair<Small0, Align0>, // Small -> Align
AZStd::pair<Large0, Small0>, // Large -> Small
AZStd::pair<Large0, Align0>, // Large -> Align
AZStd::pair<Align0, Small0>, // Align -> Small
AZStd::pair<Align0, Large0> // Align -> Large
>;
TYPED_TEST_CASE(AnyConversionTest, AnyConversionTestTypes);
//////////////////////////////////////////////////////////////////////////
// Tests for constructors
namespace Constructor
{
// Construct empty
TEST_F(AnyTest, Any_EmptyConstruct_IsEmpty)
{
const any a;
ASSERT_TRUE(a.empty());
}
// Construct via copy value
TYPED_TEST(AnySizedTest, Any_ConstructFromData_Copy)
{
{
TypeParam t;
EXPECT_EQ(TypeParam::s_count, 1);
any any1(t);
EXPECT_EQ(TypeParam::s_count, 2);
EXPECT_EQ(TypeParam::s_copied, 1);
EXPECT_EQ(TypeParam::s_moved, 0);
}
}
// Construct via move value
TYPED_TEST(AnySizedTest, Any_ConstructFromData_Move)
{
{
TypeParam t;
EXPECT_EQ(TypeParam::s_count, 1);
any any1(AZStd::move(t));
EXPECT_EQ(TypeParam::s_count, 2);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_moved, 1);
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Copy empty
TEST_F(AnyTest, Any_CopyConstructEmpty_IsEmpty)
{
const any any1;
const any any2(any1);
EXPECT_TRUE(any1.empty());
EXPECT_TRUE(any2.empty());
}
// Copy with data
TYPED_TEST(AnySizedTest, Any_CopyConstructValid_IsValid)
{
any any1(TypeParam(36));
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_moved, 1);
EXPECT_EQ(any_cast<TypeParam&>(any1).val(), 36);
any any2(any1);
EXPECT_EQ(TypeParam::s_copied, 1);
EXPECT_EQ(TypeParam::s_count, 2);
EXPECT_EQ(TypeParam::s_moved, 1);
// Modify a and check that any2 is unchanged
any_cast<TypeParam&>(any1).val() = -1;
EXPECT_EQ(any_cast<TypeParam&>(any1).val(), -1);
EXPECT_EQ(any_cast<TypeParam&>(any2).val(), 36);
// modify any2 and check that any1 is unchanged
any_cast<TypeParam&>(any2).val() = 75;
EXPECT_EQ(any_cast<TypeParam&>(any1).val(), -1);
EXPECT_EQ(any_cast<TypeParam&>(any2).val(), 75);
// clear a and check that a2 is unchanged
any1.clear();
EXPECT_TRUE(any1.empty());
EXPECT_EQ(any_cast<TypeParam&>(any2).val(), 75);
}
// Move empty
TEST_F(AnyTest, Any_MoveConstructEmpty_IsEmpty)
{
const any any1;
const any any2(AZStd::move(any1));
EXPECT_TRUE(any1.empty());
EXPECT_TRUE(any2.empty());
}
// Move with data
TYPED_TEST(AnySizedTest, Any_MoveConstructValid_IsValid)
{
any any1(TypeParam(42));
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_moved, 1);
any any2(std::move(any1));
EXPECT_GT(TypeParam::s_moved, 1); // zero or more move operations can be performed.
EXPECT_EQ(TypeParam::s_copied, 0); // no copies can be performed.
EXPECT_EQ(TypeParam::s_count, 1); // Only one instance may remain.
EXPECT_TRUE(any1.empty()); // Moves are always destructive.
EXPECT_EQ(any_cast<TypeParam&>(any2).val(), 42);
}
// Forward with data
TYPED_TEST(AnySizedTest, Any_ForwardConstructValid_IsValid)
{
any any1(AZStd::in_place_type_t<TypeParam>(), 55);
EXPECT_EQ(1, TypeParam::s_count);
EXPECT_EQ(0, TypeParam::s_copied);
EXPECT_EQ(0, TypeParam::s_moved);
any any2(AZStd::in_place_type_t<TypeParam>(), AZStd::initializer_list<int>{ 55 });
EXPECT_EQ(2, TypeParam::s_count);
EXPECT_EQ(0, TypeParam::s_moved);
EXPECT_EQ(0, TypeParam::s_copied);
EXPECT_EQ(any_cast<TypeParam&>(any1).val(), any_cast<TypeParam&>(any2).val());
}
// make_any helper forward functions
TYPED_TEST(AnySizedTest, Any_MakeAnyForwarder_IsValid)
{
any any1 = AZStd::make_any<TypeParam>(24);
EXPECT_EQ(1, TypeParam::s_count);
EXPECT_EQ(0, TypeParam::s_copied);
EXPECT_EQ(0, TypeParam::s_moved);
any any2 = AZStd::make_any<TypeParam, int>({ 24 });
EXPECT_EQ(2, TypeParam::s_count);
EXPECT_EQ(0, TypeParam::s_moved);
EXPECT_EQ(0, TypeParam::s_copied);
EXPECT_EQ(any_cast<TypeParam&>(any1).val(), any_cast<TypeParam&>(any2).val());
}
template <typename ValueType>
void InplaceAnyTypeInfo(AZStd::any::Action action, any* dest, const any*)
{
switch (action)
{
case any::Action::Reserve:
// If doing small buffer optimization, no need to validate anything
if (dest->get_type_info().m_useHeap)
{
// Allocate space for object on heap
AZStd::allocator systemAllocator;
*reinterpret_cast<void**>(dest) = systemAllocator.allocate(sizeof(ValueType), alignof(ValueType));
}
break;
case any::Action::Construct:
break;
case any::Action::Copy:
case any::Action::Move:
ADD_FAILURE() << "Copy/Move Constructor should not be getting invoke in the InPlace test";
break;
case any::Action::Destruct:
break;
case any::Action::Destroy:
// Call the destructor
reinterpret_cast<ValueType*>(AZStd::any_cast<void>(dest))->~ValueType();
// Clear memory
if (dest->get_type_info().m_useHeap)
{
AZStd::allocator systemAllocator;
systemAllocator.deallocate(AZStd::any_cast<void>(dest), sizeof(ValueType), alignof(ValueType));
}
break;
default:
ADD_FAILURE() << "Default case should never get invoked";
}
}
TEST_F(AnyTest, Any_CustomTypeInfoConstructorWithInplace_IsValid)
{
using VectorType = AZStd::vector<int>;
AZStd::any::type_info vectorTypeInfo;
vectorTypeInfo.m_id = azrtti_typeid<VectorType>();
vectorTypeInfo.m_isPointer = false;
vectorTypeInfo.m_useHeap = sizeof(VectorType) > AZStd::Internal::ANY_SBO_BUF_SIZE;
vectorTypeInfo.m_handler = &InplaceAnyTypeInfo<VectorType>;
any vectorAny(vectorTypeInfo, AZStd::in_place_type_t<VectorType>{}, 3, 17);
EXPECT_TRUE(vectorAny.is<VectorType>());
VectorType& vectorRef = AZStd::any_cast<VectorType&>(vectorAny);
EXPECT_EQ(3, vectorRef.size());
EXPECT_EQ(17, vectorRef[0]);
EXPECT_EQ(17, vectorRef[1]);
EXPECT_EQ(17, vectorRef[2]);
}
TEST_F(AnyTest, Any_CustomTypeInfoConstructorWithInitializerListAndInplace_IsValid)
{
using VectorType = AZStd::vector<int>;
AZStd::any::type_info vectorTypeInfo;
vectorTypeInfo.m_id = azrtti_typeid<VectorType>();
vectorTypeInfo.m_isPointer = false;
vectorTypeInfo.m_useHeap = sizeof(VectorType) > AZStd::Internal::ANY_SBO_BUF_SIZE;
vectorTypeInfo.m_handler = &InplaceAnyTypeInfo<VectorType>;
any vectorAny(vectorTypeInfo, AZStd::in_place_type_t<VectorType>{}, { 1, 2, 3, 4 });
EXPECT_TRUE(vectorAny.is<VectorType>());
VectorType& vectorRef = AZStd::any_cast<VectorType&>(vectorAny);
EXPECT_EQ(4, vectorRef.size());
EXPECT_EQ(1, vectorRef[0]);
EXPECT_EQ(2, vectorRef[1]);
EXPECT_EQ(3, vectorRef[2]);
EXPECT_EQ(4, vectorRef[3]);
}
}
//////////////////////////////////////////////////////////////////////////
// Tests for assignment operator
namespace Assignment
{
// Test copy assign other any
TYPED_TEST(AnyConversionTest, Any_CopyAssignAny_IsValid)
{
using LHS = typename TestFixture::LHS;
using RHS = typename TestFixture::RHS;
{
any lhs(LHS(1));
any const rhs(RHS(2));
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 1);
EXPECT_EQ(RHS::s_copied, 0);
lhs = rhs;
EXPECT_EQ(RHS::s_copied, 1);
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 2);
EXPECT_EQ(any_cast<const RHS&>(lhs).val(), 2);
EXPECT_EQ(any_cast<const RHS&>(rhs).val(), 2);
}
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 0);
}
// Test move assign other any
TYPED_TEST(AnyConversionTest, Any_MoveAssignAny_IsValid)
{
using LHS = typename TestFixture::LHS;
using RHS = typename TestFixture::RHS;
{
any lhs(LHS(1));
any rhs(RHS(2));
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 1);
EXPECT_EQ(RHS::s_moved, 1);
lhs = AZStd::move(rhs);
EXPECT_GT(RHS::s_moved, 1);
EXPECT_EQ(RHS::s_copied, 0);
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 1);
EXPECT_EQ(any_cast<RHS&>(lhs).val(), 2);
EXPECT_TRUE(rhs.empty());
}
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 0);
}
// Test copy assign other any
TYPED_TEST(AnySizedTest, Any_CopyAssignValue_IsValid)
{
{
any lhs;
TypeParam rhs(42);
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
lhs = rhs;
EXPECT_EQ(TypeParam::s_count, 2);
EXPECT_EQ(TypeParam::s_copied, 1);
EXPECT_GT(TypeParam::s_moved, 0);
EXPECT_EQ(any_cast<const TypeParam&>(lhs).val(), 42);
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test move assign other any
TYPED_TEST(AnySizedTest, Any_MoveAssignValue_IsValid)
{
{
any lhs;
TypeParam rhs(42);
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_moved, 0);
lhs = AZStd::move(rhs);
EXPECT_EQ(TypeParam::s_count, 2);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_GT(TypeParam::s_moved, 1);
EXPECT_EQ(any_cast<TypeParam&>(lhs).val(), 42);
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test copy assign other any, and that old value is destroyed
TYPED_TEST(AnyConversionTest, Any_CopyAssignAny_OldIsDestroyed)
{
using LHS = typename TestFixture::LHS;
using RHS = typename TestFixture::RHS;
{
any lhs(LHS(1));
any const rhs(RHS(2));
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 1);
EXPECT_EQ(RHS::s_copied, 0);
lhs = rhs;
EXPECT_EQ(RHS::s_copied, 1);
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 2);
EXPECT_EQ(any_cast<const RHS&>(lhs).val(), 2);
EXPECT_EQ(any_cast<const RHS&>(rhs).val(), 2);
}
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 0);
}
// Test copy assign empty any, and that old value is destroyed
TYPED_TEST(AnySizedTest, Any_CopyAssignEmptyAny_OldIsDestroyed)
{
{
any lhs(TypeParam(1));
any const rhs;
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
lhs = rhs;
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_count, 0);
EXPECT_TRUE(lhs.empty());
EXPECT_TRUE(rhs.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test copy assign empty self any
TEST_F(AnyTest, Any_CopyAssignSelfEmpty_IsEmpty)
{
any a;
a = a;
EXPECT_TRUE(a.empty());
}
// Test copy assign self any
TYPED_TEST(AnySizedTest, Any_CopyAssignSelf_IsNoop)
{
{
any a((TypeParam(1)));
EXPECT_EQ(TypeParam::s_count, 1);
a = a;
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(any_cast<const TypeParam&>(a).val(), 1);
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test move assign other any, and that the old value is destroyed
TYPED_TEST(AnyConversionTest, Any_MoveAssignAny_OldIsDestroyed)
{
using LHS = typename TestFixture::LHS;
using RHS = typename TestFixture::RHS;
{
LHS const s1(1);
any a(s1);
RHS const s2(2);
any a2(s2);
EXPECT_EQ(LHS::s_count, 2);
EXPECT_EQ(RHS::s_count, 2);
a = AZStd::move(a2);
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 2);
EXPECT_EQ(any_cast<const RHS&>(a).val(), 2);
EXPECT_TRUE(a2.empty());
}
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 0);
}
// Test move assign other any over empty
TYPED_TEST(AnySizedTest, Any_MoveAssignAny_IsValid)
{
{
any a;
any a2((TypeParam(1)));
EXPECT_EQ(TypeParam::s_count, 1);
a = std::move(a2);
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(any_cast<const TypeParam&>(a).val(), 1);
EXPECT_TRUE(a2.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test move assign other empty any
TYPED_TEST(AnySizedTest, Any_MoveAssignEmptyAny_OldIsDestroyed)
{
{
any a((TypeParam(1)));
any a2;
EXPECT_EQ(TypeParam::s_count, 1);
a = std::move(a2);
EXPECT_EQ(TypeParam::s_count, 0);
EXPECT_TRUE(a.empty());
EXPECT_TRUE(a2.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
}
namespace Modifiers
{
// Test clear empty any
TEST_F(AnyTest, Any_ClearEmptyAny_IsEmpty)
{
any a;
a.clear();
EXPECT_TRUE(a.empty());
}
// Test clear valid any
TYPED_TEST(AnySizedTest, Any_ClearValidAny_OldIsDestroyed)
{
{
any a((TypeParam(1)));
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(any_cast<const TypeParam&>(a).val(), 1);
a.clear();
EXPECT_TRUE(a.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test swap 2 valid anys
TYPED_TEST(AnyConversionTest, Any_SwapValidAnys_IsValid)
{
using LHS = typename TestFixture::LHS;
using RHS = typename TestFixture::RHS;
{
any a1((LHS(1)));
any a2(RHS(2));
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 1);
a1.swap(a2);
EXPECT_EQ(LHS::s_count, 1);
EXPECT_EQ(RHS::s_count, 1);
EXPECT_EQ(any_cast<const RHS&>(a1).val(), 2);
EXPECT_EQ(any_cast<const RHS&>(a2).val(), 1);
}
EXPECT_EQ(LHS::s_count, 0);
EXPECT_EQ(RHS::s_count, 0);
}
// Test swap valid any with empty any
TYPED_TEST(AnySizedTest, Any_SwapEmptyAndValidAny_IsValid)
{
{
any a1((TypeParam(1)));
any a2;
EXPECT_EQ(TypeParam::s_count, 1);
a1.swap(a2);
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(any_cast<const TypeParam&>(a2).val(), 1);
EXPECT_TRUE(a1.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
// Test swap empty any with valid any
TYPED_TEST(AnySizedTest, Any_SwapValidAndEmptyAny_IsValid)
{
{
any a1((TypeParam(1)));
any a2;
EXPECT_EQ(TypeParam::s_count, 1);
a2.swap(a1);
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(any_cast<const TypeParam&>(a2).val(), 1);
EXPECT_TRUE(a1.empty());
}
EXPECT_EQ(TypeParam::s_count, 0);
}
}
namespace Observers
{
// Test empty any is empty
TEST_F(AnyTest, Any_EmptyAny_IsEmpty)
{
any a;
EXPECT_TRUE(a.empty());
}
// Test valid any is not empty
TYPED_TEST(AnySizedTest, Any_ValidAny_NotIsEmpty)
{
TypeParam const s(1);
any a(s);
EXPECT_FALSE(a.empty());
}
// Test empty any is empty
TEST_F(AnyTest, Any_EmptyAny_IsTypeEmpty)
{
any const a;
EXPECT_TRUE(a.type().IsNull());
}
// Test empty any is empty
TYPED_TEST(AnySizedTest, Any_ValidAny_IsTypeValid)
{
TypeParam const s(1);
any const a(s);
EXPECT_EQ(a.type(), azrtti_typeid<TypeParam>());
}
namespace NonMembers
{
// Test swapping anys works
TEST_F(AnyTest, Any_AzstdSwapAnys_IsValid)
{
any a1(1);
any a2(2);
AZStd::swap(a1, a2);
EXPECT_EQ(any_cast<int>(a1), 2);
EXPECT_EQ(any_cast<int>(a2), 1);
}
namespace AnyCast
{
// Test return types match
TEST_F(AnyTest, Any_PointerAnyCast_IsReturnTypeValid)
{
any a;
static_assert((AZStd::is_same<decltype(any_cast<int>(&a)), int*>::value), "Return type mismatch");
static_assert((AZStd::is_same<decltype(any_cast<int const>(&a)), int const*>::value), "Return type mismatch");
any const& ca = a;
(void)ca;
static_assert((AZStd::is_same<decltype(any_cast<int>(&ca)), int const*>::value), "Return type mismatch");
static_assert((AZStd::is_same<decltype(any_cast<int const>(&ca)), int const*>::value), "Return type mismatch");
}
// Test any_cast<...>(nullptr) always returns nullptr
TEST_F(AnyTest, Any_PointerAnyCastNullptr_ReturnsNullptr)
{
any* a = nullptr;
EXPECT_EQ(nullptr, any_cast<int>(a));
EXPECT_EQ(nullptr, any_cast<int const>(a));
any const* ca = nullptr;
EXPECT_EQ(nullptr, any_cast<int>(ca));
EXPECT_EQ(nullptr, any_cast<int const>(ca));
}
// Test any_cast(&emptyAny) always returns nullptr
TEST_F(AnyTest, Any_PointerAnyCastEmptyAny_ReturnsNullptr)
{
{
any a;
EXPECT_EQ(nullptr, any_cast<int>(&a));
EXPECT_EQ(nullptr, any_cast<int const>(&a));
any const& ca = a;
EXPECT_EQ(nullptr, any_cast<int>(&ca));
EXPECT_EQ(nullptr, any_cast<int const>(&ca));
}
// Create as non-empty, then make empty and run test.
{
any a(42);
a.clear();
EXPECT_EQ(nullptr, any_cast<int>(&a));
EXPECT_EQ(nullptr, any_cast<int const>(&a));
any const& ca = a;
EXPECT_EQ(nullptr, any_cast<int>(&ca));
EXPECT_EQ(nullptr, any_cast<int const>(&ca));
}
}
// Test any_cast(&validAny) always returns proper value
TYPED_TEST(AnySizedTest, Any_PointerAnyCastValidAny_ReturnsValue)
{
{
any a((TypeParam(42)));
any const& ca = a;
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_moved, 1);
// Try a cast to a bad type.
// NOTE: Type cannot be an int.
EXPECT_EQ(any_cast<int>(&a), nullptr);
EXPECT_EQ(any_cast<int const>(&a), nullptr);
// Try a cast to the right type, but as a pointer.
EXPECT_EQ(any_cast<TypeParam*>(&a), nullptr);
EXPECT_EQ(any_cast<TypeParam const*>(&a), nullptr);
// Check getting a unqualified type from a non-const any.
TypeParam* v = any_cast<TypeParam>(&a);
EXPECT_NE(v, nullptr);
EXPECT_EQ(v->val(), 42);
// change the stored value and later check for the new value.
v->val() = 999;
// Check getting a const qualified type from a non-const any.
TypeParam const* cv = any_cast<TypeParam const>(&a);
EXPECT_NE(cv, nullptr);
EXPECT_EQ(cv, v);
EXPECT_EQ(cv->val(), 999);
// Check getting a unqualified type from a const any.
cv = any_cast<TypeParam>(&ca);
EXPECT_NE(cv, nullptr);
EXPECT_EQ(cv, v);
EXPECT_EQ(cv->val(), 999);
// Check getting a const-qualified type from a const any.
cv = any_cast<TypeParam const>(&ca);
EXPECT_NE(cv, nullptr);
EXPECT_EQ(cv, v);
EXPECT_EQ(cv->val(), 999);
// Check that no more objects were created, copied or moved.
EXPECT_EQ(TypeParam::s_count, 1);
EXPECT_EQ(TypeParam::s_copied, 0);
EXPECT_EQ(TypeParam::s_moved, 1);
}
EXPECT_EQ(TypeParam::s_count, 0);
}
}
namespace AnyNumericCast
{
TEST_F(AnyTest, AnyNumericCast_Nullptr_ReturnsNullptr)
{
{
any* a = nullptr;
int i;
EXPECT_FALSE(any_numeric_cast(a, i));
}
{
const any* a = nullptr;
int i;
EXPECT_FALSE(any_numeric_cast(a, i));
}
}
#define EXPECT_ANY_IS(any_ptr, Type, value) do { Type _r; EXPECT_TRUE(any_numeric_cast<Type>(any_ptr, _r)); EXPECT_EQ(value, _r); } while(false)
TEST_F(AnyTest, AnyNumericCast_SameType_ReturnsValid)
{
any a;
a = 10.0f;
EXPECT_ANY_IS(&a, float, 10.0f);
a = 10.0;
EXPECT_ANY_IS(&a, double, 10.0);
a = int(10);
EXPECT_ANY_IS(&a, int, 10);
}
TEST_F(AnyTest, AnyNumericCast_FloatingPoint_ConversionsWork)
{
any a;
a = 10.0f;
EXPECT_ANY_IS(&a, float, 10.0f);
EXPECT_ANY_IS(&a, double, 10.0);
}
TEST_F(AnyTest, AnyNumericCast_Integral_ConversionsWork)
{
any a;
a = int(10);
EXPECT_ANY_IS(&a, int, 10);
EXPECT_ANY_IS(&a, long, 10);
EXPECT_ANY_IS(&a, char, 10);
EXPECT_ANY_IS(&a, unsigned int, 10u);
EXPECT_ANY_IS(&a, unsigned long, 10u);
EXPECT_ANY_IS(&a, unsigned char, 10u);
}
TEST_F(AnyTest, AnyNumericCast_Integral_AssertsOnDataLoss)
{
any a;
{
// Test assert on signed -> unsigned conversion
a = -1;
AZ_TEST_START_TRACE_SUPPRESSION;
unsigned int v;
any_numeric_cast(&a, v);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
{
// Test assert on out of range
a = std::numeric_limits<int>::max();
AZ_TEST_START_TRACE_SUPPRESSION;
char v;
any_numeric_cast(&a, v);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
}
TEST_F(AnyTest, AnyNumericCast_IntegralToFloatingPoint_ConversionsWork)
{
any a;
a = int(10);
EXPECT_ANY_IS(&a, int, 10);
EXPECT_ANY_IS(&a, float, 10.0f);
EXPECT_ANY_IS(&a, double, 10.0);
}
TEST_F(AnyTest, AnyNumericCast_FloatingPointToIntegral_ConversionsWork)
{
any a;
a = 10.0f;
EXPECT_ANY_IS(&a, float, 10.0f);
EXPECT_ANY_IS(&a, int, 10);
EXPECT_ANY_IS(&a, long, 10);
EXPECT_ANY_IS(&a, char, 10);
EXPECT_ANY_IS(&a, unsigned int, 10u);
EXPECT_ANY_IS(&a, unsigned long, 10u);
EXPECT_ANY_IS(&a, unsigned char, 10u);
}
TEST_F(AnyTest, AnyNumericCast_FloatingPointToIntegral_AssertsOnDataLoss)
{
any a;
// Test assert on out of range
a = DBL_MAX;
AZ_TEST_START_TRACE_SUPPRESSION;
float f;
any_numeric_cast(&a, f);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
#undef EXPECT_ANY_IS
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,482 @@
/*
* 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 <AzCore/std/containers/bitset.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <type_traits>
#include <bitset> // Used for comparison tests.
#include "UserTypes.h"
namespace UnitTest
{
// Or the bitset can be tested without respect to the unsigned long, but benefit from having multiple test cases exercised through the parameterized test
class BitsetUnsignedLongTests
: public ::testing::WithParamInterface <unsigned long>
, public UnitTest::ScopedAllocatorSetupFixture
{
protected:
void SetUp() override
{
m_unsignedLong = GetParam();
m_bitset = AZStd::bitset<32>(m_unsignedLong);
}
// The ground truth
AZ::u32 m_unsignedLong;
// The unit under test
AZStd::bitset<32> m_bitset;
};
TEST_P(BitsetUnsignedLongTests, UnsignedLongConstructor_MatchesUnsignedLong)
{
// Bit by bit comparison
unsigned long currentBit = 1;
for (size_t i = 0; i < m_bitset.size(); ++i)
{
// Expect each bit of the bitset which was constructed during the test fixture SetUp to match the bits in the unsigned long
EXPECT_EQ(m_bitset[i], (m_unsignedLong & currentBit) != 0) << "The bit at index " << i << " did not match the corresponding bit in the unsigned long";
currentBit <<= 1;
}
}
TEST_P(BitsetUnsignedLongTests, ToUlong_MatchesUnsignedLong)
{
EXPECT_EQ(m_bitset.to_ulong(), m_unsignedLong);
}
TEST_P(BitsetUnsignedLongTests, BitwiseNot_MatchesUnsignedLong)
{
m_bitset = ~m_bitset;
EXPECT_EQ(m_bitset.to_ulong(), ~m_unsignedLong);
}
TEST_P(BitsetUnsignedLongTests, Reset_EachBitReset_EachBitIsFalse)
{
// Iterate over the entire bitset and reset each bit individually
for (size_t i = 0; i < m_bitset.size(); ++i)
{
m_bitset.reset(i);
EXPECT_FALSE(m_bitset[i]) << "The bit at index " << i << " was not reset to 0";
}
}
TEST_P(BitsetUnsignedLongTests, Flip_EachBitFlipped_MatchesFlippedUnsignedLong)
{
// Iterate over the entire bitset and flip each bit individually
for (size_t i = 0; i < m_bitset.size(); ++i)
{
m_bitset.flip(i);
}
// Compare to the flipped unsigned long
EXPECT_EQ(m_bitset.to_ulong(), ~m_unsignedLong);
}
TEST_P(BitsetUnsignedLongTests, Reference_EachBitFlipped_MatchesFlippedUnsignedLong)
{
// Iterate over the entire bitset and flip each bit individually via a bitset reference
for (size_t i = 0; i < m_bitset.size(); ++i)
{
AZStd::bitset<32>::reference ref = m_bitset[i];
ref.flip();
}
// Compare to the flipped unsigned long
EXPECT_EQ(m_bitset.to_ulong(), ~m_unsignedLong);
}
TEST_P(BitsetUnsignedLongTests, Reference_BitwiseNotForEachBit_EachBitDoesNotEqualOriginalValue)
{
for (size_t i = 0; i < m_bitset.size(); ++i)
{
// Get a reference to a bit
AZStd::bitset<32>::reference ref = m_bitset[i];
// Compare the reference to the original value of the bit
EXPECT_NE(m_bitset[i], ~ref) << "The ~ operator did not negate the reference to the bitset at index " << i << ".";
}
}
// This fixture initializes two bitsets from two unsigned longs
// Bitwise operations can be performed between the bitsets, then compared to the same bitwise operations preformed between the unsigned longs
class BitsetUnsignedLongPairTests
: public ::testing::WithParamInterface < AZStd::pair<unsigned long, unsigned long> >
, public UnitTest::ScopedAllocatorSetupFixture
{
protected:
void SetUp() override
{
m_unsignedLong1 = GetParam().first;
m_bitset1 = AZStd::bitset<32>(m_unsignedLong1);
m_unsignedLong2 = GetParam().second;
m_bitset2 = AZStd::bitset<32>(m_unsignedLong2);
}
unsigned long m_unsignedLong1;
unsigned long m_unsignedLong2;
AZStd::bitset<32> m_bitset1;
AZStd::bitset<32> m_bitset2;
};
TEST_P(BitsetUnsignedLongPairTests, BitwiseANDOperator_MatchesUnsignedLongAND)
{
EXPECT_EQ((m_bitset1 & m_bitset2).to_ulong(), m_unsignedLong1 & m_unsignedLong2);
}
TEST_P(BitsetUnsignedLongPairTests, BitwiseOROperator_MatchesUnsignedLongOR)
{
EXPECT_EQ((m_bitset1 | m_bitset2).to_ulong(), m_unsignedLong1 | m_unsignedLong2);
}
TEST_P(BitsetUnsignedLongPairTests, BitwiseXOROperator_MatchesUnsignedLongXOR)
{
EXPECT_EQ((m_bitset1 ^ m_bitset2).to_ulong(), m_unsignedLong1 ^ m_unsignedLong2);
}
TEST_P(BitsetUnsignedLongPairTests, BitwiseANDAssignmentOperator_MatchesUnsignedLongANDAssignment)
{
m_bitset1 &= m_bitset2;
m_unsignedLong1 &= m_unsignedLong2;
EXPECT_EQ(m_bitset1.to_ulong(), m_unsignedLong1);
}
TEST_P(BitsetUnsignedLongPairTests, BitwiseORAssignmentOperator_MatchesUnsignedLongORAssignment)
{
m_bitset1 |= m_bitset2;
m_unsignedLong1 |= m_unsignedLong2;
EXPECT_EQ(m_bitset1.to_ulong(), m_unsignedLong1);
}
TEST_P(BitsetUnsignedLongPairTests, BitwiseXORAssignmentOperator_MatchesUnsignedLongXORAssignment)
{
m_bitset1 ^= m_bitset2;
m_unsignedLong1 ^= m_unsignedLong2;
EXPECT_EQ(m_bitset1.to_ulong(), m_unsignedLong1);
}
TEST_P(BitsetUnsignedLongPairTests, EqualOperator_ComparedToOtherBitset_MatchesUnsignedLongComparison)
{
// The == comparison between the bitsets should have the same result as the == comparison between the unsigned longs
EXPECT_EQ(m_bitset1 == m_bitset2, m_unsignedLong1 == m_unsignedLong2);
}
TEST_P(BitsetUnsignedLongPairTests, NotEqualOperator_ComparedToOtherBitset_MatchesUnsignedLongComparison)
{
// The != comparison between the bitsets should have the same result as the != comparison between the unsigned longs
EXPECT_EQ(m_bitset1 != m_bitset2, m_unsignedLong1 != m_unsignedLong2);
}
TEST_P(BitsetUnsignedLongPairTests, CopyConstructor_CopyOtherBitset_EqualsOriginal)
{
m_bitset2 = m_bitset1;
EXPECT_EQ(m_bitset1, m_bitset2);
}
TEST_P(BitsetUnsignedLongPairTests, ReferenceAssignmentOperator_AssignBoolToReferenceForEachBit_ReferencedBitsetMatchesOriginal)
{
// Iterate over the entire m_bitset1 and assign its values to m_bitset2 bit by bit
for (size_t i = 0; i < m_bitset1.size(); ++i)
{
// Assign a bool value to the reference
bool bitValue = m_bitset1[i];
AZStd::bitset<32>::reference bitset2Ref = m_bitset2[i];
bitset2Ref = bitValue;
EXPECT_EQ(m_bitset1[i], m_bitset2[i]) << "The value of the bit at index " << i << " from m_bitset1 was not assigned to the bit at index " << i << " of m_bitset2.";
}
EXPECT_EQ(m_bitset1, m_bitset2);
}
TEST_P(BitsetUnsignedLongPairTests, ReferenceAssignmentOperator_AssignReferenceToReferenceForEachBit_ReferencedBitsetMatchesOriginal)
{
// Iterate over the entire m_bitset1 and assign its values to m_bitset2 bit by bit
for (size_t i = 0; i < m_bitset1.size(); ++i)
{
// Assign a bitset reference value to the reference
AZStd::bitset<32>::reference bitset1Ref = m_bitset1[i];
AZStd::bitset<32>::reference bitset2Ref = m_bitset2[i];
bitset2Ref = bitset1Ref;
EXPECT_EQ(m_bitset1[i], m_bitset2[i]) << "The value of the bit at index " << i << " from m_bitset1 was not assigned to the bit at index " << i << " of m_bitset2.";
}
EXPECT_EQ(m_bitset1, m_bitset2);
}
// This class initializes an AZStd::bitset and an std::bitset with the same value to validate that the behavior of AZStd::bitset matches the behavior of std::bitset
// It should only be used to test functions where we want AZStd::bitset to conform to the behavior of std::bitset
// It is useful for testing functions where operations on the bitset behave differently than the same operations on an unsigned long,
// such as shifting bits beyond the length of the bitset
class BitsetStdComparisonTests
: public ::testing::WithParamInterface <unsigned long>
, public UnitTest::ScopedAllocatorSetupFixture
{
protected:
void SetUp() override
{
// Initialize the bitsets from an unsigned long
m_stdBitset = std::bitset<32>(GetParam());
m_bitset = AZStd::bitset<32>(GetParam());
}
// The ground truth for the unit test
std::bitset<32> m_stdBitset;
// The unit under test
AZStd::bitset<32> m_bitset;
// An array of values to use when shifting bits
AZStd::vector<AZStd::size_t> m_shiftValues = { 0,1,2,3,5,8,13,21,44 };
};
TEST_P(BitsetStdComparisonTests, RightShift_MatchesStd)
{
for (AZStd::size_t value : m_shiftValues)
{
EXPECT_EQ((m_bitset >> value).to_ulong(), (m_stdBitset >> value).to_ulong()) << "Right shift by " << value << " bits did not match std::bitset";
}
}
TEST_P(BitsetStdComparisonTests, RightShiftAssignment_MatchesStd)
{
for (AZStd::size_t value : m_shiftValues)
{
m_bitset >>= value;
m_stdBitset >>= value;
EXPECT_EQ(m_bitset.to_ulong(), m_stdBitset.to_ulong()) << "Right shift assignment by " << value << " bits did not match std::bitset.";
}
}
TEST_P(BitsetStdComparisonTests, LeftShift_MatchesStd)
{
for (AZStd::size_t value : m_shiftValues)
{
EXPECT_EQ((m_bitset << value).to_ulong(), (m_stdBitset << value).to_ulong()) << "Left shift by " << value << " bits did not match std::bitset";
}
}
TEST_P(BitsetStdComparisonTests, LeftShiftAssignment_MatchesStd)
{
for (AZStd::size_t value : m_shiftValues)
{
m_bitset <<= value;
m_stdBitset <<= value;
EXPECT_EQ(m_bitset.to_ulong(), m_stdBitset.to_ulong()) << "Left shift assignment by " << value << " bits did not match std::bitset.";
}
}
TEST_P(BitsetStdComparisonTests, ToString_MatchesStd)
{
AZStd::string bitsetString = m_bitset.to_string<char>();
std::string stdBitsetString = m_stdBitset.to_string<char>();
EXPECT_TRUE(azstricmp(bitsetString.c_str(), stdBitsetString.c_str()) == 0) << "Bitset string '" << bitsetString.c_str() << "' does not match expected output '" << stdBitsetString << "'";
}
// Helper to generate a set of n bitsets that can be re-used to generate either n test cases or nxn test cases
std::vector<unsigned long> GenerateBitsetTestCases()
{
std::vector<unsigned long> testCases;
testCases.push_back(0b0000'0000'0000'0000'0000'0000'0000'0000);
testCases.push_back(0b1111'1111'1111'1111'1111'1111'1111'1111);
testCases.push_back(0b1010'1010'1010'1010'1010'1010'1010'1010);
testCases.push_back(0b0101'0101'0101'0101'0101'0101'0101'0101);
testCases.push_back(0b0000'0000'0000'0000'1111'1111'1111'1111);
testCases.push_back(0b1111'1111'1111'1111'0000'0000'0000'0000);
// Asymmetrical test cases
testCases.push_back(0b1100'1010'1000'1000'0011'1101'0100'1100);
testCases.push_back(0b0111'1100'1000'1001'1000'0001'1001'1001);
return testCases;
};
std::vector<unsigned long> GenerateBitsetUnsignedLongTestCases()
{
return GenerateBitsetTestCases();
}
std::vector<AZStd::pair<unsigned long, unsigned long>> GenerateBitsetUnsignedLongPairTestCases()
{
std::vector<AZStd::pair<unsigned long, unsigned long>> testCasePairs;
std::vector<unsigned long> testCases = GenerateBitsetTestCases();
for (unsigned long value1 : testCases)
{
for (unsigned long value2 : testCases)
{
testCasePairs.push_back(AZStd::pair<unsigned long, unsigned long>(value1, value2));
}
}
return testCasePairs;
}
std::string GenerateBitsetUnsignedLongTestCaseName(const ::testing::TestParamInfo<unsigned long>& info)
{
std::bitset<32> stdBitset(info.param);
return stdBitset.to_string();
}
std::string GenerateBitsetUnsignedLongPairTestCaseName(const ::testing::TestParamInfo<AZStd::pair<unsigned long, unsigned long>>& info)
{
// Output a string in the style of 0000x1111 where 0000 is the first bitset and 1111 is the second bitset
std::bitset<32> bitset1(info.param.first);
std::bitset<32> bitset2(info.param.second);
return bitset1.to_string() + 'x' + bitset2.to_string();
}
INSTANTIATE_TEST_CASE_P(Bitset, BitsetUnsignedLongTests, ::testing::ValuesIn(GenerateBitsetUnsignedLongTestCases()), GenerateBitsetUnsignedLongTestCaseName);
INSTANTIATE_TEST_CASE_P(Bitset, BitsetUnsignedLongPairTests, ::testing::ValuesIn(GenerateBitsetUnsignedLongPairTestCases()), GenerateBitsetUnsignedLongPairTestCaseName);
INSTANTIATE_TEST_CASE_P(Bitset, BitsetStdComparisonTests, ::testing::ValuesIn(GenerateBitsetUnsignedLongTestCases()), GenerateBitsetUnsignedLongTestCaseName);
using namespace AZStd;
class BitsetTests
: public AllocatorsFixture
{
};
TEST_F(BitsetTests, DefaultConstructor_IsZero)
{
bitset<8> bitset;
ASSERT_EQ(bitset.to_ullong(), 0);
}
TEST_F(BitsetTests, Constructor64Bits_MatchesInput)
{
constexpr AZ::u64 initValue = std::numeric_limits<AZ::u64>::max();
bitset<64> bitset(initValue);
ASSERT_EQ(bitset.to_ullong(), initValue);
}
TEST_F(BitsetTests, Constructor64BitsInto32Bits_MatchesLeastSignificant32Bits)
{
constexpr AZ::u64 initValue = std::numeric_limits<AZ::u64>::max();
bitset<32> bitset(initValue);
constexpr AZ::u64 expectedValue(initValue & static_cast<AZ::u32>(-1));
ASSERT_EQ(bitset.to_ullong(), expectedValue);
}
TEST_F(BitsetTests, Constructor32BitsInto64Bits_ZeroPadRemaining)
{
constexpr AZ::u32 initValue = std::numeric_limits<AZ::u32>::max();
bitset<64> bitset(initValue);
constexpr AZ::u64 expectedValue = static_cast<AZ::u64>(initValue);;
ASSERT_EQ(bitset.to_ullong(), expectedValue);
}
TEST_F(BitsetTests, GeneralTesting)
{
// BitsetTest-Begin
typedef bitset<25> bitset25_type;
bitset25_type bs;
AZ_TEST_ASSERT(bs.count() == 0);
bitset25_type bs1((unsigned long)5);
AZ_TEST_ASSERT(bs1.count() == 2);
AZ_TEST_ASSERT(bs1[0] && bs1[2]);
string str("10110");
bitset25_type bs2(str, 0, str.length());
AZ_TEST_ASSERT(bs2.count() == 3);
AZ_TEST_ASSERT(bs2[1] && bs2[2] && bs2[4]);
bitset25_type::reference bit0 = bs2[0], bit1 = bs2[1];
AZ_TEST_ASSERT(bit0 == false);
AZ_TEST_ASSERT(bit1 == true);
bs &= bs1;
AZ_TEST_ASSERT(bs.count() == 0);
bs |= bs1;
AZ_TEST_ASSERT(bs.count() == 2);
AZ_TEST_ASSERT(bs[0] && bs[2]);
bs ^= bs2;
AZ_TEST_ASSERT(bs.count() == 3);
AZ_TEST_ASSERT(bs[0] && bs[1] && bs[4]);
bs <<= 4;
AZ_TEST_ASSERT(bs.count() == 3);
AZ_TEST_ASSERT(bs[4] && bs[5] && bs[8]);
bs >>= 3;
AZ_TEST_ASSERT(bs.count() == 3);
AZ_TEST_ASSERT(bs[1] && bs[2] && bs[5]);
bs.set(3);
AZ_TEST_ASSERT(bs.count() == 4);
AZ_TEST_ASSERT(bs[1] && bs[2] && bs[3] && bs[5]);
bs.set(1, false);
AZ_TEST_ASSERT(bs.count() == 3);
AZ_TEST_ASSERT(!bs[1] && bs[2] && bs[3] && bs[5]);
bs.set();
AZ_TEST_ASSERT(bs.count() == 25);
bs.reset();
AZ_TEST_ASSERT(bs.count() == 0);
bs.set(0);
bs.set(1);
AZ_TEST_ASSERT(bs.count() == 2);
bs.flip();
AZ_TEST_ASSERT(bs.count() == 23);
bs.flip(0);
AZ_TEST_ASSERT(bs.count() == 24);
str = bs.to_string<char>();
AZ_TEST_ASSERT(str.length() == 25);
AZ_TEST_ASSERT(bs != bs1);
bs2 = bs;
AZ_TEST_ASSERT(bs == bs2);
bs1.reset();
AZ_TEST_ASSERT(bs.any());
AZ_TEST_ASSERT(!bs1.any());
AZ_TEST_ASSERT(!bs.none());
AZ_TEST_ASSERT(bs1.none());
bs1 = bs >> 1;
AZ_TEST_ASSERT(bs1.count() == 23);
bs1 = bs << 2;
AZ_TEST_ASSERT(bs1.count() == 22);
// extensions
bitset25_type bs3(string("10110"));
AZ_TEST_ASSERT(bs3.num_words() == 1); // check number of words
bitset25_type::word_t tempWord = *bs3.data(); // access the bits data
AZ_TEST_ASSERT((tempWord & 0x16) == 0x16); // check values
bitset25_type bs4;
*bs4.data() = tempWord; // modify the data directly
AZ_TEST_ASSERT(bs3 == bs4);
}
} // end namespace UnitTest
@@ -0,0 +1,191 @@
/*
* 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 <AzCore/std/chrono/chrono.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <limits>
#include "UserTypes.h"
namespace UnitTest
{
//////////////////////////////////////////////////////////////////////////
// Fixtures
// Fixture for non-typed tests
class DurationTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
/*
* Helper Type Trait structure for adding expected value to typed
*/
template<typename TestType, size_t RequiredBits, typename ExpectedPeriodType>
struct DurationExpectation
{
using test_type = TestType;
using rep = typename TestType::rep;
using period = typename TestType::period;
using expected_period = ExpectedPeriodType;
static constexpr bool is_signed = AZStd::is_signed<int64_t>::value;
static constexpr bool is_integral = AZStd::is_integral<int64_t>::value;
static constexpr size_t required_bits = RequiredBits;
};
// Fixture for typed tests
template<typename ExpectedResultTraits>
class DurationTypedTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
using ChronoTestTypes = ::testing::Types<
DurationExpectation<AZStd::chrono::nanoseconds, 63, AZStd::nano>,
DurationExpectation<AZStd::chrono::microseconds, 54, AZStd::micro>,
DurationExpectation<AZStd::chrono::milliseconds, 44, AZStd::milli>,
DurationExpectation<AZStd::chrono::seconds, 44, AZStd::ratio<1>>,
DurationExpectation<AZStd::chrono::minutes, 28, AZStd::ratio<60>>,
DurationExpectation<AZStd::chrono::hours, 22, AZStd::ratio<3600>>
>;
TYPED_TEST_CASE(DurationTypedTest, ChronoTestTypes);
//////////////////////////////////////////////////////////////////////////
// Tests for std::duration compile time requirements
namespace CompileTimeRequirements
{
TYPED_TEST(DurationTypedTest, TraitRequirementsSuccess)
{
static_assert(AZStd::is_signed<typename TypeParam::rep>::value, "built in helper types for AZStd::chrono::duration requires representation type to be signed");
static_assert(AZStd::is_integral<typename TypeParam::rep>::value, "built in helper types for AZStd::chrono::duration requires representation type to an integral type");
static_assert(std::numeric_limits<typename TypeParam::rep>::digits >= TypeParam::required_bits, "representation type does not have the minimum number of required bits");
static_assert(AZStd::is_same_v<typename TypeParam::period, typename TypeParam::expected_period>, "duration period type does not match expected period type");
}
}
namespace Comparisons
{
TEST_F(DurationTest, Comparisons_SameType)
{
constexpr AZStd::chrono::milliseconds threeMillis(3);
constexpr AZStd::chrono::milliseconds oneMillis(1);
constexpr AZStd::chrono::milliseconds oneMillisAgain(1);
static_assert(oneMillis == oneMillisAgain);
static_assert(threeMillis > oneMillis);
static_assert(oneMillis < threeMillis);
static_assert(threeMillis >= oneMillis);
static_assert(oneMillis <= threeMillis);
static_assert(threeMillis >= threeMillis);
static_assert(threeMillis <= threeMillis);
}
TEST_F(DurationTest, Comparisons_DifferentType)
{
constexpr AZStd::chrono::milliseconds threeMillis(3);
constexpr AZStd::chrono::milliseconds oneMillis(1);
// different types:
constexpr AZStd::chrono::microseconds threeMillisButInMicroseconds(3000);
constexpr AZStd::chrono::microseconds oneMilliButInMicroseconds(1000);
static_assert(threeMillisButInMicroseconds > oneMillis);
static_assert(oneMilliButInMicroseconds < threeMillis);
static_assert(threeMillis == threeMillisButInMicroseconds);
static_assert(oneMilliButInMicroseconds == oneMillis);
static_assert(threeMillisButInMicroseconds >= threeMillis);
static_assert(threeMillisButInMicroseconds <= threeMillis);
static_assert(threeMillisButInMicroseconds >= oneMillis);
static_assert(oneMilliButInMicroseconds <= threeMillis);
}
}
// Test for std::duration arithmatic operations
namespace ArithmaticOperators
{
TEST_F(DurationTest, MillisecondsSubtractionResultsNegativeSuccess)
{
constexpr AZStd::chrono::milliseconds milliSeconds(3);
constexpr AZStd::chrono::milliseconds inverseMilliSeconds = -milliSeconds;
static_assert(milliSeconds.count() == -inverseMilliSeconds.count(), "inverseMilliseconds should be inverse of milliseconds");
static_assert(inverseMilliSeconds.count() == -3, "inverseMilliseconds value should be negative");
constexpr auto subtractResultMilliSeconds = inverseMilliSeconds - milliSeconds;
static_assert(subtractResultMilliSeconds.count() == -6, "subtract result is incorrect");
AZStd::chrono::milliseconds compoundSubtractMilliSeconds(5);
compoundSubtractMilliSeconds -= AZStd::chrono::milliseconds(4);
EXPECT_EQ(compoundSubtractMilliSeconds.count(), 1);
}
TEST_F(DurationTest, MillisecondsAdditionWithNegativeSuccess)
{
constexpr AZStd::chrono::milliseconds milliSeconds(3);
constexpr AZStd::chrono::milliseconds negativeMilliSeconds(-4);
constexpr auto addResultMilliSeconds = negativeMilliSeconds + milliSeconds;
static_assert(addResultMilliSeconds.count() == -1, "add result is incorrect");
AZStd::chrono::milliseconds compoundAddMilliSeconds(5);
compoundAddMilliSeconds += AZStd::chrono::milliseconds(2);
EXPECT_EQ(compoundAddMilliSeconds.count(), 7);
}
TEST_F(DurationTest, NanosecondsMultiplicationWithNegativeSuccess)
{
constexpr AZStd::chrono::nanoseconds negativeNanoSeconds(-16);
constexpr AZStd::chrono::nanoseconds multiplyResultSeconds = negativeNanoSeconds * 3;
static_assert(multiplyResultSeconds.count() == -48, "multiply result is incorrect");
AZStd::chrono::nanoseconds compoundMultiplyNanoSeconds(9);
compoundMultiplyNanoSeconds *= -2;
EXPECT_EQ(compoundMultiplyNanoSeconds.count(), -18);
}
TEST_F(DurationTest, SecondsDivideWithNegativeSuccess)
{
constexpr AZStd::chrono::seconds testSeconds(17);
constexpr AZStd::chrono::seconds negativeTestSeconds(-3);
constexpr auto divideResultSeconds = testSeconds / negativeTestSeconds;
static_assert(divideResultSeconds == -5, "divide result is incorrect");
AZStd::chrono::seconds compoundDivideSeconds(-42);
compoundDivideSeconds /= -2;
EXPECT_EQ(compoundDivideSeconds.count(), 21);
}
TEST_F(DurationTest, MicrosecondsModOperatorSuccess)
{
constexpr AZStd::chrono::microseconds microSeconds(23);
constexpr AZStd::chrono::microseconds microSecondsDivisor(2);
constexpr auto modResultMicroSeconds = microSeconds % microSecondsDivisor;
static_assert(modResultMicroSeconds.count() == 1, "mod result is incorrect");
AZStd::chrono::microseconds compoundModMicroSeconds(30);
compoundModMicroSeconds %= 7;
EXPECT_EQ(compoundModMicroSeconds.count(), 2);
}
}
}
@@ -0,0 +1,171 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/parallel/allocator_concurrent_static.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
static constexpr size_t s_allocatorCapacity = 1024;
static constexpr size_t s_numberThreads = 4;
template <typename AllocatorType>
class ConcurrentAllocatorTestFixture
: public AllocatorsTestFixture
{
protected:
using this_type = ConcurrentAllocatorTestFixture<AllocatorType>;
using allocator_type = AllocatorType;
};
struct NodeType
{
int m_number;
};
using AllocatorTypes = ::testing::Types<
AZStd::static_pool_concurrent_allocator<NodeType, s_allocatorCapacity>
>;
TYPED_TEST_CASE(ConcurrentAllocatorTestFixture, AllocatorTypes);
TYPED_TEST(ConcurrentAllocatorTestFixture, Name)
{
const char name[] = "My test allocator";
typename TestFixture::allocator_type myalloc(name);
EXPECT_EQ(0, strcmp(myalloc.get_name(), name));
{
const char newName[] = "My new test allocator";
myalloc.set_name(newName);
EXPECT_EQ(0, strcmp(myalloc.get_name(), newName));
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type) * s_allocatorCapacity, myalloc.get_max_size());
}
}
TYPED_TEST(ConcurrentAllocatorTestFixture, AllocateDeallocate)
{
typename TestFixture::allocator_type myalloc;
EXPECT_EQ(0, myalloc.get_allocated_size());
typename TestFixture::allocator_type::pointer_type data = myalloc.allocate();
EXPECT_NE(nullptr, data);
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type), myalloc.get_allocated_size());
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type) * (s_allocatorCapacity - 1), myalloc.get_max_size());
myalloc.deallocate(data);
EXPECT_EQ(0, myalloc.get_allocated_size());
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type) * s_allocatorCapacity, myalloc.get_max_size());
}
TYPED_TEST(ConcurrentAllocatorTestFixture, MultipleAllocateDeallocate)
{
typename TestFixture::allocator_type myalloc;
// Allocate N (6) and free half (evens)
constexpr size_t dataSize = 6; // keep this number even
typename TestFixture::allocator_type::pointer_type data[dataSize];
AZStd::set<typename TestFixture::allocator_type::pointer_type> dataSet; // to test for uniqueness
for (size_t i = 0; i < dataSize; ++i)
{
data[i] = myalloc.allocate();
EXPECT_NE(nullptr, data[i]);
dataSet.insert(data[i]);
}
EXPECT_EQ(dataSize, dataSet.size());
dataSet.clear();
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type) * dataSize, myalloc.get_allocated_size());
EXPECT_EQ((s_allocatorCapacity - dataSize) * sizeof(typename TestFixture::allocator_type::value_type), myalloc.get_max_size());
for (size_t i = 0; i < dataSize; i += 2)
{
myalloc.deallocate(data[i]);
}
EXPECT_EQ(sizeof(typename TestFixture::allocator_type::value_type) * (dataSize / 2), myalloc.get_allocated_size());
EXPECT_EQ((s_allocatorCapacity - dataSize / 2) * sizeof(typename TestFixture::allocator_type::value_type), myalloc.get_max_size());
for (size_t i = 1; i < dataSize; i += 2)
{
myalloc.deallocate(data[i]);
}
EXPECT_EQ(0, myalloc.get_allocated_size());
EXPECT_EQ(s_allocatorCapacity * sizeof(typename TestFixture::allocator_type::value_type), myalloc.get_max_size());
}
TYPED_TEST(ConcurrentAllocatorTestFixture, ConcurrentAllocateoDeallocate)
{
typename TestFixture::allocator_type myalloc;
AZStd::atomic<int> failures{ 0 };
AZStd::array<AZStd::thread, s_numberThreads> threads;
for (size_t i = 0; i < s_numberThreads; ++i)
{
threads[i] = AZStd::thread([&myalloc, &failures]
{
// We have 4 threads, each thread can allocate at most s_allocatorCapacity/s_numberThreads values.
// The amount of iterations do not affect since each thread will free all the values before the next
// iteration
constexpr size_t numIterations = 100;
constexpr size_t numValues = s_allocatorCapacity / s_numberThreads;
AZStd::array<typename TestFixture::allocator_type::pointer_type, numValues> allocations;
for (int iter = 0; iter < numIterations; ++iter)
{
// allocate
for (int i = 0; i < numValues; ++i)
{
allocations[i] = myalloc.allocate();
if (!allocations[i])
{
++failures;
}
}
// deallocate
for (int i = 0; i < numValues; ++i)
{
myalloc.deallocate(allocations[i]);
allocations[i] = nullptr;
}
}
});
}
for (size_t i = 0; i < s_numberThreads; ++i)
{
threads[i].join();
}
EXPECT_EQ(0, failures);
EXPECT_EQ(0, myalloc.get_allocated_size());
}
using StaticPoolConcurrentAllocatorTestFixture = AllocatorsTestFixture;
TEST(StaticPoolConcurrentAllocatorTestFixture, Aligment)
{
// static pool allocator
// Generally we can't use more then 16 byte alignment on the stack.
// Some platforms might fail. Which is ok, higher alignment should be handled by US. Or not on the stack.
const int dataAlignment = 16;
typedef aligned_storage<sizeof(int), dataAlignment>::type aligned_int_type;
typedef AZStd::static_pool_concurrent_allocator<aligned_int_type, s_allocatorCapacity> aligned_int_node_pool_type;
aligned_int_node_pool_type myaligned_pool;
aligned_int_type* aligned_data = reinterpret_cast<aligned_int_type*>(myaligned_pool.allocate(sizeof(aligned_int_type), dataAlignment));
EXPECT_NE(nullptr, aligned_data);
EXPECT_EQ(0, ((AZStd::size_t)aligned_data & (dataAlignment - 1)));
EXPECT_EQ((s_allocatorCapacity - 1) * sizeof(aligned_int_type), myaligned_pool.get_max_size());
EXPECT_EQ(sizeof(aligned_int_type), myaligned_pool.get_allocated_size());
myaligned_pool.deallocate(aligned_data, sizeof(aligned_int_type), dataAlignment); // Make sure we free what we have allocated.
}
}
@@ -0,0 +1,716 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/parallel/containers/concurrent_fixed_unordered_set.h>
#include <AzCore/std/parallel/containers/concurrent_fixed_unordered_map.h>
#include <AzCore/std/parallel/containers/concurrent_unordered_map.h>
#include <AzCore/std/parallel/containers/concurrent_unordered_set.h>
#include <AzCore/std/parallel/containers/concurrent_vector.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
template<typename Set>
class ConcurrentUnorderedSetTestBase
: public ScopedAllocatorSetupFixture
{
public:
void run()
{
Set set;
//insert
AZ_TEST_ASSERT(set.empty());
AZ_TEST_ASSERT(set.size() == 0);
AZ_TEST_ASSERT(set.insert(10));
AZ_TEST_ASSERT(!set.empty());
AZ_TEST_ASSERT(set.size() == 1);
AZ_TEST_ASSERT(set.insert(20));
AZ_TEST_ASSERT(set.size() == 2);
AZ_TEST_ASSERT(set.insert(30));
AZ_TEST_ASSERT(set.size() == 3);
AZ_TEST_ASSERT(!set.insert(20)); //not multiset
AZ_TEST_ASSERT(set.size() == 3);
//find
AZ_TEST_ASSERT(set.find(10));
AZ_TEST_ASSERT(!set.find(40));
//erase
AZ_TEST_ASSERT(set.erase(10) == 1);
AZ_TEST_ASSERT(set.size() == 2);
AZ_TEST_ASSERT(set.erase(10) == 0);
AZ_TEST_ASSERT(set.size() == 2);
AZ_TEST_ASSERT(set.erase(100) == 0);
AZ_TEST_ASSERT(set.size() == 2);
//erase_one
AZ_TEST_ASSERT(set.erase_one(20));
AZ_TEST_ASSERT(set.size() == 1);
AZ_TEST_ASSERT(set.erase_one(30));
AZ_TEST_ASSERT(set.size() == 0);
AZ_TEST_ASSERT(set.empty());
//clear
set.insert(10);
AZ_TEST_ASSERT(!set.empty());
set.clear();
AZ_TEST_ASSERT(set.empty());
AZ_TEST_ASSERT(set.erase(10) == 0);
//assignment
set.insert(10);
set.insert(20);
set.insert(30);
Set set2(set);
AZ_TEST_ASSERT(set2.size() == 3);
AZ_TEST_ASSERT(set2.find(20));
Set set3;
set3 = set;
AZ_TEST_ASSERT(set3.size() == 3);
AZ_TEST_ASSERT(set3.find(20));
set.erase(10);
AZ_TEST_ASSERT(set.size() == 2);
set.swap(set3);
AZ_TEST_ASSERT(set.size() == 3);
AZ_TEST_ASSERT(set3.size() == 2);
{
m_failures = 0;
AZStd::thread thread0(AZStd::bind(&ConcurrentUnorderedSetTestBase::InsertErase, this, 0));
AZStd::thread thread1(AZStd::bind(&ConcurrentUnorderedSetTestBase::InsertErase, this, 1));
AZStd::thread thread2(AZStd::bind(&ConcurrentUnorderedSetTestBase::InsertErase, this, 2));
AZStd::thread thread3(AZStd::bind(&ConcurrentUnorderedSetTestBase::InsertErase, this, 3));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_failures == 0);
AZ_TEST_ASSERT(m_set.empty());
}
m_set = Set(); // clear memory
}
private:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 1;
#else
static const int NUM_ITERATIONS = 200;
#endif
static const int NUM_VALUES = 500;
void InsertErase(int id)
{
for (int iter = 0; iter < NUM_ITERATIONS; ++iter)
{
//insert
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_set.insert(id * NUM_VALUES + i))
{
++m_failures;
}
}
//find
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_set.find(id * NUM_VALUES + i))
{
++m_failures;
}
}
//erase
for (int i = 0; i < NUM_VALUES; ++i)
{
if (m_set.erase(id * NUM_VALUES + i) != 1)
{
++m_failures;
}
}
}
}
Set m_set;
atomic<int> m_failures;
};
typedef ConcurrentUnorderedSetTestBase<concurrent_unordered_set<int> > ConcurrentUnorderedSetTest;
typedef ConcurrentUnorderedSetTestBase<concurrent_fixed_unordered_set<int, 1543, 2100> > ConcurrentFixedUnorderedSetTest;
TEST_F(ConcurrentUnorderedSetTest, Test)
{
run();
}
TEST_F(ConcurrentFixedUnorderedSetTest, Test)
{
run();
}
template<typename Set>
class ConcurrentUnorderedMultiSetTestBase
: public ScopedAllocatorSetupFixture
{
public:
void run()
{
Set set;
//insert
AZ_TEST_ASSERT(set.empty());
AZ_TEST_ASSERT(set.size() == 0);
AZ_TEST_ASSERT(set.insert(10));
AZ_TEST_ASSERT(!set.empty());
AZ_TEST_ASSERT(set.size() == 1);
AZ_TEST_ASSERT(set.insert(20));
AZ_TEST_ASSERT(set.size() == 2);
AZ_TEST_ASSERT(set.insert(20)); //multiset
AZ_TEST_ASSERT(set.size() == 3);
AZ_TEST_ASSERT(set.insert(30));
AZ_TEST_ASSERT(set.insert(30));
AZ_TEST_ASSERT(set.insert(30));
AZ_TEST_ASSERT(set.size() == 6);
//find
AZ_TEST_ASSERT(set.find(10));
AZ_TEST_ASSERT(set.find(20));
AZ_TEST_ASSERT(!set.find(40));
//erase
AZ_TEST_ASSERT(set.erase(10) == 1);
AZ_TEST_ASSERT(set.size() == 5);
AZ_TEST_ASSERT(set.erase(10) == 0);
AZ_TEST_ASSERT(set.size() == 5);
AZ_TEST_ASSERT(set.erase(100) == 0);
AZ_TEST_ASSERT(set.size() == 5);
AZ_TEST_ASSERT(set.erase(20) == 2);
AZ_TEST_ASSERT(set.size() == 3);
//erase_one
AZ_TEST_ASSERT(set.erase_one(30));
AZ_TEST_ASSERT(set.size() == 2);
AZ_TEST_ASSERT(set.erase(30) == 2);
AZ_TEST_ASSERT(set.size() == 0);
AZ_TEST_ASSERT(set.empty());
//clear
set.insert(10);
AZ_TEST_ASSERT(!set.empty());
set.clear();
AZ_TEST_ASSERT(set.empty());
AZ_TEST_ASSERT(set.erase(10) == 0);
//assignment
set.insert(10);
set.insert(20);
set.insert(30);
Set set2(set);
AZ_TEST_ASSERT(set2.size() == 3);
AZ_TEST_ASSERT(set2.find(20));
Set set3;
set3 = set;
AZ_TEST_ASSERT(set3.size() == 3);
AZ_TEST_ASSERT(set3.find(20));
set.erase(10);
AZ_TEST_ASSERT(set.size() == 2);
set.swap(set3);
AZ_TEST_ASSERT(set.size() == 3);
AZ_TEST_ASSERT(set3.size() == 2);
{
m_failures = 0;
AZStd::thread thread0(AZStd::bind(&ConcurrentUnorderedMultiSetTestBase::InsertErase, this));
AZStd::thread thread1(AZStd::bind(&ConcurrentUnorderedMultiSetTestBase::InsertErase, this));
AZStd::thread thread2(AZStd::bind(&ConcurrentUnorderedMultiSetTestBase::InsertErase, this));
AZStd::thread thread3(AZStd::bind(&ConcurrentUnorderedMultiSetTestBase::InsertErase, this));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_failures == 0);
AZ_TEST_ASSERT(m_set.empty());
}
m_set = Set(); // clear memory
}
private:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 1;
#else
static const int NUM_ITERATIONS = 200;
#endif
static const int NUM_VALUES = 500;
void InsertErase()
{
for (int iter = 0; iter < NUM_ITERATIONS; ++iter)
{
//insert
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_set.insert(i))
{
++m_failures;
}
}
//find
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_set.find(i))
{
++m_failures;
}
}
//erase
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_set.erase_one(i))
{
++m_failures;
}
}
}
}
Set m_set;
atomic<int> m_failures;
};
typedef ConcurrentUnorderedMultiSetTestBase<concurrent_unordered_multiset<int> > ConcurrentUnorderedMultiSetTest;
typedef ConcurrentUnorderedMultiSetTestBase<concurrent_fixed_unordered_multiset<int, 1543, 2100> > ConcurrentFixedUnorderedMultiSetTest;
TEST_F(ConcurrentUnorderedMultiSetTest, Test)
{
run();
}
TEST_F(ConcurrentFixedUnorderedMultiSetTest, Test)
{
run();
}
template<typename Map>
class ConcurrentUnorderedMapTestBase
: public ScopedAllocatorSetupFixture
{
public:
void run()
{
Map map;
//insert
AZ_TEST_ASSERT(map.empty());
AZ_TEST_ASSERT(map.size() == 0);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(10, 11)));
AZ_TEST_ASSERT(!map.empty());
AZ_TEST_ASSERT(map.size() == 1);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(20, 21)));
AZ_TEST_ASSERT(map.size() == 2);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(30, 31)));
AZ_TEST_ASSERT(map.size() == 3);
AZ_TEST_ASSERT(!map.insert(AZStd::make_pair(20, 22))); //not multimap
AZ_TEST_ASSERT(map.size() == 3);
//find
AZ_TEST_ASSERT(map.find(10));
AZ_TEST_ASSERT(!map.find(40));
int result = 0;
AZ_TEST_ASSERT(map.find(10, &result));
AZ_TEST_ASSERT(result == 11);
//erase
AZ_TEST_ASSERT(map.erase(10) == 1);
AZ_TEST_ASSERT(map.size() == 2);
AZ_TEST_ASSERT(map.erase(10) == 0);
AZ_TEST_ASSERT(map.size() == 2);
AZ_TEST_ASSERT(map.erase(100) == 0);
AZ_TEST_ASSERT(map.size() == 2);
//erase_one
AZ_TEST_ASSERT(map.erase_one(20));
AZ_TEST_ASSERT(map.size() == 1);
AZ_TEST_ASSERT(map.erase_one(30));
AZ_TEST_ASSERT(map.size() == 0);
AZ_TEST_ASSERT(map.empty());
//clear
map.insert(10);
AZ_TEST_ASSERT(!map.empty());
map.clear();
AZ_TEST_ASSERT(map.empty());
AZ_TEST_ASSERT(map.erase(10) == 0);
//assignment
map.insert(AZStd::make_pair(10, 11));
map.insert(AZStd::make_pair(20, 21));
map.insert(AZStd::make_pair(30, 31));
Map map2(map);
AZ_TEST_ASSERT(map2.size() == 3);
AZ_TEST_ASSERT(map2.find(20));
Map map3;
map3 = map;
AZ_TEST_ASSERT(map3.size() == 3);
AZ_TEST_ASSERT(map3.find(20));
map.erase(10);
AZ_TEST_ASSERT(map.size() == 2);
map.swap(map3);
AZ_TEST_ASSERT(map.size() == 3);
AZ_TEST_ASSERT(map3.size() == 2);
{
m_failures = 0;
AZStd::thread thread0(AZStd::bind(&ConcurrentUnorderedMapTestBase::InsertErase, this, 0));
AZStd::thread thread1(AZStd::bind(&ConcurrentUnorderedMapTestBase::InsertErase, this, 1));
AZStd::thread thread2(AZStd::bind(&ConcurrentUnorderedMapTestBase::InsertErase, this, 2));
AZStd::thread thread3(AZStd::bind(&ConcurrentUnorderedMapTestBase::InsertErase, this, 3));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_failures == 0);
AZ_TEST_ASSERT(m_map.empty());
}
m_map = Map(); // clear memory
}
private:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 1;
#else
static const int NUM_ITERATIONS = 200;
#endif
static const int NUM_VALUES = 500;
void InsertErase(int id)
{
for (int iter = 0; iter < NUM_ITERATIONS; ++iter)
{
//insert
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_map.insert(AZStd::make_pair(id * NUM_VALUES + i, id * NUM_VALUES + i + 1)))
{
++m_failures;
}
}
//find
for (int i = 0; i < NUM_VALUES; ++i)
{
int result = 0;
if (!m_map.find(id * NUM_VALUES + i, &result))
{
++m_failures;
}
if (result != id * NUM_VALUES + i + 1)
{
++m_failures;
}
}
//erase
for (int i = 0; i < NUM_VALUES; ++i)
{
if (m_map.erase(id * NUM_VALUES + i) != 1)
{
++m_failures;
}
}
}
}
Map m_map;
atomic<int> m_failures;
};
typedef ConcurrentUnorderedMapTestBase<concurrent_unordered_map<int, int> > ConcurrentUnorderedMapTest;
typedef ConcurrentUnorderedMapTestBase<concurrent_fixed_unordered_map<int, int, 1543, 2100> > ConcurrentFixedUnorderedMapTest;
TEST_F(ConcurrentUnorderedMapTest, Test)
{
run();
}
TEST_F(ConcurrentFixedUnorderedMapTest, Test)
{
run();
}
template<typename Map>
class ConcurrentUnorderedMultiMapTestBase
: public ScopedAllocatorSetupFixture
{
public:
void run()
{
Map map;
//insert
AZ_TEST_ASSERT(map.empty());
AZ_TEST_ASSERT(map.size() == 0);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(10, 11)));
AZ_TEST_ASSERT(!map.empty());
AZ_TEST_ASSERT(map.size() == 1);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(20, 21)));
AZ_TEST_ASSERT(map.size() == 2);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(20, 22))); //multimap
AZ_TEST_ASSERT(map.size() == 3);
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(30, 31)));
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(30, 32)));
AZ_TEST_ASSERT(map.insert(AZStd::make_pair(30, 33)));
AZ_TEST_ASSERT(map.size() == 6);
//find
AZ_TEST_ASSERT(map.find(10));
AZ_TEST_ASSERT(map.find(20));
AZ_TEST_ASSERT(!map.find(40));
int result = 0;
AZ_TEST_ASSERT(map.find(10, &result));
AZ_TEST_ASSERT(result == 11);
//erase
AZ_TEST_ASSERT(map.erase(10) == 1);
AZ_TEST_ASSERT(map.size() == 5);
AZ_TEST_ASSERT(map.erase(10) == 0);
AZ_TEST_ASSERT(map.size() == 5);
AZ_TEST_ASSERT(map.erase(100) == 0);
AZ_TEST_ASSERT(map.size() == 5);
AZ_TEST_ASSERT(map.erase(20) == 2);
AZ_TEST_ASSERT(map.size() == 3);
//erase_one
AZ_TEST_ASSERT(map.erase_one(30));
AZ_TEST_ASSERT(map.size() == 2);
AZ_TEST_ASSERT(map.erase(30) == 2);
AZ_TEST_ASSERT(map.size() == 0);
AZ_TEST_ASSERT(map.empty());
//clear
map.insert(10);
AZ_TEST_ASSERT(!map.empty());
map.clear();
AZ_TEST_ASSERT(map.empty());
AZ_TEST_ASSERT(map.erase(10) == 0);
//assignment
map.insert(10);
map.insert(20);
map.insert(30);
Map map2(map);
AZ_TEST_ASSERT(map2.size() == 3);
AZ_TEST_ASSERT(map2.find(20));
Map map3;
map3 = map;
AZ_TEST_ASSERT(map3.size() == 3);
AZ_TEST_ASSERT(map3.find(20));
map.erase(10);
AZ_TEST_ASSERT(map.size() == 2);
map.swap(map3);
AZ_TEST_ASSERT(map.size() == 3);
AZ_TEST_ASSERT(map3.size() == 2);
{
m_failures = 0;
AZStd::thread thread0(AZStd::bind(&ConcurrentUnorderedMultiMapTestBase::InsertErase, this));
AZStd::thread thread1(AZStd::bind(&ConcurrentUnorderedMultiMapTestBase::InsertErase, this));
AZStd::thread thread2(AZStd::bind(&ConcurrentUnorderedMultiMapTestBase::InsertErase, this));
AZStd::thread thread3(AZStd::bind(&ConcurrentUnorderedMultiMapTestBase::InsertErase, this));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_failures == 0);
AZ_TEST_ASSERT(m_map.empty());
}
Map dummy;
m_map.swap(dummy); // clear memory
}
private:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 1;
#else
static const int NUM_ITERATIONS = 200;
#endif
static const int NUM_VALUES = 500;
void InsertErase()
{
for (int iter = 0; iter < NUM_ITERATIONS; ++iter)
{
//insert
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_map.insert(AZStd::make_pair(i, i + 1)))
{
++m_failures;
}
}
//find
for (int i = 0; i < NUM_VALUES; ++i)
{
int result = 0;
if (!m_map.find(i, &result))
{
++m_failures;
}
if (result != i + 1)
{
++m_failures;
}
}
//erase
for (int i = 0; i < NUM_VALUES; ++i)
{
if (!m_map.erase_one(i))
{
++m_failures;
}
}
}
}
Map m_map;
atomic<int> m_failures;
};
typedef ConcurrentUnorderedMultiMapTestBase<concurrent_unordered_multimap<int, int> > ConcurrentUnorderedMultiMapTest;
typedef ConcurrentUnorderedMultiMapTestBase<concurrent_fixed_unordered_multimap<int, int, 1543, 2100> > ConcurrentFixedUnorderedMultiMapTest;
TEST_F(ConcurrentUnorderedMultiMapTest, Test)
{
run();
}
TEST_F(ConcurrentFixedUnorderedMultiMapTest, Test)
{
run();
}
class ConcurrentVectorTest
: public ScopedAllocatorSetupFixture
{
public:
void run()
{
//
//single threaded functionality tests
//
concurrent_vector<int> testVector;
AZ_TEST_ASSERT(testVector.empty());
AZ_TEST_ASSERT(testVector.size() == 0);
testVector.push_back(10);
AZ_TEST_ASSERT(!testVector.empty());
AZ_TEST_ASSERT(testVector.size() == 1);
AZ_TEST_ASSERT(testVector[0] == 10);
testVector[0] = 20;
AZ_TEST_ASSERT(testVector[0] == 20);
testVector.clear();
AZ_TEST_ASSERT(testVector.empty());
AZ_TEST_ASSERT(testVector.size() == 0);
for (int i = 0; i < 100; ++i)
{
testVector.push_back(i + 1000);
}
AZ_TEST_ASSERT(testVector.size() == 100);
for (int i = 0; i < 100; ++i)
{
AZ_TEST_ASSERT(testVector[i] == i + 1000);
}
//
//multithread tests
//
{
AZStd::thread thread0(AZStd::bind(&ConcurrentVectorTest::Push, this, 0));
AZStd::thread thread1(AZStd::bind(&ConcurrentVectorTest::Push, this, 1));
AZStd::thread thread2(AZStd::bind(&ConcurrentVectorTest::Push, this, 2));
AZStd::thread thread3(AZStd::bind(&ConcurrentVectorTest::Push, this, 3));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
//verify vector contains the right values in the expected order
AZ_TEST_ASSERT(m_vector.size() == 4 * NUM_ITERATIONS);
int nextValue[4];
for (int i = 0; i < 4; ++i)
{
nextValue[i] = i * NUM_ITERATIONS;
}
for (unsigned int vecIndex = 0; vecIndex < m_vector.size(); ++vecIndex)
{
int value = m_vector[vecIndex];
bool isFound = false;
for (int i = 0; i < 4; ++i)
{
if (nextValue[i] == value)
{
isFound = true;
++nextValue[i];
break;
}
}
AZ_TEST_ASSERT(isFound);
}
}
}
private:
#if defined(_DEBUG)
static const int NUM_ITERATIONS = 10000;
#else
static const int NUM_ITERATIONS = 500000;
#endif
void Push(int threadIndex)
{
for (int i = 0; i < NUM_ITERATIONS; ++i)
{
m_vector.push_back(threadIndex * NUM_ITERATIONS + i);
}
}
concurrent_vector<int> m_vector;
};
TEST_F(ConcurrentVectorTest, Test)
{
run();
}
}
@@ -0,0 +1,141 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/array.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/fixed_string.h>
#include <array>
using namespace UnitTestInternal;
namespace UnitTest
{
TEST(CreateDestroy, UninitializedFill_StdArray_IntType_AllEight)
{
const int intArraySize = 5;
const int fillValue = 8;
std::array<int, intArraySize> intArray;
AZStd::uninitialized_fill(intArray.begin(), intArray.end(), fillValue, std::false_type());
for (auto itr : intArray)
{
EXPECT_EQ(itr, fillValue);
}
}
TEST(CreateDestroy, UninitializedFill_AZStdArray_IntType_AllEight)
{
const int intArraySize = 5;
const int fillValue = 8;
AZStd::array<int, intArraySize> intArray;
AZStd::uninitialized_fill(intArray.begin(), intArray.end(), fillValue, std::false_type());
for (auto itr : intArray)
{
EXPECT_EQ(itr, fillValue);
}
}
TEST(CreateDestroy, UninitializedFill_StdArray_StringType_AllEight)
{
const int stringArraySize = 5;
const AZStd::string fillValue = "hello, world";
std::array<AZStd::string, stringArraySize> stringArray;
AZStd::uninitialized_fill(stringArray.begin(), stringArray.end(), fillValue, std::false_type());
for (auto itr : stringArray)
{
EXPECT_EQ(0, strcmp(itr.c_str(), fillValue.c_str()));
}
}
TEST(CreateDestroy, UninitializedFill_AZStdArray_StringType_AllEight)
{
const int stringArraySize = 5;
const AZStd::string fillValue = "hello, world";
AZStd::array<AZStd::string, stringArraySize> stringArray;
AZStd::uninitialized_fill(stringArray.begin(), stringArray.end(), fillValue, std::false_type());
for (auto itr : stringArray)
{
EXPECT_EQ(0, strcmp(itr.c_str(), fillValue.c_str()));
}
}
TEST(CreateDestroy, Destroy_Compile_WhenUsedInConstexpr)
{
auto TestDestroyFunc = []() constexpr -> int
{
AZStd::string_view testValue("Test");
AZStd::Internal::destroy<decltype(testValue)*>::single(&testValue);
AZStd::string_view testArray[] = { AZStd::string_view("Test"), AZStd::string_view("World") };
AZStd::Internal::destroy<decltype(testValue)*>::range(AZStd::begin(testArray), AZStd::end(testArray));
return 73;
};
static_assert(AZStd::is_trivially_destructible_v<AZStd::string_view>);
static_assert(TestDestroyFunc() == 73);
}
TEST(CreateDestroy, IsFastCopyTraits_SucceedForContiguousIteratorTypes)
{
using list_type = AZStd::list<int>;
using vector_type = AZStd::vector<int>;
using string_type = AZStd::string;
static_assert(AZStd::Internal::is_fast_copy<const char*, const char*>::value);
static_assert(!AZStd::Internal::is_fast_copy<typename list_type::iterator, int*>::value);
static_assert(!AZStd::Internal::is_fast_copy<int*, typename list_type::iterator>::value);
static_assert(AZStd::Internal::is_fast_copy<typename vector_type::iterator, int*>::value);
static_assert(AZStd::Internal::is_fast_copy<int*, typename vector_type::iterator>::value);
static_assert(AZStd::Internal::is_fast_copy<typename string_type::iterator, char*>::value);
static_assert(AZStd::Internal::is_fast_copy<char*, typename string_type::iterator>::value);
static_assert(!AZStd::Internal::is_fast_copy<typename vector_type::iterator, typename list_type::iterator>::value);
}
TEST(CreateDestroy, IsFastFillTraits_TrueForContiguousIteratorTypes)
{
using list_type = AZStd::list<int>;
using vector_type = AZStd::vector<int>;
using string_type = AZStd::string;
using fixed_string_type = AZStd::fixed_string<128>;
static_assert(!AZStd::Internal::is_fast_fill<typename list_type::iterator>::value);
static_assert(!AZStd::Internal::is_fast_fill<typename vector_type::iterator>::value);
// Fast fill requires the type to be of size 1
static_assert(AZStd::Internal::is_fast_fill<const char*>::value);
static_assert(AZStd::Internal::is_fast_fill<typename string_type::iterator>::value);
static_assert(AZStd::Internal::is_fast_fill<typename fixed_string_type::iterator>::value);
}
TEST(CreateDestroy, ConstructAt_IsAbleToConstructReferenceTypes_Success)
{
struct TestConstructAt
{
TestConstructAt(int& intRef, float&& floatRef)
: m_intRef(intRef)
, m_floatValue(floatRef)
{}
int& m_intRef;
float m_floatValue;
};
int testValue = 32;
AZStd::aligned_storage_for_t<TestConstructAt> constructStorage;
auto constructAddress = &reinterpret_cast<TestConstructAt&>(constructStorage);
auto resultAddress = AZStd::construct_at(constructAddress, testValue, 4.0f);
resultAddress->m_intRef = 22;
EXPECT_EQ(22, testValue);
EXPECT_FLOAT_EQ(4.0f, resultAddress->m_floatValue);
AZStd::destroy_at(resultAddress);
}
}
@@ -0,0 +1,676 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/stack.h>
#include <AzCore/std/containers/ring_buffer.h>
#include <AzCore/std/allocator_static.h>
#include <AzCore/std/allocator_ref.h>
using namespace AZStd;
using namespace UnitTestInternal;
#define AZ_TEST_VALIDATE_EMPTY_DEQUE(_Deque) \
AZ_TEST_ASSERT(_Deque.validate()); \
AZ_TEST_ASSERT(_Deque.size() == 0); \
AZ_TEST_ASSERT(_Deque.empty()); \
AZ_TEST_ASSERT(_Deque.begin() == _Deque.end());
#define AZ_TEST_VALIDATE_DEQUE(_Deque, _NumElements) \
AZ_TEST_ASSERT(_Deque.validate()); \
AZ_TEST_ASSERT(_Deque.size() == _NumElements); \
AZ_TEST_ASSERT((_NumElements > 0) ? !_Deque.empty() : _Deque.empty()); \
AZ_TEST_ASSERT((_NumElements > 0) ? _Deque.begin() != _Deque.end() : _Deque.begin() == _Deque.end());
namespace UnitTest
{
class Containers
: public AllocatorsFixture
{
};
/**
* Deque container test.
*/
TEST_F(Containers, Deque)
{
// DequeContainerTest-Begin
typedef deque<int> int_deque_type;
int_deque_type int_deque;
AZ_TEST_VALIDATE_EMPTY_DEQUE(int_deque);
int_deque_type int_deque1(10);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 10);
int_deque_type int_deque2(6, 101);
AZ_TEST_VALIDATE_DEQUE(int_deque2, 6);
AZ_TEST_ASSERT(int_deque2.front() == 101);
AZ_TEST_ASSERT(int_deque2.back() == 101);
int_deque_type int_deque3(int_deque1);
AZ_TEST_VALIDATE_DEQUE(int_deque3, int_deque1.size());
AZ_TEST_ASSERT(int_deque3 == int_deque1);
AZ_TEST_ASSERT(int_deque3 != int_deque2);
int_deque_type int_deque4(int_deque2.begin(), int_deque2.end());
AZ_TEST_VALIDATE_DEQUE(int_deque4, int_deque2.size());
AZ_TEST_ASSERT(int_deque4 == int_deque2);
AZ_TEST_ASSERT(int_deque4 != int_deque3);
// This one will force the map to grow, which is a different code path.
int_deque1.insert(int_deque1.end(), 10, 99);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 20);
AZ_TEST_ASSERT(int_deque1.back() == 99);
int_deque3 = int_deque2;
AZ_TEST_VALIDATE_DEQUE(int_deque3, int_deque2.size());
AZ_TEST_ASSERT(int_deque3 == int_deque2);
int_deque1.resize(30, 199);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 30);
AZ_TEST_ASSERT(int_deque1.back() == 199);
int_deque1.resize(40, 299);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 40);
AZ_TEST_ASSERT(int_deque1.at(29) == 199);
AZ_TEST_ASSERT(int_deque1.at(30) == 299);
for (int_deque_type::size_type i = 0; i < int_deque1.size(); ++i)
{
AZ_TEST_ASSERT(int_deque1.at(i) == int_deque1[i]);
}
AZ_TEST_ASSERT(int_deque2.front() == 101);
AZ_TEST_ASSERT(int_deque2.back() == 101);
int_deque2.push_front(11);
AZ_TEST_VALIDATE_DEQUE(int_deque2, 7);
AZ_TEST_ASSERT(int_deque2.front() == 11);
int_deque2.push_back(21);
AZ_TEST_VALIDATE_DEQUE(int_deque2, 8);
AZ_TEST_ASSERT(int_deque2.back() == 21);
int_deque2.pop_front();
AZ_TEST_VALIDATE_DEQUE(int_deque2, 7);
AZ_TEST_ASSERT(int_deque2.front() == 101);
int_deque2.pop_back();
AZ_TEST_VALIDATE_DEQUE(int_deque2, 6);
AZ_TEST_ASSERT(int_deque2.back() == 101);
int_deque1.assign(5, 333);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 5);
array<int, 7> elements = {
{1, 2, 3, 4, 5, 6, 7}
};
int_deque1.assign(elements.begin(), elements.end());
AZ_TEST_VALIDATE_DEQUE(int_deque1, 7);
int_deque1.insert(int_deque1.begin(), 101);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 8);
AZ_TEST_ASSERT(int_deque1.front() == 101);
int_deque1.insert(int_deque1.end(), 201);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 9);
AZ_TEST_ASSERT(int_deque1.back() == 201);
int_deque1.insert(next(int_deque1.begin(), 3), 301);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 10);
AZ_TEST_ASSERT(int_deque1[3] == 301);
int_deque1.insert(int_deque1.begin(), 2, 401);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 12);
AZ_TEST_ASSERT(int_deque1.front() == 401);
int_deque1.insert(int_deque1.end(), 3, 501);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 15);
AZ_TEST_ASSERT(int_deque1.back() == 501);
int_deque1.insert(next(int_deque1.begin(), 3), 5, 601);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 20);
AZ_TEST_ASSERT(int_deque1[3] == 601);
int_deque1.insert(int_deque1.begin(), elements.begin(), next(elements.begin()));
AZ_TEST_VALIDATE_DEQUE(int_deque1, 21);
AZ_TEST_ASSERT(int_deque1.front() == 1);
int_deque1.insert(int_deque1.end(), prev(elements.end()), elements.end());
AZ_TEST_VALIDATE_DEQUE(int_deque1, 22);
AZ_TEST_ASSERT(int_deque1.back() == 7);
int_deque1.insert(next(int_deque1.begin(), 3), elements.begin(), elements.end());
AZ_TEST_VALIDATE_DEQUE(int_deque1, 29);
AZ_TEST_ASSERT(int_deque1[3] == 1);
int_deque1.insert(int_deque1.begin(), { 42 });
AZ_TEST_VALIDATE_DEQUE(int_deque1, 30);
AZ_TEST_ASSERT(int_deque1.front() == 42);
int_deque1.insert(int_deque1.begin(), { 1, 1, 2, 3, 5, 8, 13 });
AZ_TEST_VALIDATE_DEQUE(int_deque1, 37);
AZ_TEST_ASSERT(int_deque1.front() == 1);
AZ_TEST_ASSERT(int_deque1[3] == 3);
AZ_TEST_START_TRACE_SUPPRESSION;
int_deque1.insert(int_deque1.begin(), {});
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 37);
int_deque1.erase(int_deque1.begin(), int_deque1.begin() + 8);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 29);
int_deque1.erase(int_deque1.begin());
AZ_TEST_VALIDATE_DEQUE(int_deque1, 28);
AZ_TEST_ASSERT(int_deque1.front() == 401);
int_deque1.erase(prev(int_deque1.end()));
AZ_TEST_VALIDATE_DEQUE(int_deque1, 27);
AZ_TEST_ASSERT(int_deque1.back() == 501);
int_deque1.erase(next(int_deque1.begin()), int_deque1.end());
AZ_TEST_VALIDATE_DEQUE(int_deque1, 1);
AZ_TEST_ASSERT(int_deque1.front() == 401);
int_deque1.swap(int_deque2);
AZ_TEST_VALIDATE_DEQUE(int_deque1, 6);
AZ_TEST_VALIDATE_DEQUE(int_deque2, 1);
AZ_TEST_ASSERT(int_deque1.front() == 101);
AZ_TEST_ASSERT(int_deque1.back() == 101);
AZ_TEST_ASSERT(int_deque2.front() == 401);
for (int_deque_type::iterator it = int_deque2.begin(); it != int_deque2.end(); ++it)
{
AZ_TEST_ASSERT(*it == 401);
}
for (int_deque_type::reverse_iterator rit = int_deque2.rbegin(); rit != int_deque2.rend(); ++rit)
{
AZ_TEST_ASSERT(*rit == 401);
}
// extensions
int_deque2.push_back();
AZ_TEST_VALIDATE_DEQUE(int_deque2, 2);
AZ_TEST_ASSERT(int_deque2.front() == 401);
int_deque2.push_front();
AZ_TEST_VALIDATE_DEQUE(int_deque2, 3);
AZ_TEST_ASSERT(int_deque2[1] == 401);
// alignment
deque<MyClass> aligned_deque(5, 99);
for (AZStd::size_t i = 0; i < aligned_deque.size(); ++i)
{
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_deque[i] & (alignment_of<MyClass>::value - 1)) == 0);
}
// different allocators
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB_type;
static_buffer_16KB_type myMemoryManager1;
static_buffer_16KB_type myMemoryManager2;
typedef allocator_ref<static_buffer_16KB_type> static_allocator_ref_type;
static_allocator_ref_type allocator1(myMemoryManager1, "Mystack allocator 1");
static_allocator_ref_type allocator2(myMemoryManager2, "Mystack allocator 2");
typedef deque<int, static_allocator_ref_type> int_deque_myalloc_type;
int_deque_myalloc_type int_deque10(100, 13, allocator1); /// Allocate 100 elements using memory manager 1
AZ_TEST_VALIDATE_DEQUE(int_deque10, 100);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 100 * sizeof(int));
// leak_and_reset
int_deque10.leak_and_reset(); /// leave the allocated memory and reset the vector.
AZ_TEST_VALIDATE_EMPTY_DEQUE(int_deque10);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 100 * sizeof(int));
myMemoryManager1.reset(); /// discard the memory
// allocate again from myMemoryManager1
int_deque10.resize(100, 15);
int_deque10.set_allocator(allocator2);
AZ_TEST_VALIDATE_DEQUE(int_deque10, 100);
// now we move the allocated size from menager1 to manager2 (without freeing menager1)
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() == myMemoryManager2.get_allocated_size());
myMemoryManager1.reset(); // flush manager 1 again (int_vector10 is stored in manager 2)
// swap with different allocators
int_deque_myalloc_type int_deque11(50, 25, allocator1); // create copy in manager1
AZ_TEST_VALIDATE_DEQUE(int_deque11, 50);
int_deque11.swap(int_deque10); // swap the vectors content (since the allocators are different)
AZ_TEST_VALIDATE_DEQUE(int_deque10, 50);
AZ_TEST_VALIDATE_DEQUE(int_deque11, 100);
AZ_TEST_ASSERT(int_deque11.front() == 15);
AZ_TEST_ASSERT(int_deque10.front() == 25);
//////////////////////////////////////////////////////////////////////////////////////////
// Test asserts (which don't cause throw exceptions)
//AZ_TEST_START_TRACE_SUPPRESSION;
//int_deque10.resize(1000000); // too many elements, 1 assert on too many, 1 assert on allocator returning NULL
//AZ_TEST_STOP_TRACE_SUPPRESSION(2);
#ifdef AZSTD_HAS_CHECKED_ITERATORS
int_deque.clear();
int_deque_type::iterator iter = int_deque.end();
// We have exeption when we access the map.
//AZ_TEST_START_TRACE_SUPPRESSION;
//int b = *iter; // the end if is valid but can not dereferenced
//int_deque.validate_iterator(iter);
//(void)b;
//AZ_TEST_STOP_TRACE_SUPPRESSION(1);
int_deque.push_back(1);
AZ_TEST_START_TRACE_SUPPRESSION;
int_deque.validate_iterator(iter); // The push back should make the end iterator invalid.
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
iter = int_deque.begin();
int_deque.clear();
AZ_TEST_START_TRACE_SUPPRESSION;
int_deque.validate_iterator(iter); // The clear should invalidate all iterators
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
#endif
aligned_deque.emplace_back(10, true, 2.0f);
// DequeContainerTest-End
}
/**
* Queue container test.
*/
TEST_F(Containers, Queue)
{
// QueueContainerTest-Begin
typedef queue<int> int_queue_type;
int_queue_type int_queue;
AZ_TEST_ASSERT(int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 0);
// Queue uses deque as default container, so try to contruct to queue from a deque.
deque<int> container(40, 10);
int_queue_type int_queue2(container);
AZ_TEST_ASSERT(!int_queue2.empty());
AZ_TEST_ASSERT(int_queue2.size() == 40);
int_queue.push(10);
AZ_TEST_ASSERT(!int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 1);
AZ_TEST_ASSERT(int_queue.front() == int_queue.back());
AZ_TEST_ASSERT(int_queue.front() == 10);
int_queue.pop();
AZ_TEST_ASSERT(int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 0);
int_queue2.push(20);
AZ_TEST_ASSERT(!int_queue2.empty());
AZ_TEST_ASSERT(int_queue2.size() == 41);
AZ_TEST_ASSERT(int_queue2.back() == 20);
int_queue2.pop();
AZ_TEST_ASSERT(!int_queue2.empty());
AZ_TEST_ASSERT(int_queue2.size() == 40);
AZ_TEST_ASSERT(int_queue2.back() == 20);
int_queue.push();
AZ_TEST_ASSERT(!int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 1);
// Test Swap
int_queue.swap(int_queue2);
AZ_TEST_ASSERT(!int_queue2.empty());
AZ_TEST_ASSERT(int_queue2.size() == 1);
AZ_TEST_ASSERT(!int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 40);
AZ_TEST_ASSERT(int_queue.back() == 20);
queue<MyClass> class_queue;
class_queue.emplace(3, false, 1.0f);
// QueueContainerTest-End
}
/**
* Priority queue container test.
*/
TEST_F(Containers, PriorityQueue)
{
// PriorityQueueContainerTest-Begin
typedef priority_queue<int> int_priority_queue_type;
int_priority_queue_type int_queue;
AZ_TEST_ASSERT(int_queue.empty());
AZ_TEST_ASSERT(int_queue.size() == 0);
array<int, 10> elements = {
{10, 2, 6, 3, 5, 8, 7, 9, 1, 4}
};
int_priority_queue_type int_queue2(elements.begin(), elements.end());
AZ_TEST_ASSERT(!int_queue2.empty());
AZ_TEST_ASSERT(int_queue2.size() == 10);
int lastValue = 11;
while (!int_queue2.empty())
{
AZ_TEST_ASSERT(int_queue2.top() < lastValue);
lastValue = int_queue2.top();
int_queue2.pop();
}
AZ_TEST_ASSERT(int_queue2.size() == 0);
priority_queue<int, vector<int>, AZStd::greater<int> > int_queue3(elements.begin(), elements.end());
AZ_TEST_ASSERT(!int_queue3.empty());
AZ_TEST_ASSERT(int_queue3.size() == 10);
lastValue = 0;
while (!int_queue3.empty())
{
AZ_TEST_ASSERT(int_queue3.top() > lastValue);
lastValue = int_queue3.top();
int_queue3.pop();
}
AZ_TEST_ASSERT(int_queue3.size() == 0);
int_priority_queue_type int_queue4(elements.begin(), elements.end());
int_queue4.push(100);
AZ_TEST_ASSERT(!int_queue4.empty());
AZ_TEST_ASSERT(int_queue4.size() == 11);
AZ_TEST_ASSERT(int_queue4.top() == 100);
// PriorityQueueContainerTest-End
}
/**
* Stack container test.
*/
TEST_F(Containers, Stack)
{
// StackContainerTest-Begin
typedef stack<int> int_stack_type;
int_stack_type int_stack;
AZ_TEST_ASSERT(int_stack.empty());
AZ_TEST_ASSERT(int_stack.size() == 0);
deque<int> container(40, 10);
int_stack_type int_stack2(container);
AZ_TEST_ASSERT(!int_stack2.empty());
AZ_TEST_ASSERT(int_stack2.size() == 40);
int_stack.push(20);
AZ_TEST_ASSERT(!int_stack.empty());
AZ_TEST_ASSERT(int_stack.size() == 1);
AZ_TEST_ASSERT(int_stack.top() == 20);
int_stack.pop();
AZ_TEST_ASSERT(int_stack.empty());
AZ_TEST_ASSERT(int_stack.size() == 0);
int_stack2.push(20);
AZ_TEST_ASSERT(!int_stack2.empty());
AZ_TEST_ASSERT(int_stack2.size() == 41);
AZ_TEST_ASSERT(int_stack2.top() == 20);
int_stack2.pop();
AZ_TEST_ASSERT(!int_stack2.empty());
AZ_TEST_ASSERT(int_stack2.size() == 40);
AZ_TEST_ASSERT(int_stack2.top() == 10);
int_stack.push();
AZ_TEST_ASSERT(!int_stack.empty());
AZ_TEST_ASSERT(int_stack.size() == 1);
// StackContainerTest-End
}
/**
* Make sure a ring_buffer is empty, and control all functions to return the proper values.
* Empty ring_buffer as all AZStd containers should not have allocated any memory. Empty and clean containers are not the same.
*/
#define AZ_TEST_VALIDATE_EMPTY_RINGBUFFER(_RingBuffer) \
AZ_TEST_ASSERT(_RingBuffer.validate()); \
AZ_TEST_ASSERT(_RingBuffer.size() == 0); \
AZ_TEST_ASSERT(_RingBuffer.empty()); \
AZ_TEST_ASSERT(_RingBuffer.capacity() == 0); \
AZ_TEST_ASSERT(_RingBuffer.begin() == _RingBuffer.end());
/**
* Validate a ring_buffer for certain number of elements.
*/
#define AZ_TEST_VALIDATE_RINGBUFFER(_RingBuffer, _NumElements) \
AZ_TEST_ASSERT(_RingBuffer.validate()); \
AZ_TEST_ASSERT(_RingBuffer.size() == _NumElements); \
AZ_TEST_ASSERT((_NumElements > 0) ? !_RingBuffer.empty() : _RingBuffer.empty()); \
AZ_TEST_ASSERT((_NumElements > 0) ? _RingBuffer.capacity() >= _NumElements : true); \
AZ_TEST_ASSERT((_NumElements > 0) ? _RingBuffer.begin() != _RingBuffer.end() : _RingBuffer.begin() == _RingBuffer.end());
/**
* ring_buffer container test.
*/
TEST_F(Containers, RingBuffer)
{
typedef ring_buffer<int> int_ringbuffer_type;
typedef ring_buffer<MyClass> class_ringbuffer_type;
// Test empty buffer with intergral type.
int_ringbuffer_type int_buffer;
AZ_TEST_VALIDATE_EMPTY_RINGBUFFER(int_buffer);
// Default vector (non-integral type).
class_ringbuffer_type myclass_buffer;
AZ_TEST_VALIDATE_EMPTY_RINGBUFFER(myclass_buffer);
// Allocate buffer with capacity of 10 elements.
int_ringbuffer_type int_buffer1(10);
AZ_TEST_ASSERT(int_buffer1.size() == 0);
AZ_TEST_ASSERT(int_buffer1.capacity() == 10);
AZ_TEST_ASSERT(int_buffer1.empty());
AZ_TEST_ASSERT(int_buffer1.begin() == int_buffer1.end());
// Allocate buffer with 15 elements init to 13.
int_ringbuffer_type int_buffer2(15, 13);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer2, 15);
for (int_ringbuffer_type::iterator iter = int_buffer2.begin(); iter != int_buffer2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 13);
}
// Allocate buffer with 15 elements init to 13 and a capacity 31.
int_ringbuffer_type int_buffer3(31, 15, 13);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer3, 15);
AZ_TEST_ASSERT(int_buffer3.capacity() == 31);
for (int_ringbuffer_type::iterator iter = int_buffer3.begin(); iter != int_buffer3.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 13);
}
// Copy ctor
int_ringbuffer_type int_buffer4(int_buffer3);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer4, 15);
AZ_TEST_ASSERT(int_buffer4.capacity() == 31);
for (int_ringbuffer_type::iterator iter = int_buffer4.begin(); iter != int_buffer4.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 13);
}
// Test == and !=
AZ_TEST_ASSERT(int_buffer4 == int_buffer3);
AZ_TEST_ASSERT((int_buffer4 != int_buffer3) == false);
AZStd::array<int, 6> myArr = {
{0, 1, 2, 3, 4, 5}
};
int_ringbuffer_type int_buffer5(myArr.begin(), myArr.end());
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size());
int i = 0;
for (int_ringbuffer_type::iterator iter = int_buffer5.begin(); iter != int_buffer5.end(); ++iter, ++i)
{
AZ_TEST_ASSERT(*iter == i);
}
int_ringbuffer_type int_buffer6(10, myArr.begin(), myArr.end());
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer6, myArr.size());
AZ_TEST_ASSERT(int_buffer6.capacity() == 10);
i = 0;
for (int_ringbuffer_type::iterator iter = int_buffer6.begin(); iter != int_buffer6.end(); ++iter, ++i)
{
AZ_TEST_ASSERT(*iter == i);
}
// =
int_buffer1 = int_buffer6;
AZ_TEST_ASSERT(int_buffer1 == int_buffer6);
// []
AZ_TEST_ASSERT(int_buffer5[3] == 3);
AZ_TEST_ASSERT(int_buffer5[4] == int_buffer5.at(4));
AZ_TEST_ASSERT(int_buffer5.front() == 0);
AZ_TEST_ASSERT(int_buffer5.back() == 5);
// full
AZ_TEST_ASSERT(int_buffer5.full() == true);
AZ_TEST_ASSERT(int_buffer6.full() == false);
// Circular checks
AZ_TEST_ASSERT(int_buffer5.is_linearized() == true);
int_ringbuffer_type::array_range arr1 = int_buffer5.array_one();
int_ringbuffer_type::array_range arr2 = int_buffer5.array_two();
AZ_TEST_ASSERT(arr1.second == int_buffer5.size()); // we have only 1 linear array
AZ_TEST_ASSERT(arr2.second == 0);
AZ_TEST_ASSERT(*arr1.first == 0); // Check that we are pointing to the first elements, which is 0.
// Overwrite the first 2 elements.
int_buffer5.push_back(6);
int_buffer5.push_back(7);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size());
AZ_TEST_ASSERT(int_buffer5.front() == 2);
AZ_TEST_ASSERT(int_buffer5.back() == 7);
arr1 = int_buffer5.array_one();
arr2 = int_buffer5.array_two();
AZ_TEST_ASSERT(arr1.second == 4);
AZ_TEST_ASSERT(*arr1.first == 2);
AZ_TEST_ASSERT(arr2.second == 2);
AZ_TEST_ASSERT(*arr2.first == 6);
// rotate - full buffer
int_buffer5.rotate(int_buffer5.begin() + 1); // rotate right by 1
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size());
AZ_TEST_ASSERT(int_buffer5.front() == 3);
AZ_TEST_ASSERT(int_buffer5.back() == 2);
arr1 = int_buffer5.array_one();
arr2 = int_buffer5.array_two();
AZ_TEST_ASSERT(arr1.second == 3);
AZ_TEST_ASSERT(*arr1.first == 3);
AZ_TEST_ASSERT(arr2.second == 3);
AZ_TEST_ASSERT(*arr2.first == 6);
// rotate - !full buffer
int_buffer6.rotate(int_buffer6.begin() + 5);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer6, myArr.size());
AZ_TEST_ASSERT(int_buffer6.front() == 5);
AZ_TEST_ASSERT(int_buffer6.back() == 4);
arr1 = int_buffer6.array_one();
arr2 = int_buffer6.array_two();
AZ_TEST_ASSERT(arr1.second == 1);
AZ_TEST_ASSERT(*arr1.first == 5);
AZ_TEST_ASSERT(arr2.second == 5);
AZ_TEST_ASSERT(*arr2.first == 0);
// linearize
int_buffer5.linearize();
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size());
AZ_TEST_ASSERT(int_buffer5.is_linearized());
AZ_TEST_ASSERT(int_buffer5.front() == 3);
AZ_TEST_ASSERT(int_buffer5.back() == 2);
arr1 = int_buffer5.array_one();
arr2 = int_buffer5.array_two();
AZ_TEST_ASSERT(arr1.second == 6);
AZ_TEST_ASSERT(*arr1.first == 3);
AZ_TEST_ASSERT(arr2.second == 0);
// resize - grow
int_buffer5.resize(100, 11);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, 100);
AZ_TEST_ASSERT(int_buffer5.front() == 3);
AZ_TEST_ASSERT(int_buffer5.back() == 11);
// resize - shrink
int_buffer5.resize(5);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, 5);
AZ_TEST_ASSERT(int_buffer5.front() == 3);
AZ_TEST_ASSERT(int_buffer5.back() == 7);
// swap
int_buffer5.swap(int_buffer6);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer6, 5);
AZ_TEST_ASSERT(int_buffer6.front() == 3);
AZ_TEST_ASSERT(int_buffer6.back() == 7);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size());
AZ_TEST_ASSERT(int_buffer5.front() == 5);
AZ_TEST_ASSERT(int_buffer5.back() == 4);
// push
int_buffer5.push_back(101);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 1);
AZ_TEST_ASSERT(int_buffer5.back() == 101);
int_buffer5.push_back();
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 2);
int_buffer5.push_front(201);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 3);
AZ_TEST_ASSERT(int_buffer5.front() == 201);
int_buffer5.push_front();
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 4);
// pop
int_buffer5.pop_front();
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 3);
AZ_TEST_ASSERT(int_buffer5.front() == 201);
int_buffer5.pop_back();
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 2);
AZ_TEST_ASSERT(int_buffer5.back() == 101);
// insert
int_buffer5.insert(int_buffer5.begin() + 1, 303);
AZ_TEST_VALIDATE_RINGBUFFER(int_buffer5, myArr.size() + 3);
AZ_TEST_ASSERT(int_buffer5[0] == 201);
AZ_TEST_ASSERT(int_buffer5[1] == 303);
/* int_buffer5.insert(int_buffer5.begin(),3,404);
AZ_TEST_ASSERT(int_buffer5[0]==404);
AZ_TEST_ASSERT(int_buffer5[3]==201);*/
// erase
int_buffer5.erase(int_buffer5.begin() + 1);
AZ_TEST_ASSERT(int_buffer5[0] == 201);
}
TEST_F(Containers, RingBufferReverseIterators)
{
using int_ringbuffer_type = AZStd::ring_buffer<int>;
const int max = 42;
int_ringbuffer_type rev_buffer(max);
for (int i = 0; i < max; ++i)
{
rev_buffer.push_back(i);
}
int iteration = 0;
for (int_ringbuffer_type::const_reverse_iterator rit = rev_buffer.rbegin(); rit != rev_buffer.rend(); ++rit)
{
EXPECT_EQ(max - iteration - 1, *rit);
++iteration;
}
}
}
@@ -0,0 +1,568 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/typetraits/typetraits.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/semaphore.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/deque.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/fixed_list.h>
#include <AzCore/std/containers/intrusive_list.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/stack.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
class TypeTraitExamples
{
public:
void run()
{
// TypeTraitExample-Begin
// Checks the alignment of different types.
AZ_TEST_STATIC_ASSERT(alignment_of<int>::value == 4);
AZ_TEST_STATIC_ASSERT(alignment_of<char>::value == 1);
AZ_TEST_STATIC_ASSERT(alignment_of<MyClass>::value == 16);
// aligned_storage example
// Create an int type aligned on 32 bytes.
typedef aligned_storage<sizeof(int), 32>::type int_aligned32_type;
AZ_TEST_STATIC_ASSERT((alignment_of<int_aligned32_type>::value) == 32);
// aligned_storage example
// Declare a buffer of 100 bytes, aligned on 16 bytes. (don't use more than 16 bytes alignment on the stack. It doesn't work on all platforms.)
typedef aligned_storage<100, 16>::type buffer100_16alinged_type;
// Check that our type is aligned on 16 bytes
AZ_TEST_STATIC_ASSERT((alignment_of< buffer100_16alinged_type>::value) == 16);
buffer100_16alinged_type myAlignedBuffer;
// Make sure the buffer pointer is aligned to 16 bytes.
AZ_TEST_ASSERT((AZStd::size_t(&myAlignedBuffer) & 15) == 0);
// POD
// Checks if a type is POD (Plain Old Data).
AZ_TEST_STATIC_ASSERT(is_pod<MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_pod<MyClass>::value == false);
// TypeTraitExample-End
}
};
TEST(Allocator, Examples)
{
// AllocatorExamples-Begin
// Sharing allocator between containers
{
// I will use a 16 KB static_buffer_allocator (on the stack) for this sample. You can use any of your own allocators
// unless they don't already point to your memory manager (which is the common way to use STL allocators)
typedef static_buffer_allocator<16*1024, 16> static_buffer_16KB_aligned16;
static_buffer_16KB_aligned16 bufferAllocator;
typedef allocator_ref<static_buffer_16KB_aligned16> static_buffer_16KB_aligned16_ref;
static_buffer_16KB_aligned16_ref sharedAllocator(bufferAllocator);
// All containers will allocator from the same buffer. Here it is not
// important that we will never actually free the data because of the static_buffer_allocator.
// But if we consider that fact this is great example for some temporary containers, when
// we don't want to even involve any memory managers.
vector<int, static_buffer_16KB_aligned16_ref> int_vector(sharedAllocator);
list<float, static_buffer_16KB_aligned16_ref> float_list(sharedAllocator);
deque<MyClass, static_buffer_16KB_aligned16_ref> myclass_deque(sharedAllocator);
}
// AllocatorExamples-End
}
class ContainersExamples
: public AllocatorsFixture
{
public:
void Array()
{
// ArrayExamples-Begin
// Array class is like a regular C array, but if gives it container functionality.
// All elements are initialized, when the array is created.
// Create an array of 5 ints. All elements will be uninitialized (they will call the default ctor)
array<int, 5> int10_uninit_array;
// Array of 5 ints initialized to some values.
array<int, 5> int10_init_array = {
{1, 2, 3, 4, 5}
};
// just check if the first element is 1 and last is 5
AZ_TEST_ASSERT(int10_init_array[0] == 1);
AZ_TEST_ASSERT(int10_init_array[4] == 5);
// set all elements to 11
int10_init_array.fill(11);
AZ_TEST_ASSERT(int10_init_array[0] == 11);
AZ_TEST_ASSERT(int10_init_array[4] == 11);
// Create an array of my class with default init.
array<MyClass, 5> myclass_array;
// default value for MyClass::m_data is 10, verify this.
AZ_TEST_ASSERT(myclass_array[0].m_data == 10);
AZ_TEST_ASSERT(myclass_array[4].m_data == 10);
// My class pointer should be aligned on 32 bytes so verify this too.
AZ_TEST_ASSERT((((AZStd::size_t)&myclass_array[0]) & (alignment_of<MyClass>::value - 1)) == 0);
// ArrayExamples-End
(void)int10_uninit_array;
}
void Vector()
{
// VectorExamples-Begin
// Int vector using the default allocator
typedef vector<int> int_vector_type;
// 100 constant elements.
{
// Bad way, lot's of allocations and slow
int_vector_type int_vec1;
for (int i = 0; i < 100; ++i)
{
int_vec1.push_back(10);
}
// Correct ways...
int_vector_type int_vec2(100, 10); // Best way
int_vec1.resize(100, 10); // Similar with a few more function calls
int_vec1.assign(100, 10); // Similar with even more function calls
}
// 100 random values (0 to 99 in this example)
{
// Bad way, lot's of allocation and slow
int_vector_type int_vec1;
for (int i = 0; i < 100; ++i)
{
int_vec1.push_back(i);
}
// Bad way, one allocation but pointless copies
int_vec1.resize(100, 0 /*reset to 0*/); // one allocation, but sets all the values to 0
for (int i = 0; i < 100; ++i)
{
int_vec1[i] = i;
}
// Bad because it's tricky (sometimes correct)
int_vec1.resize(100); // AZStd extension. This will work fast only for POD data types (like this int is), otherwise it will call default ctor.
for (int i = 0; i < 100; ++i)
{
int_vec1[i] = i;
}
// Correct way
int_vec1.reserve(100); // as part of the standard or you can use AZStd extension set_capacity(), this will trim is down if necessary
for (int i = 0; i < 100; ++i)
{
int_vec1.push_back(i);
}
}
// Copy values from other containers
{
int_vector_type int_vec1(100, 10);
int_vector_type int_vec2;
// Bad ways
// Slow with many allocations
for (int_vector_type::size_type i = 0; i < int_vec1.size(); ++i)
{
int_vec2.push_back(int_vec1[i]);
}
// Correct if it's the same type
int_vector_type int_vec3(int_vec1);
int_vector_type int_vec4 = int_vec1;
// Correct from different types
list<int> int_list(10, 10);
array<int, 4> int_array = {
{1, 2, 3, 4}
};
// C array
int int_carray[] = {1, 2, 3, 4};
int_vector_type int_vec5(int_list.begin(), int_list.end());
int_vector_type int_vec6(int_array.begin(), int_array.end());
int_vector_type int_vec7(&int_carray[0], &int_carray[4]);
}
//
// As you know from STL avoid using insert and erase on a vector, since they are slow operations.
//
// Clearing a container
{
int_vector_type int_vec1(100, 55); // 100 elements, 55 value
// Bad way
while (!int_vec1.empty())
{
int_vec1.pop_back();
}
// Correct ways
int_vec1.clear();
int_vec1.erase(int_vec1.begin(), int_vec1.end()); // a few more function calls than clear
// If you want to clear and make sure we free the memory.
int_vec1.set_capacity(0); // AZStd extension.
}
// Exchanging the content of 2 vectors
{
int_vector_type int_vec1(100, 10);
int_vector_type int_vec2(10, 11);
// Only one way is correct, everything else is BAD (even if the allocators are different, it will do the proper job as fast as possible).
int_vec1.swap(int_vec2);
}
// Quick tear-down (leak_and_reset extension)
{
// Assuming you have your own temporary allocators, I will use static_buffer_allocator for this sample.
// \note this allocator already instruct the vector the he doesn't need to delete it's memory
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB;
// Add 100 elements on the stack
// and YES having fixed_vector<int, (16*1024)/sizeof(T) > is the same.
vector<int, static_buffer_16KB> tempVector(100, 10);
// .. do some crazy operations, sorting whatever...
// clearing (when the can afford to NOT call the destructor - it will not leak or something), otherwise just use the regular functions.
// Bad ways, although all the bad way will be work as fast for POD data types, we consider them tricky, because you rely on the vector value_type.
tempVector.clear(); // even it will not free any memory, it will call if the type is not POD.
tempVector.erase(tempVector.begin(), tempVector.end());
tempVector.set_capacity(0);
// Correct way to NOT call the dtor.
// IMPORTANT: Leak and reset can be used on normal vectors too for instance if you have garbage collector, you are
// just exiting the process and rely on somebody else to clean after you.
tempVector.leak_and_reset();
}
// Allocators
{
// I will use static_buffer_allocator for this sample.
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB;
static_buffer_16KB otherAllocator("Other allocator");
// change allocator name
// All of this depends if your allocator assignment is slow/expensive. Otherwise this is valid code
vector<int, static_buffer_16KB> int_vec1(100, 10, static_buffer_16KB("New Allocator Name"));
// If assignment is slow, we can do this. This works on the instance of the allocator.
int_vec1.get_allocator().set_name("New Name");
// Changing the allocator, will force the vector to re-allocate itself, if it has any elements.
int_vec1.set_allocator(otherAllocator);
// As in the allocators sample, you can share an allocator if you the allocator_ref.
typedef allocator_ref<static_buffer_16KB> static_buffer_16KB_ref;
// Both int_vec2 and int_vec3 will allocate from the otherAllocator.
static_buffer_16KB_ref sharedAlloc(otherAllocator);
vector<int, static_buffer_16KB_ref> int_vec2(sharedAlloc);
vector<int, static_buffer_16KB_ref> int_vec3(sharedAlloc);
// using the container allocator, for other purpose... allocate 100 bytes on 16 byte alignment
void* myData = int_vec2.get_allocator().allocate(100, 16);
// do something...
// free if you should, in the static_buffer_allocator you should not care about this.
int_vec2.get_allocator().deallocate(myData, 100, 16);
}
// VectorExamples-End
}
void List()
{
// ListExamples-Begin
// Use the list node type to pre allocate memory pools.
{
// One of the futures of the AZStd containers, that we node allocation type for each container (not only the list).
// This allows us to know at compile time the size of the allocations (vector class is exception).
// This example if very similar to what this fixed_list container does.
// Some let's create pool for int list nodes.
typedef static_pool_allocator< list<int>::node_type, 1000 > int_list_pool_allocator_type;
typedef allocator_ref<int_list_pool_allocator_type> int_pool_alloc_ref_type;
int_list_pool_allocator_type myPool;
int_pool_alloc_ref_type myPoolRef(myPool, "My list<int>::note_type allocator!");
// Now we want to share that pool in multiple containers.
list<int, int_pool_alloc_ref_type> int_list(myPoolRef);
list<int, int_pool_alloc_ref_type> int_list1(myPoolRef);
// in addition we can use the pool to allocate nodes that a smaller than the int type.
list<char, int_pool_alloc_ref_type> char_list(myPoolRef);
list<short, int_pool_alloc_ref_type> short_list(myPoolRef);
// Now all of the above containers will allocate from the list<int> pool.
int_list.assign(10, 202);
AZ_TEST_ASSERT(int_list.size() == 10);
AZ_TEST_ASSERT(int_list.front() == 202);
int_list1.assign(10, 302);
AZ_TEST_ASSERT(int_list1.size() == 10);
AZ_TEST_ASSERT(int_list1.front() == 302);
char_list.assign(30, (char)120);
AZ_TEST_ASSERT(char_list.size() == 30);
AZ_TEST_ASSERT(char_list.front() == 120);
short_list.assign(20, (short)32000);
AZ_TEST_ASSERT(short_list.size() == 20);
AZ_TEST_ASSERT(short_list.front() == 32000);
// Now after we did some work with the containers, we can tear them down faster. Which is another example of the
// use of leak_and_reset.
// If you look at the static_pool_allocator allocator, you will notice that the deallocate function returns the allocated node
// to the pool. At this moment we don't really want to do that since we will not use this pool anymore and the memory will be free once we
// destroy the pool. On the other hand we use integral type (which is POD types) and we don't need to worry about the destructor at all... so to be fast
// instead of deallocating each node on it's own.
int_list.leak_and_reset();
int_list1.leak_and_reset();
char_list.leak_and_reset();
short_list.leak_and_reset();
myPool.leak_before_destroy(); // tell the pool it's ok that we have allocated nodes.
}
// ListExamples-End
}
void Deque()
{
// DequeExamples-Begin
// Customize the deque so it fits better our allocation needs
{
// Specialize the deque so we do allocate 20 int at in a block. If you look at the default
// settings you can see for 4 byte types we will allocate blocks with 4 elements. 20 can be a little wasteful,
// but a lot lass allocations will happen.
deque<int, AZStd::allocator, 20> int_deque;
int_deque.push_back(10);
int_deque.push_front(11);
AZ_TEST_ASSERT(int_deque.size() == 2);
AZ_TEST_ASSERT(int_deque.front() == 11);
AZ_TEST_ASSERT(int_deque.back() == 10);
}
// DequeExamples-End
}
void Hashed()
{
#ifdef AZ_PLATFORM_WINDOWS // Just to make sure examples actually work
// UnorderedExamples-Begin
// Advanced examples. This examples may be a little hard to read or understand if you are familiar with
// way Hashed containers work. Read about hash_table and check the papers references.
// Keep in mind that this customizations and speed ups should be used if you really know what they do, and
// you really need it!
// You should try to be complaint with the standard wherever possible, to avoid problems
// if you use other STL. In 99% of the cases copying keys and value types is very fast or doesn't happed as often at all.
// So using this you will make the code look more complicated and not compatible with the standard for no any real benefit.
// Of course if you use these containers in rendering code for example and you insert hundreds (or even thousands) entries every frame,
// these examples might help a lot.
//////////////////////////////////////////////////////////////////////////
// Some example classes we use
struct MyExpensiveKeyType
{
MyExpensiveKeyType()
: m_keyData(0) { /* expensive operations */ }
MyExpensiveKeyType(int data)
: m_keyData(data) { /* expensive operations */ }
AZStd::size_t GetHashed() const { return m_keyData; /* just some hashing function */ }
bool IsEqual(const MyExpensiveKeyType& rhs) const { return m_keyData == rhs.m_keyData; }
int m_keyData;
};
// KeyHasher
struct MyExpensiveKeyHasher
{
AZStd::size_t operator()(const MyExpensiveKeyType& k) const
{
return k.GetHashed();
}
};
// KeyTypeCompare
struct MyExpensiveKeyEqualTo
{
AZ_FORCE_INLINE bool operator()(const MyExpensiveKeyType& left, const MyExpensiveKeyType& right) const { return left.IsEqual(right); }
// We use this class to compare the AZStd::size_t to our key type.
AZ_FORCE_INLINE bool operator()(const AZStd::size_t leftKey, const MyExpensiveKeyType& right) const { return (int)leftKey == right.m_keyData; }
};
// Map expensive value type.
class MyExpensiveValueType
{
public:
MyExpensiveValueType()
: m_data(0) { /* expensive operations */ }
MyExpensiveValueType(int data)
: m_data(data) { /* expensive operations */ }
private:
int m_data;
};
//////////////////////////////////////////////////////////////////////////
// Customization for expensive value type.
{
// Let's say the MyExpensiveValueType is expensive to construct. Ex. Does allocations register itself in systems, etc.
// so if we have the map with it.
// - In many cases people try to avoid this by storing pointer in the container
// so the value type is not expensive to move around, but even if this works. It's not the idea of the container.
typedef unordered_map<int, MyExpensiveValueType> myclass_map_type;
myclass_map_type myMap;
int myNewClassKey = 100;
// So to use the regular insert, we need to create temp pair. Even if the key exists, or we don't really have a source for
// for MyClass. Then this insert can be a big overhead.
// - People sometimes try to fix this problem by calling myMap.find(myNewClassKey) to see if it's there and then make
// the pair, this is ok. But you do the find 2 times... if it's not there. Even time is constant time operation O(1) it's pointless.
myclass_map_type::value_type tempPair = AZStd::make_pair(myNewClassKey, MyExpensiveValueType());
myMap.insert(tempPair);
// When we don't have MyClass source value, a call to the extension insert_key() will to the job.
myMap.insert_key(myNewClassKey); // So if the key doesn't't exist it will insert pair with default value. Which is g).
}
// Customization for expensive key type
{
// Like the example with word counting... in the 14CrazyIdeas paper. Sometimes the key can be expensive.
typedef unordered_set<MyExpensiveKeyType, MyExpensiveKeyHasher, MyExpensiveKeyEqualTo> myclass_set_type;
myclass_set_type mySet;
// Let say as in the above example MyClass is expensive to construct, copy, etc. But we know can know the search key (we use hash
// values for the unordered set).
AZStd::size_t myNewClassKey = 101; // So we know the key (that hash_function(MyClass) will return, a good practical example is when you have string literal and you can get the hash without making string object)
// This way you don't need to make the expensive MyClass object at all. This the situation is the same for maps. We need
// to provide functions how to compare AZStd::size_t (in this case) to MyExpensiveKeyType (in this case). Which MyExpensiveKeyEqualTo class does.
mySet.find_as(myNewClassKey, AZStd::hash<AZStd::size_t>(), MyExpensiveKeyEqualTo());
}
// Customization for expensive key or value with non default ctor
{
// This is most complicated of advanced example. It shows how to customize the insert function for both complex key and complex value types.
typedef unordered_map<MyExpensiveKeyType, MyExpensiveValueType, MyExpensiveKeyHasher, MyExpensiveKeyEqualTo> expensive_map_type;
// So again for the sake of discussion let's say MyExpensiveValueType is expensive to construct and the key MyExpensiveKeyType is expensive too.
// As we saw from prev examples we can compare they key quickly to AZStd::size_t if we know the comparable to key, without making the expensive key
// class. But this time if they key is not found we you like to construct the expensive value type with an input (non default)... so we have the fallowinf
// quick insert struct...
struct QuickInsert
{
AZStd::size_t m_comparebleToKey;
int m_keyInput;
int m_valueInput;
};
// Then we need a converter class that will convert form this struct to the map key and value
struct Converter
{
typedef AZStd::size_t key_type; // required because we might use the Map::key_type or Comparable to Key type.
const key_type& to_key(const QuickInsert& qi) const { return qi.m_comparebleToKey; }
expensive_map_type::value_type to_value(const QuickInsert& qi) const
{
// This is the place where the expensive ctors will be called only of really necessary
return AZStd::make_pair(MyExpensiveKeyType(qi.m_keyInput), MyExpensiveValueType(qi.m_valueInput));
}
};
Converter convQuickInsertToMapType;
expensive_map_type myMap;
QuickInsert qi;
qi.m_comparebleToKey = 10;
qi.m_keyInput = 100; ///< Input for ctors or whatever
qi.m_valueInput = 200; ///< Input for ctors or whatever
myMap.insert_from(qi, convQuickInsertToMapType, AZStd::hash<AZStd::size_t>() /*hasher for the comparable to key*/, MyExpensiveKeyEqualTo());
// And that's about it, this way you will do a fast (find_as like) compare without constructing the key, if doesn't exist, the insert
// structure (QuickInsert) will be converted to the map types (expensive). This way if they key is in the map, it will be lightning fast.
// Other example of the same thing imagine you have a map unordered_map<string,ExpensiveObject> everytime when you want to insert an object
// and name is not string but string literal, you will one or more copies of the string object. Just to compute the key. You can compute (hash)
// the string literal 100% the same as the string. This way you can avoid creating string object. This example is similar to the word count example
// in the lazy_insert sample.
}
// UnorderedExamples-End
#endif // AZ_PLATFORM_WINDOWS
}
};
TEST_F(ContainersExamples, Array)
{
Array();
}
TEST_F(ContainersExamples, Vector)
{
Vector();
}
TEST_F(ContainersExamples, List)
{
List();
}
TEST_F(ContainersExamples, Deque)
{
Deque();
}
TEST_F(ContainersExamples, Hashed)
{
Hashed();
}
}
@@ -0,0 +1,224 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/functional_basic.h>
#include <AzCore/std/tuple.h>
namespace UnitTest
{
class FunctionalBasicTest
: public ScopedAllocatorSetupFixture
{
};
namespace Internal
{
struct FunctionalOperatorConfig
{
template<typename OperandType, typename T, typename U, typename TupleType>
static void PerformOperation(T&& lhs, U&& rhs, const TupleType& expectedValues)
{
// Arithmetic
EXPECT_EQ(AZStd::get<0>(expectedValues), AZStd::plus<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<1>(expectedValues), AZStd::minus<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<2>(expectedValues), AZStd::multiplies<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<3>(expectedValues), AZStd::divides<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<4>(expectedValues), AZStd::modulus<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<5>(expectedValues), AZStd::negate<OperandType>{}(AZStd::forward<T>(lhs)));
// Comparison
EXPECT_EQ(AZStd::get<6>(expectedValues), AZStd::equal_to<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<7>(expectedValues), AZStd::not_equal_to<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<8>(expectedValues), AZStd::greater<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<9>(expectedValues), AZStd::less<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<10>(expectedValues), AZStd::greater_equal<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<11>(expectedValues), AZStd::less_equal<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
// Logical
EXPECT_EQ(AZStd::get<12>(expectedValues), AZStd::logical_and<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<13>(expectedValues), AZStd::logical_or<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<14>(expectedValues), AZStd::logical_not<OperandType>{}(AZStd::forward<T>(lhs)));
// Bitwise
EXPECT_EQ(AZStd::get<15>(expectedValues), AZStd::bit_and<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<16>(expectedValues), AZStd::bit_or<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<17>(expectedValues), AZStd::bit_xor<OperandType>{}(AZStd::forward<T>(lhs), AZStd::forward<U>(rhs)));
EXPECT_EQ(AZStd::get<18>(expectedValues), AZStd::bit_not<OperandType>{}(AZStd::forward<T>(lhs)));
}
};
struct IntWrapper
{
int32_t m_value;
};
// arithmetic operators
int32_t operator+(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value + rhs;
}
int32_t operator+(int32_t lhs, IntWrapper rhs)
{
return lhs + rhs.m_value;
}
int32_t operator-(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value - rhs;
}
int32_t operator-(int32_t lhs, IntWrapper rhs)
{
return lhs - rhs.m_value;
}
int32_t operator*(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value * rhs;
}
int32_t operator*(int32_t lhs, IntWrapper rhs)
{
return lhs * rhs.m_value;
}
int32_t operator/(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value / rhs;
}
int32_t operator/(int32_t lhs, IntWrapper rhs)
{
return lhs / rhs.m_value;
}
int32_t operator%(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value % rhs;
}
int32_t operator%(int32_t lhs, IntWrapper rhs)
{
return lhs % rhs.m_value;
}
int32_t operator-(IntWrapper lhs)
{
return -lhs.m_value;
}
// comparison operators
bool operator==(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value == rhs;
}
bool operator==(int32_t lhs, IntWrapper rhs)
{
return lhs == rhs.m_value;
}
bool operator!=(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value != rhs;
}
bool operator!=(int32_t lhs, IntWrapper rhs)
{
return lhs != rhs.m_value;
}
bool operator>(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value > rhs;
}
bool operator>(int lhs, IntWrapper rhs)
{
return lhs > rhs.m_value;
}
bool operator<(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value < rhs;
}
bool operator<(int lhs, IntWrapper rhs)
{
return lhs < rhs.m_value;
}
bool operator>=(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value >= rhs;
}
bool operator>=(int lhs, IntWrapper rhs)
{
return lhs >= rhs.m_value;
}
bool operator<=(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value <= rhs;
}
bool operator<=(int lhs, IntWrapper rhs)
{
return lhs <= rhs.m_value;
}
// logical operators
bool operator&&(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value && rhs;
}
bool operator&&(int lhs, IntWrapper rhs)
{
return lhs && rhs.m_value;
}
bool operator||(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value || rhs;
}
bool operator||(int lhs, IntWrapper rhs)
{
return lhs || rhs.m_value;
}
bool operator!(IntWrapper lhs)
{
return !lhs.m_value;
}
// bitwise operators
int32_t operator&(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value & rhs;
}
int32_t operator&(int lhs, IntWrapper rhs)
{
return lhs & rhs.m_value;
}
int32_t operator|(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value | rhs;
}
int32_t operator|(int lhs, IntWrapper rhs)
{
return lhs | rhs.m_value;
}
int32_t operator^(IntWrapper lhs, int32_t rhs)
{
return lhs.m_value ^ rhs;
}
int32_t operator^(int lhs, IntWrapper rhs)
{
return lhs ^ rhs.m_value;
}
int32_t operator~(IntWrapper lhs)
{
return ~lhs.m_value;
}
}
TEST_F(FunctionalBasicTest, FunctionalOperators_ReturnsExpectedValue)
{
Internal::FunctionalOperatorConfig::PerformOperation<int>(7, 11, AZStd::make_tuple(18, -4, 77, 0, 7, -7, false, true, false, true, false, true, true, true, false, 3, 15, 12, ~7));
Internal::FunctionalOperatorConfig::PerformOperation<int>(45, 34, AZStd::make_tuple(79, 11, 1530, 1, 11, -45, false, true, true, false, true, false, true, true, false, 32, 47, 15, ~45));
Internal::FunctionalOperatorConfig::PerformOperation<int>(24, 24, AZStd::make_tuple(48, 0, 576, 1, 0, -24, true, false, false, false, true, true, true, true, false, 24, 24, 0, ~24));
}
TEST_F(FunctionalBasicTest, FunctionalOperators_TransparentOperands)
{
Internal::FunctionalOperatorConfig::PerformOperation<void>(7, Internal::IntWrapper{ 11 }, AZStd::make_tuple(18, -4, 77, 0, 7, -7, false, true, false, true, false, true, true, true, false, 3, 15, 12, ~7));
Internal::FunctionalOperatorConfig::PerformOperation<void>(Internal::IntWrapper{ 45 }, 34, AZStd::make_tuple(79, 11, 1530, 1, 11, -45, false, true, true, false, true, false, true, true, false, 32, 47, 15, ~45));
Internal::FunctionalOperatorConfig::PerformOperation<void>(24, Internal::IntWrapper{ 24 }, AZStd::make_tuple(48, 0, 576, 1, 0, -24, true, false, false, false, true, true, true, true, false, 24, 24, 0, ~24));
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,583 @@
/*
* 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 <AzCore/std/any.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/function/invoke.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string_view.h>
#include "UserTypes.h"
namespace UnitTest
{
//////////////////////////////////////////////////////////////////////////
// Fixtures
struct InvokeNonCopyable
{
InvokeNonCopyable() = default;
private:
InvokeNonCopyable(const InvokeNonCopyable&) = delete;
InvokeNonCopyable& operator=(const InvokeNonCopyable&) = delete;
};
struct InvokeTestStruct
{
explicit InvokeTestStruct(int num)
: m_data(num)
{}
int IntResultIntParameter(int) { return m_data; }
int& operator()(InvokeNonCopyable&&) & { return m_data; }
const int& operator()(InvokeNonCopyable&&) const & { return m_data; }
volatile int& operator()(InvokeNonCopyable&&) volatile & { return m_data; }
const volatile int& operator()(InvokeNonCopyable&&) const volatile & { return m_data; }
int&& operator()(InvokeNonCopyable&&) && { return AZStd::move(m_data); }
const int&& operator()(InvokeNonCopyable&&) const && { return AZStd::move(m_data); }
volatile int&& operator()(InvokeNonCopyable&&) volatile && { return AZStd::move(m_data); }
const volatile int&& operator()(InvokeNonCopyable&&) const volatile && { return AZStd::move(m_data); }
int m_data;
};
struct InvokeTestDerivedStruct : InvokeTestStruct
{
explicit InvokeTestDerivedStruct(int num)
: InvokeTestStruct(num)
{}
};
struct InvokeTestImplicitConstructor
{
InvokeTestImplicitConstructor(AZ::s32) {}
};
struct InvokeTestExplicitConstructor
{
explicit InvokeTestExplicitConstructor(AZ::s32) {}
};
struct InvokeTestDeletedS32Callable
{
bool operator()(InvokeTestStruct) { return false; }
private:
bool operator()(AZ::s32) = delete;
};
// Fixture for non-typed tests
class InvocableTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
TEST_F(InvocableTest, InvalidInvocableArgsTest)
{
using Func = int(InvokeTestStruct::*)(int);
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, Test, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<int>::value));
}
TEST_F(InvocableTest, MemberFunctionTest)
{
using Func = int(InvokeTestStruct::*)(int);
using CLFunc = int(InvokeTestStruct::*)(int) const &;
using RFunc = int(InvokeTestStruct::*)(int) &&;
using CRFunc = int(InvokeTestStruct::*)(int) const &&;
// Member functions require a "this" object in order to be invocable
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<AZStd::decay_t<Func>>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<AZStd::decay_t<CLFunc>>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<AZStd::decay_t<RFunc>>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<AZStd::decay_t<CRFunc>>::value));
// Bullet 1
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, InvokeTestStruct, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, InvokeTestDerivedStruct, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, const InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, const InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, int, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, InvokeTestStruct, AZStd::string_view>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const InvokeTestDerivedStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RFunc, InvokeTestStruct, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RFunc, InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, const InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, const InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CRFunc, InvokeTestStruct, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CRFunc, InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CRFunc, const InvokeTestStruct&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, const InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, InvokeTestStruct&, int>::value));
// Bullet 2
using RefTest = AZStd::reference_wrapper<InvokeTestStruct>;
using RefDerivedTest = AZStd::reference_wrapper<InvokeTestDerivedStruct>;
using RefConstTest = AZStd::reference_wrapper<const InvokeTestStruct>;
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, RefDerivedTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, const RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, RefConstTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, RefTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, RefDerivedTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, const RefTest&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, RefConstTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefTest&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefTest&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefDerivedTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefConstTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefConstTest&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefConstTest, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, RefConstTest&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const RefConstTest&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, RefDerivedTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, RefConstTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, RefTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, RefDerivedTest, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, RefConstTest, int>::value));
// Bullet 3
using TestPtrType = InvokeTestStruct*;
using DerivedTestPtrType = InvokeTestDerivedStruct*;
using ConstTestPtrType = const InvokeTestStruct*;
using UniqueTestPtrType = AZStd::unique_ptr<InvokeTestStruct>;
using SharedTestPtrType = AZStd::shared_ptr<InvokeTestStruct>;
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, const TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, DerivedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, UniqueTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<Func, SharedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, ConstTestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<Func, ConstTestPtrType&&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, const TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, DerivedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, UniqueTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, SharedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, ConstTestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<CLFunc, ConstTestPtrType&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, const TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, DerivedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, UniqueTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, SharedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, ConstTestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RFunc, ConstTestPtrType&&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, const TestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, DerivedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, UniqueTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, SharedTestPtrType, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, ConstTestPtrType&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<CRFunc, ConstTestPtrType&&, int>::value));
}
TEST_F(InvocableTest, MemberObjectTest)
{
using MemberFn = int(InvokeTestStruct::*);
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<MemberFn>::value));
// Bullet 4
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, InvokeTestStruct>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, InvokeTestStruct&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, const InvokeTestStruct&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, InvokeTestStruct&&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, const InvokeTestStruct&&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, InvokeTestDerivedStruct&>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<MemberFn, InvokeTestStruct, int>::value));
// Bullet 5
using RefTest = AZStd::reference_wrapper<InvokeTestStruct>;
using RefDerivedTest = AZStd::reference_wrapper<InvokeTestDerivedStruct>;
using RefConstTest = AZStd::reference_wrapper<const InvokeTestStruct>;
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, RefTest>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, RefTest&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, const RefTest&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, RefConstTest&&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, const RefConstTest&&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, RefDerivedTest&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, RefConstTest&>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<MemberFn, RefTest, float>::value));
// Bullet 6
using TestPtrType = InvokeTestStruct*;
using DerivedTestPtrType = InvokeTestDerivedStruct;
using ConstTestPtrType = const InvokeTestStruct*;
using UniqueTestPtrType = AZStd::unique_ptr<InvokeTestStruct>;
using SharedTestPtrType = AZStd::shared_ptr<InvokeTestStruct>;
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, TestPtrType>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, TestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, const TestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, TestPtrType&&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, DerivedTestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, ConstTestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, SharedTestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<MemberFn, UniqueTestPtrType&>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<MemberFn, TestPtrType, float>::value));
}
TEST_F(InvocableTest, FunctionObjectTest)
{
using RawFuncPtr = AZStd::string_view(*)(InvokeTestStruct, int);
using RawFuncRef = AZStd::string_view(&)(InvokeTestStruct&, int);
using FuncObject = InvokeTestDeletedS32Callable;
using StdFunctionObject = AZStd::function<int(InvokeTestStruct&&)>;
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RawFuncPtr>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RawFuncPtr, InvokeTestStruct&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RawFuncPtr, InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RawFuncPtr, InvokeTestDerivedStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RawFuncRef>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RawFuncRef, InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<RawFuncRef, const InvokeTestStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<RawFuncRef, InvokeTestDerivedStruct&, int>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<FuncObject, AZ::s32>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<FuncObject, InvokeTestStruct>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable<StdFunctionObject, InvokeTestStruct&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable<StdFunctionObject, InvokeTestStruct&&>::value));
}
TEST_F(InvocableTest, Invocable_R_Test)
{
using Func = int(*)();
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable_r<int, Func>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable_r<double, Func>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable_r<const volatile void, Func>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_invocable_r<InvokeTestImplicitConstructor, Func>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable_r<InvokeTestExplicitConstructor, Func>::value));
AZ_TEST_STATIC_ASSERT((!AZStd::is_invocable_r<InvokeTestStruct, Func>::value));
}
class InvokeTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
static int RawIntFunc(int num)
{
return num;
}
static int DoubleRValueIntValue(int&& num)
{
return num * 2;
}
public:
static const int s_rawFuncResult;
};
const int InvokeTest::s_rawFuncResult = 24;
template<typename FuncSig, typename ExpectResultType, typename Functor>
void InvokeMemberFunctionTester(Functor&& functor, int expectResult)
{
using MemberFunc = FuncSig;
MemberFunc memberFunc = &InvokeTestStruct::operator();
InvokeNonCopyable nonCopyableArg;
using DeducedResultType = decltype(AZStd::invoke(memberFunc, AZStd::forward<Functor>(functor), AZStd::move(nonCopyableArg)));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, DeducedResultType>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, AZStd::invoke_result_t<MemberFunc, Functor, InvokeNonCopyable&&>>::value));
auto result = AZStd::invoke(memberFunc, AZStd::forward<Functor>(functor), AZStd::move(nonCopyableArg));
EXPECT_EQ(expectResult, result);
};
template<typename ExpectResultType, typename Functor>
void InvokeMemberObjectTester(Functor&& functor, int expectResult)
{
auto memberObjPtr = &InvokeTestStruct::m_data;
using DeducedResultType = decltype(AZStd::invoke(memberObjPtr, AZStd::forward<Functor>(functor)));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, DeducedResultType>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, AZStd::invoke_result_t<decltype(memberObjPtr), Functor>>::value));
auto result = AZStd::invoke(memberObjPtr, AZStd::forward<Functor>(functor));
EXPECT_EQ(expectResult, result);
};
template<typename ExpectResultType, typename Functor>
void InvokeFunctionObjectTester(Functor&& functor, int expectResult)
{
InvokeNonCopyable nonCopyableArg;
using DeducedResultType = decltype(AZStd::invoke(AZStd::forward<Functor>(functor), AZStd::move(nonCopyableArg)));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, DeducedResultType>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<ExpectResultType, AZStd::invoke_result_t<Functor, InvokeNonCopyable&&>>::value));
auto result = AZStd::invoke(AZStd::forward<Functor>(functor), AZStd::move(nonCopyableArg));
EXPECT_EQ(expectResult, result);
};
int InvokeRawFunc(InvokeNonCopyable&&)
{
return InvokeTest::s_rawFuncResult;
}
TEST_F(InvokeTest, MemberFunctionTest)
{
{
// Bullet 1
{
InvokeTestStruct test(1);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(test, test.m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(test, test.m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(test, test.m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(test, test.m_data);
InvokeMemberFunctionTester<int&& (InvokeTestStruct::*)(InvokeNonCopyable&&) && , int&&>(AZStd::move(test), test.m_data);
InvokeMemberFunctionTester<const int&& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &&, const int&&>(AZStd::move(test), test.m_data);
InvokeMemberFunctionTester<volatile int&& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &&, volatile int&&>(AZStd::move(test), test.m_data);
InvokeMemberFunctionTester<const volatile int&& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &&, const volatile int&&>(AZStd::move(test), test.m_data);
}
{
InvokeTestDerivedStruct derivedTest(2);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(derivedTest, derivedTest.m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(derivedTest, derivedTest.m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(derivedTest, derivedTest.m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(derivedTest, derivedTest.m_data);
using MemberDerivedRFunc = int&& (InvokeTestDerivedStruct::*)(InvokeNonCopyable&&) &&;
using MemberDerivedCRFunc = const int&& (InvokeTestDerivedStruct::*)(InvokeNonCopyable&&) const &&;
using MemberDerivedVRFunc = volatile int&& (InvokeTestDerivedStruct::*)(InvokeNonCopyable&&) volatile&&;
using MemberDerivedCVRFunc = const volatile int&& (InvokeTestDerivedStruct::*)(InvokeNonCopyable&&) const volatile&&;
InvokeMemberFunctionTester<MemberDerivedRFunc, int&&>(AZStd::move(derivedTest), derivedTest.m_data);
InvokeMemberFunctionTester<MemberDerivedCRFunc, const int&&>(AZStd::move(derivedTest), derivedTest.m_data);
InvokeMemberFunctionTester<MemberDerivedVRFunc, volatile int&&>(AZStd::move(derivedTest), derivedTest.m_data);
InvokeMemberFunctionTester<MemberDerivedCVRFunc, const volatile int&&>(AZStd::move(derivedTest), derivedTest.m_data);
}
}
{
// Bullet 2
{
InvokeTestStruct testObj(3);
AZStd::reference_wrapper<InvokeTestStruct> test(testObj);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(test, test.get().m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(test, test.get().m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(test, test.get().m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(test, test.get().m_data);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) & , int&>(AZStd::move(test), test.get().m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(AZStd::move(test), test.get().m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(AZStd::move(test), test.get().m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(AZStd::move(test), test.get().m_data);
}
{
InvokeTestDerivedStruct derivedTestObj(4);
AZStd::reference_wrapper<InvokeTestDerivedStruct> derivedTest(derivedTestObj);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(derivedTest, derivedTest.get().m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(derivedTest, derivedTest.get().m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(derivedTest, derivedTest.get().m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(derivedTest, derivedTest.get().m_data);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) & , int&>(AZStd::move(derivedTest), derivedTest.get().m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(AZStd::move(derivedTest), derivedTest.get().m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(AZStd::move(derivedTest), derivedTest.get().m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(AZStd::move(derivedTest), derivedTest.get().m_data);
}
}
{
// Bullet 3
{
InvokeTestStruct testObj(5);
InvokeTestStruct* test(&testObj);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(test, test->m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(test, test->m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(test, test->m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(test, test->m_data);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) & , int&>(AZStd::move(test), test->m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(AZStd::move(test), test->m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(AZStd::move(test), test->m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(AZStd::move(test), test->m_data);
AZStd::unique_ptr<InvokeTestStruct> testUniquePtr(&testObj);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(test, test->m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(test, test->m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(test, test->m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(test, test->m_data);
testUniquePtr.release();
}
{
InvokeTestDerivedStruct derivedTestObj(6);
InvokeTestDerivedStruct* derivedTest(&derivedTestObj);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) &, int&>(derivedTest, derivedTest->m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(derivedTest, derivedTest->m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(derivedTest, derivedTest->m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(derivedTest, derivedTest->m_data);
InvokeMemberFunctionTester<int& (InvokeTestStruct::*)(InvokeNonCopyable&&) & , int&>(AZStd::move(derivedTest), derivedTest->m_data);
InvokeMemberFunctionTester<const int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const &, const int&>(AZStd::move(derivedTest), derivedTest->m_data);
InvokeMemberFunctionTester<volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) volatile &, volatile int&>(AZStd::move(derivedTest), derivedTest->m_data);
InvokeMemberFunctionTester<const volatile int& (InvokeTestStruct::*)(InvokeNonCopyable&&) const volatile &, const volatile int&>(AZStd::move(derivedTest), derivedTest->m_data);
}
}
}
TEST_F(InvokeTest, MemberObjectTest)
{
{
// Bullet 4
{
using TestStruct = InvokeTestStruct;
TestStruct test(7);
InvokeMemberObjectTester<int&>(test, test.m_data);
InvokeMemberObjectTester<const int&>(static_cast<const TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<volatile int&>(static_cast<volatile TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<const volatile int&>(static_cast<const volatile TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<int&&>(static_cast<TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<const int&&>(static_cast<const TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<volatile int&&>(static_cast<volatile TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<const volatile int&&>(static_cast<const volatile TestStruct&&>(test), test.m_data);
}
{
using TestStruct = InvokeTestDerivedStruct;
TestStruct test(8);
InvokeMemberObjectTester<int&>(test, test.m_data);
InvokeMemberObjectTester<const int&>(static_cast<const TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<volatile int&>(static_cast<volatile TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<const volatile int&>(static_cast<const volatile TestStruct&>(test), test.m_data);
InvokeMemberObjectTester<int&&>(static_cast<TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<const int&&>(static_cast<const TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<volatile int&&>(static_cast<volatile TestStruct&&>(test), test.m_data);
InvokeMemberObjectTester<const volatile int&&>(static_cast<const volatile TestStruct&&>(test), test.m_data);
}
}
{
// Bullet 5
{
using TestStruct = InvokeTestStruct;
TestStruct testObj(9);
InvokeMemberObjectTester<int&>(AZStd::reference_wrapper<TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<const int&>(AZStd::reference_wrapper<const TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<volatile int&>(AZStd::reference_wrapper<volatile TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<const volatile int&>(AZStd::reference_wrapper<const volatile TestStruct>(testObj), testObj.m_data);
}
{
using TestStruct = InvokeTestDerivedStruct;
TestStruct testObj(10);
InvokeMemberObjectTester<int&>(AZStd::reference_wrapper<TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<const int&>(AZStd::reference_wrapper<const TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<volatile int&>(AZStd::reference_wrapper<volatile TestStruct>(testObj), testObj.m_data);
InvokeMemberObjectTester<const volatile int&>(AZStd::reference_wrapper<const volatile TestStruct>(testObj), testObj.m_data);
}
}
{
// Bullet 6
{
using TestStruct = InvokeTestStruct;
TestStruct testObj(11);
TestStruct* test(&testObj);
const TestStruct* cTest(&testObj);
volatile TestStruct* vTest(&testObj);
const volatile TestStruct* cvTest(&testObj);
InvokeMemberObjectTester<int&>(test, test->m_data);
InvokeMemberObjectTester<const int&>(cTest, test->m_data);
InvokeMemberObjectTester<volatile int&>(vTest, test->m_data);
InvokeMemberObjectTester<const volatile int&>(cvTest, test->m_data);
}
{
using TestStruct = InvokeTestDerivedStruct;
TestStruct testObj(12);
TestStruct* test(&testObj);
InvokeMemberObjectTester<int&>(test, test->m_data);
InvokeMemberObjectTester<const int&>(static_cast<const TestStruct*>(test), test->m_data);
InvokeMemberObjectTester<volatile int&>(static_cast<volatile TestStruct*>(test), test->m_data);
InvokeMemberObjectTester<const volatile int&>(static_cast<const volatile TestStruct*>(test), test->m_data);
}
}
}
TEST_F(InvokeTest, FunctionObjectTest)
{
// Bullet 7
using RawFuncPtr = int(*)(InvokeNonCopyable&&);
using RawFuncRef = int(&)(InvokeNonCopyable&&);
RawFuncPtr testRawFuncPtr = &InvokeRawFunc;
RawFuncRef testRawFuncRef = InvokeRawFunc;
InvokeFunctionObjectTester<int>(testRawFuncPtr, InvokeTest::s_rawFuncResult);
InvokeFunctionObjectTester<int>(testRawFuncRef, InvokeTest::s_rawFuncResult);
AZStd::function<int(int)> testStdFunc = &RawIntFunc;
int numResult = AZStd::invoke(testStdFunc, InvokeTest::s_rawFuncResult);
EXPECT_EQ(InvokeTest::s_rawFuncResult, numResult);
AZStd::function<int(int&&)> testStdFuncWithRValueParam = &DoubleRValueIntValue;
numResult = AZStd::invoke(testStdFuncWithRValueParam, 520);
EXPECT_EQ(1040, numResult);
InvokeTestStruct testFunctor(13);
InvokeFunctionObjectTester<int&>(testFunctor, testFunctor.m_data);
InvokeFunctionObjectTester<const int&>(static_cast<const InvokeTestStruct&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<volatile int&>(static_cast<volatile InvokeTestStruct&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<const volatile int&>(static_cast<const volatile InvokeTestStruct&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<int&&>(static_cast<InvokeTestStruct&&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<const int&&>(static_cast<const InvokeTestStruct&&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<volatile int&&>(static_cast<volatile InvokeTestStruct&&>(testFunctor), testFunctor.m_data);
InvokeFunctionObjectTester<const volatile int&&>(static_cast<const volatile InvokeTestStruct&&>(testFunctor), testFunctor.m_data);
}
}
@@ -0,0 +1,226 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/iterator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/utils.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
class Iterators
: public AllocatorsFixture
{
};
template<typename Container>
void Test_WrapperFunctions_MutableIterator_MutableContainer()
{
Container int_container = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }};
typename Container::iterator iter_begin = begin(int_container);
EXPECT_EQ(*iter_begin, 0);
EXPECT_EQ(*next(iter_begin), 1);
EXPECT_EQ(*next(iter_begin, 2), 2);
++iter_begin;
EXPECT_EQ(*iter_begin, 1);
typename Container::iterator iter_end = end(int_container);
EXPECT_EQ(iter_end, int_container.end());
EXPECT_EQ(*prev(iter_end), 9);
EXPECT_EQ(*prev(iter_end, 2), 8);
--iter_end;
EXPECT_EQ(*iter_end, 9);
typename Container::reverse_iterator iter_rbegin = rbegin(int_container);
EXPECT_EQ(*iter_rbegin, 9);
EXPECT_EQ(*next(iter_rbegin), 8);
EXPECT_EQ(*next(iter_rbegin, 2), 7);
++iter_rbegin;
EXPECT_EQ(*iter_rbegin, 8);
typename Container::reverse_iterator iter_rend = rend(int_container);
EXPECT_EQ(*prev(iter_rend), 0);
EXPECT_EQ(*prev(iter_rend, 2), 1);
--iter_rend;
EXPECT_EQ(*iter_rend, 0);
//verify we can successfully modify the value in a non-const iterator
*begin(int_container) = 42;
EXPECT_EQ(*begin(int_container), 42);
}
template<typename Container>
void Test_WrapperFunctions_ConstIterator_MutableContainer()
{
Container int_container = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }};
typename Container::const_iterator iter_cbegin = cbegin(int_container);
EXPECT_EQ(*iter_cbegin, 0);
EXPECT_EQ(*next(iter_cbegin), 1);
EXPECT_EQ(*next(iter_cbegin, 2), 2);
++iter_cbegin;
EXPECT_EQ(*iter_cbegin, 1);
typename Container::const_iterator iter_cend = cend(int_container);
EXPECT_EQ(iter_cend, int_container.cend());
EXPECT_EQ(*prev(iter_cend), 9);
EXPECT_EQ(*prev(iter_cend, 2), 8);
--iter_cend;
EXPECT_EQ(*iter_cend, 9);
typename Container::const_reverse_iterator iter_crbegin = crbegin(int_container);
EXPECT_EQ(*iter_crbegin, 9);
EXPECT_EQ(*next(iter_crbegin), 8);
EXPECT_EQ(*next(iter_crbegin, 2), 7);
++iter_crbegin;
EXPECT_EQ(*iter_crbegin, 8);
typename Container::const_reverse_iterator iter_crend = crend(int_container);
EXPECT_EQ(*prev(iter_crend), 0);
EXPECT_EQ(*prev(iter_crend, 2), 1);
--iter_crend;
EXPECT_EQ(*iter_crend, 0);
}
template<typename ConstContainer>
void Test_WrapperFunctions_ConstIterator_ConstContainer()
{
const ConstContainer const_int_container = {{ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }};
typename ConstContainer::const_iterator const_iter_begin = begin(const_int_container);
EXPECT_EQ(*const_iter_begin, 10);
EXPECT_EQ(*next(const_iter_begin), 11);
EXPECT_EQ(*next(const_iter_begin, 2), 12);
typename ConstContainer::const_iterator const_iter_end = end(const_int_container);
EXPECT_EQ(const_iter_end, const_int_container.end());
EXPECT_EQ(*prev(const_iter_end), 19);
EXPECT_EQ(*prev(const_iter_end, 2), 18);
}
TEST_F(Iterators, FunctionWrappers_MutableContainers)
{
Test_WrapperFunctions_MutableIterator_MutableContainer<AZStd::vector<int>>();
Test_WrapperFunctions_ConstIterator_MutableContainer<AZStd::vector<int>>();
Test_WrapperFunctions_MutableIterator_MutableContainer<AZStd::array<int, 10>>();
Test_WrapperFunctions_ConstIterator_MutableContainer<AZStd::array<int, 10>>();
Test_WrapperFunctions_MutableIterator_MutableContainer<AZStd::list<int>>();
Test_WrapperFunctions_MutableIterator_MutableContainer<AZStd::set<int>>();
//list and set currently don't support const iterator accessors so we can't test them here (yet)
}
TEST_F(Iterators, FunctionWrappers_ConstContainers)
{
Test_WrapperFunctions_ConstIterator_ConstContainer<const AZStd::vector<int>>();
Test_WrapperFunctions_ConstIterator_ConstContainer<const AZStd::array<int, 10>>();
Test_WrapperFunctions_ConstIterator_ConstContainer<const AZStd::list<int>>();
Test_WrapperFunctions_ConstIterator_ConstContainer<const AZStd::set<int>>();
}
TEST_F(Iterators, FunctionWrappers_MutableRawArray)
{
int int_array[10] = { 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 };
EXPECT_EQ(*begin(int_array), 20);
EXPECT_EQ(*next(begin(int_array)), 21);
EXPECT_EQ(*next(begin(int_array), 2), 22);
EXPECT_EQ(end(int_array) - AZ_ARRAY_SIZE(int_array), begin(int_array));
EXPECT_EQ(*prev(end(int_array)), 29);
EXPECT_EQ(*prev(end(int_array), 2), 28);
EXPECT_EQ(*rbegin(int_array), 29);
EXPECT_EQ(*next(rbegin(int_array)), 28);
EXPECT_EQ(*next(rbegin(int_array), 2), 27);
EXPECT_EQ(*prev(rend(int_array)), 20);
EXPECT_EQ(*prev(rend(int_array), 2), 21);
EXPECT_EQ(*crbegin(int_array), 29);
EXPECT_EQ(*next(crbegin(int_array)), 28);
EXPECT_EQ(*next(crbegin(int_array), 2), 27);
EXPECT_EQ(*prev(crend(int_array)), 20);
EXPECT_EQ(*prev(crend(int_array), 2), 21);
//verify we can successfully modify the value in a non-const iterator
*begin(int_array) = -42;
EXPECT_EQ(*begin(int_array), -42);
}
TEST_F(Iterators, FunctionWrappers_ConstRawArray)
{
const int const_int_array[10] = { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 };
EXPECT_EQ(*cbegin(const_int_array), 30);
EXPECT_EQ(*next(cbegin(const_int_array)), 31);
EXPECT_EQ(*next(cbegin(const_int_array), 2), 32);
EXPECT_EQ(cend(const_int_array) - AZ_ARRAY_SIZE(const_int_array), cbegin(const_int_array));
EXPECT_EQ(*prev(cend(const_int_array)), 39);
EXPECT_EQ(*prev(cend(const_int_array), 2), 38);
}
TEST_F(Iterators, IteratorTraits_ResolveAtCompileTime)
{
using list_type = AZStd::list<int>;
static_assert(AZStd::Internal::has_iterator_category_v<typename list_type::iterator>);
static_assert(AZStd::Internal::has_iterator_type_aliases_v<typename list_type::iterator>);
constexpr bool list_type_iterator_type_aliases = AZStd::Internal::has_iterator_type_aliases_v<typename list_type::iterator>;
static_assert(AZStd::is_convertible_v<AZStd::Internal::iterator_traits_type_aliases<typename list_type::iterator, list_type_iterator_type_aliases>::iterator_category,
AZStd::input_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::iterator>::iterator_category, bidirectional_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::iterator>::value_type, int>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::iterator>::difference_type, AZStd::ptrdiff_t>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::iterator>::pointer, int*>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::iterator>::reference, int&>);
static_assert(AZStd::Internal::is_input_iterator_v<typename list_type::iterator>);
static_assert(!AZStd::Internal::has_iterator_concept_v<AZStd::iterator_traits<typename list_type::iterator>>);
static_assert(!AZStd::Internal::satisfies_contiguous_iterator_concept_v<typename list_type::iterator>);
static_assert(AZStd::Internal::has_iterator_category_v<typename list_type::const_iterator>);
static_assert(AZStd::Internal::has_iterator_type_aliases_v<typename list_type::const_iterator>);
constexpr bool list_type_const_iterator_type_aliases = AZStd::Internal::has_iterator_type_aliases_v<typename list_type::const_iterator>;
static_assert(AZStd::is_convertible_v<AZStd::Internal::iterator_traits_type_aliases<typename list_type::iterator, list_type_const_iterator_type_aliases>::iterator_category,
AZStd::input_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::const_iterator>::iterator_category, bidirectional_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::const_iterator>::value_type, int>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::const_iterator>::difference_type, AZStd::ptrdiff_t>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::const_iterator>::pointer, const int*>);
static_assert(is_same_v<AZStd::iterator_traits<typename list_type::const_iterator>::reference, const int&>);
static_assert(AZStd::Internal::is_input_iterator_v<typename list_type::const_iterator>);
static_assert(!AZStd::Internal::has_iterator_concept_v<AZStd::iterator_traits<typename list_type::const_iterator>>);
static_assert(!AZStd::Internal::satisfies_contiguous_iterator_concept_v<typename list_type::const_iterator>);
using pointer_type = const char*;
static_assert(AZStd::Internal::has_iterator_category_v<AZStd::iterator_traits<pointer_type>>);
static_assert(AZStd::Internal::has_iterator_type_aliases_v<AZStd::iterator_traits<pointer_type>>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::iterator_concept, contiguous_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::iterator_category, random_access_iterator_tag>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::value_type, char>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::difference_type, AZStd::ptrdiff_t>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::pointer, const char*>);
static_assert(is_same_v<AZStd::iterator_traits<pointer_type>::reference, const char&>);
static_assert(AZStd::Internal::has_iterator_concept_v<AZStd::iterator_traits<pointer_type>>);
static_assert(AZStd::Internal::satisfies_contiguous_iterator_concept_v<pointer_type>);
}
}
+969
View File
@@ -0,0 +1,969 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/list.h>
#include <AzCore/std/containers/forward_list.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/allocator_static.h>
#include <AzCore/std/allocator_ref.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
using namespace AZStd;
using namespace UnitTestInternal;
#define AZ_TEST_VALIDATE_EMPTY_LIST(_List) \
AZ_TEST_ASSERT(_List.validate()); \
AZ_TEST_ASSERT(_List.size() == 0); \
AZ_TEST_ASSERT(_List.begin() == _List.end()); \
AZ_TEST_ASSERT(_List.empty());
#define AZ_TEST_VALIDATE_LIST(_List, _NumElements) \
AZ_TEST_ASSERT(_List.validate()); \
AZ_TEST_ASSERT(_List.size() == _NumElements); \
AZ_TEST_ASSERT((_NumElements > 0) ? !_List.empty() : _List.empty()); \
AZ_TEST_ASSERT((_NumElements > 0) ? _List.begin() != _List.end() : _List.begin() == _List.end());
namespace UnitTest
{
/**
* Tests AZSTD::list container.
*/
class ListContainers
: public AllocatorsFixture
{
public:
// ListContainerTest-Begin
struct RemoveLessThan401
{
AZ_FORCE_INLINE bool operator()(int element) const { return element < 401; }
};
struct UniqueForLessThan401
{
AZ_FORCE_INLINE bool operator()(int el1, int el2) const { return (el1 == el2 && el1 < 401); }
};
};
TEST_F(ListContainers, InitializerListCtor)
{
list<int> intList({ 1, 2, 3, 4, 5, 6 });
EXPECT_EQ(6, intList.size());
}
TEST_F(ListContainers, ListCtorAssign)
{
list<int> int_list;
list<int> int_list1;
list<int> int_list2;
list<int> int_list3;
// default ctor
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
// 10 int elements, value 33
int_list1 = list<int>(10, 33);
AZ_TEST_VALIDATE_LIST(int_list1, 10);
for (list<int>::iterator iter = int_list1.begin(); iter != int_list1.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy list 1 using, first,last,allocator.
int_list2 = list<int>(int_list1.begin(), int_list1.end());
AZ_TEST_VALIDATE_LIST(int_list2, 10);
for (list<int>::iterator iter = int_list2.begin(); iter != int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy construct
int_list3 = list<int>(int_list1);
AZ_TEST_ASSERT(int_list1 == int_list3);
// assign
int_list = int_list1;
AZ_TEST_ASSERT(int_list == int_list1);
int_list2.push_back(60);
int_list = int_list2;
AZ_TEST_ASSERT(int_list == int_list2);
int_list2.pop_back();
int_list2.pop_back();
int_list = int_list2;
AZ_TEST_ASSERT(int_list == int_list2);
// assign with iterators (at the moment this uses the function operator= calls)
int_list2.assign(int_list1.begin(), int_list1.end());
AZ_TEST_ASSERT(int_list2 == int_list1);
// assign a fixed value
int_list.assign(5, 55);
AZ_TEST_VALIDATE_LIST(int_list, 5);
for (list<int>::iterator iter = int_list.begin(); iter != int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 55);
}
}
TEST_F(ListContainers, ListResizeInsertEraseClear)
{
list<int> int_list;
list<int> int_list1;
list<int> int_list2;
list<int> int_list3;
list<int>::iterator list_it;
int_list.assign(5, 55);
int_list1 = list<int>(10, 33);
// resize to bigger
int_list.resize(6, 43);
AZ_TEST_VALIDATE_LIST(int_list, 6);
AZ_TEST_ASSERT(int_list.back() == 43);
AZ_TEST_ASSERT(*prev(int_list.end(), 2) == 55);
AZ_TEST_ASSERT(*int_list.begin() == *prev(int_list.rend()));
// resize to smaller
int_list.resize(3);
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.back() == 55);
// insert
list_it = int_list.insert(int_list.begin(), 44);
AZ_TEST_VALIDATE_LIST(int_list, 4);
AZ_TEST_ASSERT(int_list.front() == 44);
AZ_TEST_ASSERT(*list_it == 44);
list_it = int_list.insert(int_list.end(), 66);
AZ_TEST_VALIDATE_LIST(int_list, 5);
AZ_TEST_ASSERT(int_list.back() == 66);
AZ_TEST_ASSERT(*list_it == 66);
list_it = int_list.insert(int_list.begin(), 2, 11);
AZ_TEST_VALIDATE_LIST(int_list, 7);
AZ_TEST_ASSERT(int_list.front() == 11);
AZ_TEST_ASSERT(*next(int_list.begin()) == 11);
AZ_TEST_ASSERT(*list_it == 11);
AZ_TEST_ASSERT(*next(list_it) == 11);
list_it = int_list.insert(int_list.end(), 2, 22);
AZ_TEST_VALIDATE_LIST(int_list, 9);
AZ_TEST_ASSERT(int_list.back() == 22);
AZ_TEST_ASSERT(*prev(int_list.end(), 2) == 22);
AZ_TEST_ASSERT(*prev(list_it) == 66); // 66 was the last element added at int_list.end()
AZ_TEST_ASSERT(*list_it == 22);
AZ_TEST_ASSERT(*next(list_it) == 22);
list_it = int_list.insert(int_list.end(), int_list1.begin(), int_list1.end());
AZ_TEST_VALIDATE_LIST(int_list, 9 + int_list1.size());
AZ_TEST_ASSERT(int_list.back() == 33);
// Ensure that parallel iterating over int_list1 and int_list
// from the returned iterator result in the same elements.
list<int>::iterator list1_it = int_list1.begin();
for (; list1_it != int_list1.end(); ++list_it, ++list1_it)
{
AZ_TEST_ASSERT(list_it != int_list.end());
AZ_TEST_ASSERT(list1_it != int_list1.end());
AZ_TEST_ASSERT(*list_it == *list_it);
}
// erase
int_list.assign(2, 10);
int_list.push_back(20);
int_list.erase(--int_list.end());
AZ_TEST_VALIDATE_LIST(int_list, 2);
AZ_TEST_ASSERT(int_list.back() == 10);
int_list.insert(int_list.end(), 3, 44);
int_list.erase(prev(int_list.end(), 2), int_list.end());
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.back() == 44);
// clear
int_list.clear();
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
}
TEST_F(ListContainers, SwapSplice)
{
list<int> int_list;
list<int> int_list1;
list<int> int_list2;
int_list2 = list<int>(10, 33);
// list operations with container with the same allocator.
// swap
int_list.swap(int_list2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 10);
int_list.swap(int_list2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
AZ_TEST_VALIDATE_LIST(int_list2, 10);
int_list.assign(5, 55);
int_list.swap(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 10);
AZ_TEST_VALIDATE_LIST(int_list2, 5);
AZ_TEST_ASSERT(int_list.front() == 33);
AZ_TEST_ASSERT(int_list2.front() == 55);
// splice
// splice(iterator splicePos, this_type& rhs)
int_list.splice(int_list.end(), int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 15);
AZ_TEST_ASSERT(int_list.front() == 33);
AZ_TEST_ASSERT(int_list.back() == 55);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_list2.push_back(101);
int_list.splice(int_list.begin(), int_list2, int_list2.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 16);
AZ_TEST_ASSERT(int_list.front() == 101);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_list2.assign(5, 201);
int_list.splice(int_list.end(), int_list2, ++int_list2.begin(), int_list2.end());
AZ_TEST_VALIDATE_LIST(int_list2, 1);
AZ_TEST_VALIDATE_LIST(int_list, 20);
AZ_TEST_ASSERT(int_list.back() == 201);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last) the whole vector optimization.
int_list2.push_back(301);
int_list.splice(int_list.end(), int_list2, int_list2.begin(), int_list2.end());
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 22);
AZ_TEST_ASSERT(int_list.back() == 301);
}
TEST_F(ListContainers, RemoveUniqueSort)
{
list<int> int_list;
// remove
int_list.assign(5, 101);
int_list.push_back(201);
int_list.push_back(301);
int_list.push_back(401);
int_list.remove(101);
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.front() == 201);
AZ_TEST_ASSERT(int_list.back() == 401);
int_list.remove_if(RemoveLessThan401());
AZ_TEST_VALIDATE_LIST(int_list, 1);
AZ_TEST_ASSERT(int_list.back() == 401);
// unique
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(201);
int_list.push_back(301);
int_list.unique();
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.front() == 101);
AZ_TEST_ASSERT(int_list.back() == 301);
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(201);
int_list.push_back(401);
int_list.push_back(401);
int_list.push_back(501);
int_list.unique(UniqueForLessThan401());
AZ_TEST_VALIDATE_LIST(int_list, 5);
AZ_TEST_ASSERT(int_list.front() == 101);
AZ_TEST_ASSERT(int_list.back() == 501);
// sort
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(1);
int_list.sort();
AZ_TEST_VALIDATE_LIST(int_list, 4);
for (list<int>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(1);
int_list.sort(AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_list, 4);
for (list<int>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter >= *next(iter));
}
}
TEST_F(ListContainers, ReverseMerge)
{
list<int> int_list;
list<int> int_list1;
list<int> int_list2;
list<int> int_list3;
int_list.push_back(201);
int_list.push_back(1);
int_list.sort(AZStd::greater<int>());
// reverse
int_list.reverse();
for (list<int>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
// merge
int_list.clear();
int_list1.clear();
int_list.push_back(1); // 2 sorted lists for merge
int_list.push_back(10);
int_list.push_back(50);
int_list.push_back(200);
int_list1.push_back(2);
int_list1.push_back(8);
int_list1.push_back(60);
int_list1.push_back(180);
int_list2 = int_list;
int_list3 = int_list1;
int_list2.merge(int_list3);
AZ_TEST_VALIDATE_LIST(int_list2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list3);
for (list<int>::iterator iter = int_list2.begin(); iter != --int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_list.reverse();
int_list1.reverse();
int_list2 = int_list;
int_list3 = int_list1;
int_list2.merge(int_list3, AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_list2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list3);
for (list<int>::iterator iter = int_list2.begin(); iter != --int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
TEST_F(ListContainers, Extensions)
{
list<int> int_list;
// Extensions.
int_list.clear();
// Push_back()
int_list.push_back();
AZ_TEST_VALIDATE_LIST(int_list, 1);
int_list.front() = 100;
// Push_front()
int_list.push_front();
AZ_TEST_VALIDATE_LIST(int_list, 2);
AZ_TEST_ASSERT(int_list.back() == 100);
// Insert without value to copy from.
int_list.insert(int_list.begin());
AZ_TEST_VALIDATE_LIST(int_list, 3);
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)&int_list.front() % 4) == 0); // default int alignment
// make sure every allocation is aligned.
list<MyClass> aligned_list(5, MyClass(99));
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_list.front() & (alignment_of<MyClass>::value - 1)) == 0);
}
TEST_F(ListContainers, StaticBufferAllocator)
{
/////////////////////////////////////////////////////////////////////////
// Test swap, splice, merge, etc. of containers with different allocators.
// list operations with container with the same allocator.
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB_type;
static_buffer_16KB_type myMemoryManager1;
static_buffer_16KB_type myMemoryManager2;
typedef allocator_ref<static_buffer_16KB_type> static_allocator_ref_type;
static_allocator_ref_type allocator1(myMemoryManager1, "Mystack allocator 1");
static_allocator_ref_type allocator2(myMemoryManager2, "Mystack allocator 2");
typedef list<MyClass, static_allocator_ref_type> stack_myclass_list_type;
stack_myclass_list_type int_list10(allocator1);
stack_myclass_list_type int_list20(allocator2);
int_list20.assign(10, MyClass(33));
AZ_TEST_VALIDATE_LIST(int_list20, 10);
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 10 * sizeof(stack_myclass_list_type::node_type));
int_list20.leak_and_reset();
AZ_TEST_VALIDATE_EMPTY_LIST(int_list20);
myMemoryManager2.reset(); // free all memory
// set allocator
int_list20.assign(20, MyClass(22));
int_list20.set_allocator(allocator1);
AZ_TEST_VALIDATE_LIST(int_list20, 20);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 20 * sizeof(stack_myclass_list_type::node_type));
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 20 * sizeof(stack_myclass_list_type::node_type));
int_list20.leak_and_reset();
int_list20.set_allocator(allocator2);
myMemoryManager1.reset();
myMemoryManager2.reset();
int_list20.assign(10, MyClass(11));
// swap
int_list10.swap(int_list20);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list20);
AZ_TEST_VALIDATE_LIST(int_list10, 10);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 10 * sizeof(stack_myclass_list_type::node_type));
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 10 * sizeof(stack_myclass_list_type::node_type));
int_list10.swap(int_list20);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list10);
AZ_TEST_VALIDATE_LIST(int_list20, 10);
int_list10.assign(5, 55);
int_list10.swap(int_list20);
AZ_TEST_VALIDATE_LIST(int_list10, 10);
AZ_TEST_VALIDATE_LIST(int_list20, 5);
AZ_TEST_ASSERT(int_list10.front() == MyClass(11));
AZ_TEST_ASSERT(int_list20.front() == MyClass(55));
// splice
// splice(iterator splicePos, this_type& rhs)
int_list10.splice(int_list10.end(), int_list20);
AZ_TEST_VALIDATE_LIST(int_list10, 15);
AZ_TEST_ASSERT(int_list10.front() == MyClass(11));
AZ_TEST_ASSERT(int_list10.back() == MyClass(55));
AZ_TEST_VALIDATE_EMPTY_LIST(int_list20);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_list20.push_back(MyClass(101));
int_list10.splice(int_list10.begin(), int_list20, int_list20.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_list20);
AZ_TEST_VALIDATE_LIST(int_list10, 16);
AZ_TEST_ASSERT(int_list10.front() == MyClass(101));
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_list20.assign(5, MyClass(201));
int_list10.splice(int_list10.end(), int_list20, ++int_list20.begin(), int_list20.end());
AZ_TEST_VALIDATE_LIST(int_list20, 1);
AZ_TEST_VALIDATE_LIST(int_list10, 20);
AZ_TEST_ASSERT(int_list10.back() == MyClass(201));
int_list10.leak_and_reset();
int_list20.leak_and_reset();
myMemoryManager1.reset();
myMemoryManager2.reset();
// merge
stack_myclass_list_type int_list30(allocator1);
stack_myclass_list_type int_list40(allocator2);
int_list10.push_back(MyClass(1)); // 2 sorted lists for merge
int_list10.push_back(MyClass(10));
int_list10.push_back(MyClass(50));
int_list10.push_back(MyClass(200));
int_list20.push_back(MyClass(2));
int_list20.push_back(MyClass(8));
int_list20.push_back(MyClass(60));
int_list20.push_back(MyClass(180));
int_list30 = int_list10;
int_list40 = int_list20;
int_list30.merge(int_list40);
AZ_TEST_VALIDATE_LIST(int_list30, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list40);
for (stack_myclass_list_type::iterator iter = int_list30.begin(); iter != --int_list30.end(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_list10.reverse();
int_list20.reverse();
int_list30 = int_list10;
int_list40 = int_list20;
int_list30.merge(int_list40, AZStd::greater<MyClass>());
AZ_TEST_VALIDATE_LIST(int_list30, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list40);
for (stack_myclass_list_type::iterator iter = int_list30.begin(); iter != --int_list30.end(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
TEST_F(ListContainers, UniquePtr)
{
list<unique_ptr<MyNoCopyClass>> nocopy_list;
nocopy_list.emplace_back(new MyNoCopyClass(1, true, 3.0f));
nocopy_list.emplace_front(new MyNoCopyClass(2, true, 4.0f));
nocopy_list.emplace(nocopy_list.end(), new MyNoCopyClass(3, true, 5.0f));
nocopy_list.insert(nocopy_list.end(), AZStd::unique_ptr<MyNoCopyClass>(new MyNoCopyClass(4, true, 6.0f)));
for (const auto& ptr : nocopy_list)
{
AZ_TEST_ASSERT(ptr->m_bool);
}
}
TEST_F(ListContainers, ForwardListAssign)
{
forward_list<int> int_slist;
forward_list<int> int_slist1;
forward_list<int> int_slist2;
forward_list<int> int_slist3;
forward_list<int> int_slist4;
// default ctor
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
// 10 int elements, value 33
int_slist1 = forward_list<int>(10, 33);
AZ_TEST_VALIDATE_LIST(int_slist1, 10);
for (forward_list<int>::iterator iter = int_slist1.begin(); iter != int_slist1.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy list 1 using, first,last,allocator.
int_slist2 = forward_list<int>(int_slist1.begin(), int_slist1.end());
AZ_TEST_VALIDATE_LIST(int_slist2, 10);
for (forward_list<int>::iterator iter = int_slist2.begin(); iter != int_slist2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy construct
int_slist3 = forward_list<int>(int_slist1);
AZ_TEST_ASSERT(int_slist1 == int_slist3);
// initializer_list construct
int_slist4 = { 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 };
AZ_TEST_ASSERT(int_slist1 == int_slist4);
// assign
int_slist = int_slist1;
AZ_TEST_ASSERT(int_slist == int_slist1);
int_slist2.push_back(60);
int_slist = int_slist2;
AZ_TEST_ASSERT(int_slist == int_slist2);
//int_slist2.pop_back();
//int_slist2.pop_back();
//int_slist = int_slist2;
//AZ_TEST_ASSERT(int_slist==int_slist2);
// assign with iterators (at the moment this uses the function operator= calls)
int_slist2.assign(int_slist1.begin(), int_slist1.end());
AZ_TEST_ASSERT(int_slist2 == int_slist1);
// assign a fixed value
int_slist.assign(5, 55);
AZ_TEST_VALIDATE_LIST(int_slist, 5);
for (forward_list<int>::iterator iter = int_slist.begin(); iter != int_slist.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 55);
}
}
TEST_F(ListContainers, ForwardListInsertEraseClear)
{
forward_list<int> int_slist;
forward_list<int> int_slist1;
int_slist.assign(5, 55);
int_slist1 = forward_list<int>(10, 33);
// resize to bigger
int_slist.resize(6, 43);
AZ_TEST_VALIDATE_LIST(int_slist, 6);
AZ_TEST_ASSERT(int_slist.back() == 43);
// resize to smaller
int_slist.resize(3);
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.back() == 55);
// insert
int_slist.insert(int_slist.begin(), 44);
AZ_TEST_VALIDATE_LIST(int_slist, 4);
AZ_TEST_ASSERT(int_slist.front() == 44);
int_slist.insert(int_slist.end(), 66);
AZ_TEST_VALIDATE_LIST(int_slist, 5);
AZ_TEST_ASSERT(int_slist.back() == 66);
int_slist.insert(int_slist.begin(), 2, 11);
AZ_TEST_VALIDATE_LIST(int_slist, 7);
AZ_TEST_ASSERT(int_slist.front() == 11);
AZ_TEST_ASSERT(*next(int_slist.begin()) == 11);
int_slist.insert(int_slist.end(), 2, 22);
AZ_TEST_VALIDATE_LIST(int_slist, 9);
AZ_TEST_ASSERT(int_slist.back() == 22);
AZ_TEST_ASSERT(*int_slist.previous(int_slist.last()) == 22);
int_slist.insert(int_slist.end(), int_slist1.begin(), int_slist1.end());
AZ_TEST_VALIDATE_LIST(int_slist, 9 + int_slist1.size());
AZ_TEST_ASSERT(int_slist.back() == 33);
// erase
int_slist.assign(2, 10);
int_slist.push_back(20);
int_slist.erase(int_slist.last());
AZ_TEST_VALIDATE_LIST(int_slist, 2);
AZ_TEST_ASSERT(int_slist.back() == 10);
int_slist.insert(int_slist.end(), 3, 44);
int_slist.erase(int_slist.previous(int_slist.last()), int_slist.end());
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.back() == 44);
// clear
int_slist.clear();
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
}
TEST_F(ListContainers, ForwardListSwapSplice)
{
forward_list<int> int_slist;
forward_list<int> int_slist1;
forward_list<int> int_slist2;
int_slist2 = forward_list<int>(10, 33);
// list operations with container with the same allocator.
// swap
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 10);
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
AZ_TEST_VALIDATE_LIST(int_slist2, 10);
int_slist.assign(5, 55);
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 10);
AZ_TEST_VALIDATE_LIST(int_slist2, 5);
AZ_TEST_ASSERT(int_slist.front() == 33);
AZ_TEST_ASSERT(int_slist2.front() == 55);
// splice
// splice(iterator splicePos, this_type& rhs)
int_slist.splice(int_slist.end(), int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 15);
AZ_TEST_ASSERT(int_slist.front() == 33);
AZ_TEST_ASSERT(int_slist.back() == 55);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_slist2.push_back(101);
int_slist.splice(int_slist.begin(), int_slist2, int_slist2.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 16);
AZ_TEST_ASSERT(int_slist.front() == 101);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_slist2.assign(5, 201);
int_slist.splice(int_slist.end(), int_slist2, ++int_slist2.begin(), int_slist2.end());
AZ_TEST_VALIDATE_LIST(int_slist2, 1);
AZ_TEST_VALIDATE_LIST(int_slist, 20);
AZ_TEST_ASSERT(int_slist.back() == 201);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last) the whole vector optimization.
int_slist2.push_back(301);
int_slist.splice(int_slist.end(), int_slist2, int_slist2.begin(), int_slist2.end());
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 22);
AZ_TEST_ASSERT(int_slist.back() == 301);
}
TEST_F(ListContainers, ForwardListRemoveUniqueSort)
{
forward_list<int> int_slist;
// remove
int_slist.assign(5, 101);
int_slist.push_back(201);
int_slist.push_back(301);
int_slist.push_back(401);
int_slist.remove(101);
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.front() == 201);
AZ_TEST_ASSERT(int_slist.back() == 401);
int_slist.remove_if(RemoveLessThan401());
AZ_TEST_VALIDATE_LIST(int_slist, 1);
AZ_TEST_ASSERT(int_slist.back() == 401);
// unique
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(201);
int_slist.push_back(301);
int_slist.unique();
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.front() == 101);
AZ_TEST_ASSERT(int_slist.back() == 301);
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(201);
int_slist.push_back(401);
int_slist.push_back(401);
int_slist.push_back(501);
int_slist.unique(UniqueForLessThan401());
AZ_TEST_VALIDATE_LIST(int_slist, 5);
AZ_TEST_ASSERT(int_slist.front() == 101);
AZ_TEST_ASSERT(int_slist.back() == 501);
// sort
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort();
AZ_TEST_VALIDATE_LIST(int_slist, 4);
for (forward_list<int>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort(AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_slist, 4);
for (forward_list<int>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter >= *next(iter));
}
}
TEST_F(ListContainers, ForwardListReverseMerge)
{
forward_list<int> int_slist;
forward_list<int> int_slist1;
forward_list<int> int_slist2;
forward_list<int> int_slist3;
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort(AZStd::greater<int>());
// reverse
int_slist.reverse();
for (forward_list<int>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
// merge
int_slist.clear();
int_slist1.clear();
int_slist.push_back(1); // 2 sorted lists for merge
int_slist.push_back(10);
int_slist.push_back(50);
int_slist.push_back(200);
int_slist1.push_back(2);
int_slist1.push_back(8);
int_slist1.push_back(60);
int_slist1.push_back(180);
int_slist2 = int_slist;
int_slist3 = int_slist1;
int_slist2.merge(int_slist3);
AZ_TEST_VALIDATE_LIST(int_slist2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist3);
for (forward_list<int>::iterator iter = int_slist2.begin(); iter != int_slist2.last(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_slist.reverse();
int_slist1.reverse();
int_slist2 = int_slist;
int_slist3 = int_slist1;
int_slist2.merge(int_slist3, AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_slist2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist3);
for (forward_list<int>::iterator iter = int_slist2.begin(); iter != int_slist2.last(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
TEST_F(ListContainers, ForwardListExtensions)
{
forward_list<int> int_slist;
// Extensions.
int_slist.clear();
// Push_back()
int_slist.push_back();
AZ_TEST_VALIDATE_LIST(int_slist, 1);
int_slist.front() = 100;
// Push_front()
int_slist.push_front();
AZ_TEST_VALIDATE_LIST(int_slist, 2);
AZ_TEST_ASSERT(int_slist.back() == 100);
// Insert without value to copy from.
int_slist.insert(int_slist.begin());
AZ_TEST_VALIDATE_LIST(int_slist, 3);
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)&int_slist.front() % 4) == 0); // default int alignment
// make sure every allocation is aligned.
forward_list<MyClass> aligned_list(5, MyClass(99));
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_list.front() & (alignment_of<MyClass>::value - 1)) == 0);
}
TEST_F(ListContainers, ForwardListStaticBuffer)
{
// Test swap, splice, merge, etc. of containers with different allocators.
// list operations with container with the same allocator.
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB_type;
static_buffer_16KB_type myMemoryManager1;
static_buffer_16KB_type myMemoryManager2;
typedef allocator_ref<static_buffer_16KB_type> static_allocator_ref_type;
static_allocator_ref_type allocator1(myMemoryManager1, "Mystack allocator 1");
static_allocator_ref_type allocator2(myMemoryManager2, "Mystack allocator 2");
typedef forward_list<MyClass, static_allocator_ref_type> stack_myclass_slist_type;
stack_myclass_slist_type int_slist10(allocator1);
stack_myclass_slist_type int_slist20(allocator2);
int_slist20.assign(10, MyClass(33));
AZ_TEST_VALIDATE_LIST(int_slist20, 10);
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 10 * sizeof(stack_myclass_slist_type::node_type));
int_slist20.leak_and_reset();
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist20);
myMemoryManager2.reset(); // free all memory
// set allocator
int_slist20.assign(20, MyClass(22));
int_slist20.set_allocator(allocator1);
AZ_TEST_VALIDATE_LIST(int_slist20, 20);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 20 * sizeof(stack_myclass_slist_type::node_type));
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 20 * sizeof(stack_myclass_slist_type::node_type));
int_slist20.leak_and_reset();
int_slist20.set_allocator(allocator2);
myMemoryManager1.reset();
myMemoryManager2.reset();
int_slist20.assign(10, MyClass(11));
// swap
int_slist10.swap(int_slist20);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist20);
AZ_TEST_VALIDATE_LIST(int_slist10, 10);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() >= 10 * sizeof(stack_myclass_slist_type::node_type));
AZ_TEST_ASSERT(myMemoryManager2.get_allocated_size() >= 10 * sizeof(stack_myclass_slist_type::node_type));
int_slist10.swap(int_slist20);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist10);
AZ_TEST_VALIDATE_LIST(int_slist20, 10);
int_slist10.assign(5, 55);
int_slist10.swap(int_slist20);
AZ_TEST_VALIDATE_LIST(int_slist10, 10);
AZ_TEST_VALIDATE_LIST(int_slist20, 5);
AZ_TEST_ASSERT(int_slist10.front() == MyClass(11));
AZ_TEST_ASSERT(int_slist20.front() == MyClass(55));
// splice
// splice(iterator splicePos, this_type& rhs)
int_slist10.splice(int_slist10.end(), int_slist20);
AZ_TEST_VALIDATE_LIST(int_slist10, 15);
AZ_TEST_ASSERT(int_slist10.front() == MyClass(11));
AZ_TEST_ASSERT(int_slist10.back() == MyClass(55));
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist20);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_slist20.push_back(MyClass(101));
int_slist10.splice(int_slist10.begin(), int_slist20, int_slist20.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist20);
AZ_TEST_VALIDATE_LIST(int_slist10, 16);
AZ_TEST_ASSERT(int_slist10.front() == MyClass(101));
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_slist20.assign(5, MyClass(201));
int_slist10.splice(int_slist10.end(), int_slist20, ++int_slist20.begin(), int_slist20.end());
AZ_TEST_VALIDATE_LIST(int_slist20, 1);
AZ_TEST_VALIDATE_LIST(int_slist10, 20);
AZ_TEST_ASSERT(int_slist10.back() == MyClass(201));
int_slist10.leak_and_reset();
int_slist20.leak_and_reset();
myMemoryManager1.reset();
myMemoryManager2.reset();
// merge
stack_myclass_slist_type int_slist30(allocator1);
stack_myclass_slist_type int_slist40(allocator2);
int_slist10.push_back(MyClass(1)); // 2 sorted lists for merge
int_slist10.push_back(MyClass(10));
int_slist10.push_back(MyClass(50));
int_slist10.push_back(MyClass(200));
int_slist20.push_back(MyClass(2));
int_slist20.push_back(MyClass(8));
int_slist20.push_back(MyClass(60));
int_slist20.push_back(MyClass(180));
int_slist30 = int_slist10;
int_slist40 = int_slist20;
int_slist30.merge(int_slist40);
AZ_TEST_VALIDATE_LIST(int_slist30, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist40);
for (stack_myclass_slist_type::iterator iter = int_slist30.begin(); iter != int_slist30.last(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_slist10.reverse();
int_slist20.reverse();
int_slist30 = int_slist10;
int_slist40 = int_slist20;
int_slist30.merge(int_slist40, AZStd::greater<MyClass>());
AZ_TEST_VALIDATE_LIST(int_slist30, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist40);
for (stack_myclass_slist_type::iterator iter = int_slist30.begin(); iter != int_slist30.last(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
}
#undef AZ_TEST_VALIDATE_EMPTY_LIST
#undef AZ_TEST_VALIDATE_LIST
@@ -0,0 +1,688 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/fixed_list.h>
#include <AzCore/std/containers/fixed_forward_list.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/containers/array.h>
using namespace AZStd;
using namespace UnitTestInternal;
#define AZ_TEST_VALIDATE_EMPTY_LIST(_List) \
AZ_TEST_ASSERT(_List.validate()); \
AZ_TEST_ASSERT(_List.size() == 0); \
AZ_TEST_ASSERT(_List.begin() == _List.end()); \
AZ_TEST_ASSERT(_List.empty());
#define AZ_TEST_VALIDATE_LIST(_List, _NumElements) \
AZ_TEST_ASSERT(_List.validate()); \
AZ_TEST_ASSERT(_List.size() == _NumElements); \
AZ_TEST_ASSERT((_NumElements > 0) ? !_List.empty() : _List.empty()); \
AZ_TEST_ASSERT((_NumElements > 0) ? _List.begin() != _List.end() : _List.begin() == _List.end()); \
AZ_TEST_ASSERT(!_List.empty());
namespace UnitTest
{
class FixedListContainers
: public AllocatorsFixture
{
public:
struct RemoveLessThan401
{
AZ_FORCE_INLINE bool operator()(int element) const { return element < 401; }
};
struct UniqueForLessThan401
{
AZ_FORCE_INLINE bool operator()(int el1, int el2) const { return (el1 == el2 && el1 < 401); }
};
};
TEST_F(FixedListContainers, ListCtorAssign)
{
fixed_list<int, 100> int_list;
fixed_list<int, 100> int_list1;
fixed_list<int, 100> int_list2;
fixed_list<int, 100> int_list3;
fixed_list<int, 100> int_list4;
// default ctor
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
// 10 int elements, value 33
int_list1 = fixed_list<int, 100>(10, 33);
AZ_TEST_VALIDATE_LIST(int_list1, 10);
for (fixed_list<int, 100>::iterator iter = int_list1.begin(); iter != int_list1.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy list 1 using, first,last,allocator.
int_list2 = fixed_list<int, 100>(int_list1.begin(), int_list1.end());
AZ_TEST_VALIDATE_LIST(int_list2, 10);
for (fixed_list<int, 100>::iterator iter = int_list2.begin(); iter != int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy construct
int_list3 = fixed_list<int, 100>(int_list1);
AZ_TEST_ASSERT(int_list1 == int_list3);
// initializer_list construct
int_list4 = { 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 };
AZ_TEST_ASSERT(int_list1 == int_list4);
// assign
int_list = int_list1;
AZ_TEST_ASSERT(int_list == int_list1);
int_list2.push_back(60);
int_list = int_list2;
AZ_TEST_ASSERT(int_list == int_list2);
int_list2.pop_back();
int_list2.pop_back();
int_list = int_list2;
AZ_TEST_ASSERT(int_list == int_list2);
// assign with iterators (at the moment this uses the function operator= calls)
int_list2.assign(int_list1.begin(), int_list1.end());
AZ_TEST_ASSERT(int_list2 == int_list1);
// assign a fixed value
int_list.assign(5, 55);
AZ_TEST_VALIDATE_LIST(int_list, 5);
for (fixed_list<int, 100>::iterator iter = int_list.begin(); iter != int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 55);
}
}
TEST_F(FixedListContainers, ListResizeInsertErase)
{
fixed_list<int, 100> int_list;
fixed_list<int, 100> int_list1;
int_list.assign(5, 55);
int_list1 = fixed_list<int, 100>(10, 33);
// resize to bigger
int_list.resize(6, 43);
AZ_TEST_VALIDATE_LIST(int_list, 6);
AZ_TEST_ASSERT(int_list.back() == 43);
AZ_TEST_ASSERT(*prev(int_list.end(), 2) == 55);
AZ_TEST_ASSERT(*int_list.begin() == *prev(int_list.rend()));
// resize to smaller
int_list.resize(3);
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.back() == 55);
// insert
int_list.insert(int_list.begin(), 44);
AZ_TEST_VALIDATE_LIST(int_list, 4);
AZ_TEST_ASSERT(int_list.front() == 44);
int_list.insert(int_list.end(), 66);
AZ_TEST_VALIDATE_LIST(int_list, 5);
AZ_TEST_ASSERT(int_list.back() == 66);
int_list.insert(int_list.begin(), 2, 11);
AZ_TEST_VALIDATE_LIST(int_list, 7);
AZ_TEST_ASSERT(int_list.front() == 11);
AZ_TEST_ASSERT(*next(int_list.begin()) == 11);
int_list.insert(int_list.end(), 2, 22);
AZ_TEST_VALIDATE_LIST(int_list, 9);
AZ_TEST_ASSERT(int_list.back() == 22);
AZ_TEST_ASSERT(*prev(int_list.end(), 2) == 22);
int_list.insert(int_list.end(), int_list1.begin(), int_list1.end());
AZ_TEST_VALIDATE_LIST(int_list, 9 + int_list1.size());
AZ_TEST_ASSERT(int_list.back() == 33);
// erase
int_list.assign(2, 10);
int_list.push_back(20);
int_list.erase(--int_list.end());
AZ_TEST_VALIDATE_LIST(int_list, 2);
AZ_TEST_ASSERT(int_list.back() == 10);
int_list.insert(int_list.end(), 3, 44);
int_list.erase(prev(int_list.end(), 2), int_list.end());
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.back() == 44);
// clear
int_list.clear();
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
}
TEST_F(FixedListContainers, ListSwapSplice)
{
fixed_list<int, 100> int_list;
fixed_list<int, 100> int_list1;
fixed_list<int, 100> int_list2;
int_list2 = fixed_list<int, 100>(10, 33);
// list operations with container with the same allocator.
// swap
int_list.swap(int_list2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 10);
int_list.swap(int_list2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list);
AZ_TEST_VALIDATE_LIST(int_list2, 10);
int_list.assign(5, 55);
int_list.swap(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 10);
AZ_TEST_VALIDATE_LIST(int_list2, 5);
AZ_TEST_ASSERT(int_list.front() == 33);
AZ_TEST_ASSERT(int_list2.front() == 55);
// splice
// splice(iterator splicePos, this_type& rhs)
int_list.splice(int_list.end(), int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 15);
AZ_TEST_ASSERT(int_list.front() == 33);
AZ_TEST_ASSERT(int_list.back() == 55);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_list2.push_back(101);
int_list.splice(int_list.begin(), int_list2, int_list2.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 16);
AZ_TEST_ASSERT(int_list.front() == 101);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_list2.assign(5, 201);
int_list.splice(int_list.end(), int_list2, ++int_list2.begin(), int_list2.end());
AZ_TEST_VALIDATE_LIST(int_list2, 1);
AZ_TEST_VALIDATE_LIST(int_list, 20);
AZ_TEST_ASSERT(int_list.back() == 201);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last) the whole vector optimization.
int_list2.push_back(301);
int_list.splice(int_list.end(), int_list2, int_list2.begin(), int_list2.end());
AZ_TEST_VALIDATE_EMPTY_LIST(int_list2);
AZ_TEST_VALIDATE_LIST(int_list, 22);
AZ_TEST_ASSERT(int_list.back() == 301);
}
TEST_F(FixedListContainers, ListRemoveUniqueSort)
{
fixed_list<int, 100> int_list;
// remove
int_list.assign(5, 101);
int_list.push_back(201);
int_list.push_back(301);
int_list.push_back(401);
int_list.remove(101);
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.front() == 201);
AZ_TEST_ASSERT(int_list.back() == 401);
int_list.remove_if(RemoveLessThan401());
AZ_TEST_VALIDATE_LIST(int_list, 1);
AZ_TEST_ASSERT(int_list.back() == 401);
// unique
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(201);
int_list.push_back(301);
int_list.unique();
AZ_TEST_VALIDATE_LIST(int_list, 3);
AZ_TEST_ASSERT(int_list.front() == 101);
AZ_TEST_ASSERT(int_list.back() == 301);
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(201);
int_list.push_back(401);
int_list.push_back(401);
int_list.push_back(501);
int_list.unique(UniqueForLessThan401());
AZ_TEST_VALIDATE_LIST(int_list, 5);
AZ_TEST_ASSERT(int_list.front() == 101);
AZ_TEST_ASSERT(int_list.back() == 501);
// sort
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(1);
int_list.sort();
AZ_TEST_VALIDATE_LIST(int_list, 4);
for (fixed_list<int, 100>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(1);
int_list.sort(AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_list, 4);
for (fixed_list<int, 100>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter >= *next(iter));
}
}
TEST_F(FixedListContainers, ListReverseMerge)
{
fixed_list<int, 100> int_list;
fixed_list<int, 100> int_list1;
fixed_list<int, 100> int_list2;
fixed_list<int, 100> int_list3;
int_list.assign(2, 101);
int_list.push_back(201);
int_list.push_back(1);
int_list.sort(AZStd::greater<int>());
// reverse
int_list.reverse();
for (fixed_list<int, 100>::iterator iter = int_list.begin(); iter != --int_list.end(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
// merge
int_list.clear();
int_list1.clear();
int_list.push_back(1); // 2 sorted lists for merge
int_list.push_back(10);
int_list.push_back(50);
int_list.push_back(200);
int_list1.push_back(2);
int_list1.push_back(8);
int_list1.push_back(60);
int_list1.push_back(180);
int_list2 = int_list;
int_list3 = int_list1;
int_list2.merge(int_list3);
AZ_TEST_VALIDATE_LIST(int_list2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list3);
for (fixed_list<int, 100>::iterator iter = int_list2.begin(); iter != --int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_list.reverse();
int_list1.reverse();
int_list2 = int_list;
int_list3 = int_list1;
int_list2.merge(int_list3, AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_list2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_list3);
for (fixed_list<int, 100>::iterator iter = int_list2.begin(); iter != --int_list2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
TEST_F(FixedListContainers, ListExtensions)
{
fixed_list<int, 100> int_list;
// Push_back()
int_list.push_back();
AZ_TEST_VALIDATE_LIST(int_list, 1);
int_list.front() = 100;
// Push_front()
int_list.push_front();
AZ_TEST_VALIDATE_LIST(int_list, 2);
AZ_TEST_ASSERT(int_list.back() == 100);
// Insert without value to copy from.
int_list.insert(int_list.begin());
AZ_TEST_VALIDATE_LIST(int_list, 3);
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)&int_list.front() % 4) == 0); // default int alignment
// make sure every allocation is aligned.
fixed_list<MyClass, 100> aligned_list(5, MyClass(99));
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_list.front() & (alignment_of<MyClass>::value - 1)) == 0);
}
TEST_F(FixedListContainers, ForwardListCtorAssign)
{
fixed_forward_list<int, 100> int_slist;
fixed_forward_list<int, 100> int_slist1;
fixed_forward_list<int, 100> int_slist2;
fixed_forward_list<int, 100> int_slist3;
fixed_forward_list<int, 100> int_slist4;
// default ctor
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
// 10 int elements, value 33
int_slist1 = fixed_forward_list<int, 100>(10, 33);
AZ_TEST_VALIDATE_LIST(int_slist1, 10);
for (fixed_forward_list<int, 100>::iterator iter = int_slist1.begin(); iter != int_slist1.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy list 1 using, first,last,allocator.
int_slist2 = fixed_forward_list<int, 100>(int_slist1.begin(), int_slist1.end());
AZ_TEST_VALIDATE_LIST(int_slist2, 10);
for (fixed_forward_list<int, 100>::iterator iter = int_slist2.begin(); iter != int_slist2.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 33);
}
// copy construct
int_slist3 = fixed_forward_list<int, 100>(int_slist1);
AZ_TEST_ASSERT(int_slist1 == int_slist3);
// initializer_list construct
int_slist4 = { 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 };
AZ_TEST_ASSERT(int_slist1 == int_slist4);
// assign
int_slist = int_slist1;
AZ_TEST_ASSERT(int_slist == int_slist1);
int_slist2.push_back(60);
int_slist = int_slist2;
AZ_TEST_ASSERT(int_slist == int_slist2);
//int_slist2.pop_back();
//int_slist2.pop_back();
//int_slist = int_slist2;
//AZ_TEST_ASSERT(int_slist==int_slist2);
// assign with iterators (at the moment this uses the function operator= calls)
int_slist2.assign(int_slist1.begin(), int_slist1.end());
AZ_TEST_ASSERT(int_slist2 == int_slist1);
// assign a fixed value
int_slist.assign(5, 55);
AZ_TEST_VALIDATE_LIST(int_slist, 5);
for (fixed_forward_list<int, 100>::iterator iter = int_slist.begin(); iter != int_slist.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 55);
}
}
TEST_F(FixedListContainers, ForwardListResizeInsertErase)
{
fixed_forward_list<int, 100> int_slist;
fixed_forward_list<int, 100> int_slist1;
int_slist.assign(5, 55);
int_slist1 = fixed_forward_list<int, 100>(10, 33);
// resize to bigger
int_slist.resize(6, 43);
AZ_TEST_VALIDATE_LIST(int_slist, 6);
AZ_TEST_ASSERT(int_slist.back() == 43);
// resize to smaller
int_slist.resize(3);
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.back() == 55);
// insert
int_slist.insert(int_slist.begin(), 44);
AZ_TEST_VALIDATE_LIST(int_slist, 4);
AZ_TEST_ASSERT(int_slist.front() == 44);
int_slist.insert(int_slist.end(), 66);
AZ_TEST_VALIDATE_LIST(int_slist, 5);
AZ_TEST_ASSERT(int_slist.back() == 66);
int_slist.insert(int_slist.begin(), 2, 11);
AZ_TEST_VALIDATE_LIST(int_slist, 7);
AZ_TEST_ASSERT(int_slist.front() == 11);
AZ_TEST_ASSERT(*next(int_slist.begin()) == 11);
int_slist.insert(int_slist.end(), 2, 22);
AZ_TEST_VALIDATE_LIST(int_slist, 9);
AZ_TEST_ASSERT(int_slist.back() == 22);
AZ_TEST_ASSERT(*int_slist.previous(int_slist.last()) == 22);
int_slist.insert(int_slist.end(), int_slist1.begin(), int_slist1.end());
AZ_TEST_VALIDATE_LIST(int_slist, 9 + int_slist1.size());
AZ_TEST_ASSERT(int_slist.back() == 33);
// erase
int_slist.assign(2, 10);
int_slist.push_back(20);
int_slist.erase(int_slist.last());
AZ_TEST_VALIDATE_LIST(int_slist, 2);
AZ_TEST_ASSERT(int_slist.back() == 10);
int_slist.insert(int_slist.end(), 3, 44);
int_slist.erase(int_slist.previous(int_slist.last()), int_slist.end());
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.back() == 44);
// clear
int_slist.clear();
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
}
TEST_F(FixedListContainers, ForwardListSwapSplice)
{
fixed_forward_list<int, 100> int_slist;
fixed_forward_list<int, 100> int_slist1;
fixed_forward_list<int, 100> int_slist2;
int_slist2 = fixed_forward_list<int, 100>(10, 33);
// list operations with container with the same allocator.
// swap
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 10);
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist);
AZ_TEST_VALIDATE_LIST(int_slist2, 10);
int_slist.assign(5, 55);
int_slist.swap(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 10);
AZ_TEST_VALIDATE_LIST(int_slist2, 5);
AZ_TEST_ASSERT(int_slist.front() == 33);
AZ_TEST_ASSERT(int_slist2.front() == 55);
// splice
// splice(iterator splicePos, this_type& rhs)
int_slist.splice(int_slist.end(), int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 15);
AZ_TEST_ASSERT(int_slist.front() == 33);
AZ_TEST_ASSERT(int_slist.back() == 55);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
// splice(iterator splicePos, this_type& rhs, iterator first)
int_slist2.push_back(101);
int_slist.splice(int_slist.begin(), int_slist2, int_slist2.begin());
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 16);
AZ_TEST_ASSERT(int_slist.front() == 101);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last)
int_slist2.assign(5, 201);
int_slist.splice(int_slist.end(), int_slist2, ++int_slist2.begin(), int_slist2.end());
AZ_TEST_VALIDATE_LIST(int_slist2, 1);
AZ_TEST_VALIDATE_LIST(int_slist, 20);
AZ_TEST_ASSERT(int_slist.back() == 201);
// splice(iterator splicePos, this_type& rhs, iterator first, iterator last) the whole vector optimization.
int_slist2.push_back(301);
int_slist.splice(int_slist.end(), int_slist2, int_slist2.begin(), int_slist2.end());
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist2);
AZ_TEST_VALIDATE_LIST(int_slist, 22);
AZ_TEST_ASSERT(int_slist.back() == 301);
}
TEST_F(FixedListContainers, ForwardListRemoveUniqueSort)
{
fixed_forward_list<int, 100> int_slist;
// remove
int_slist.assign(5, 101);
int_slist.push_back(201);
int_slist.push_back(301);
int_slist.push_back(401);
int_slist.remove(101);
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.front() == 201);
AZ_TEST_ASSERT(int_slist.back() == 401);
int_slist.remove_if(RemoveLessThan401());
AZ_TEST_VALIDATE_LIST(int_slist, 1);
AZ_TEST_ASSERT(int_slist.back() == 401);
// unique
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(201);
int_slist.push_back(301);
int_slist.unique();
AZ_TEST_VALIDATE_LIST(int_slist, 3);
AZ_TEST_ASSERT(int_slist.front() == 101);
AZ_TEST_ASSERT(int_slist.back() == 301);
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(201);
int_slist.push_back(401);
int_slist.push_back(401);
int_slist.push_back(501);
int_slist.unique(UniqueForLessThan401());
AZ_TEST_VALIDATE_LIST(int_slist, 5);
AZ_TEST_ASSERT(int_slist.front() == 101);
AZ_TEST_ASSERT(int_slist.back() == 501);
// sort
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort();
AZ_TEST_VALIDATE_LIST(int_slist, 4);
for (fixed_forward_list<int, 100>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort(AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_slist, 4);
for (fixed_forward_list<int, 100>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter >= *next(iter));
}
}
TEST_F(FixedListContainers, ForwardListReverseMerge)
{
fixed_forward_list<int, 100> int_slist;
fixed_forward_list<int, 100> int_slist1;
fixed_forward_list<int, 100> int_slist2;
fixed_forward_list<int, 100> int_slist3;
int_slist.assign(2, 101);
int_slist.push_back(201);
int_slist.push_back(1);
int_slist.sort(AZStd::greater<int>());
// reverse
int_slist.reverse();
for (fixed_forward_list<int, 100>::iterator iter = int_slist.begin(); iter != int_slist.last(); ++iter)
{
AZ_TEST_ASSERT(*iter <= *next(iter));
}
// merge
int_slist.clear();
int_slist1.clear();
int_slist.push_back(1); // 2 sorted lists for merge
int_slist.push_back(10);
int_slist.push_back(50);
int_slist.push_back(200);
int_slist1.push_back(2);
int_slist1.push_back(8);
int_slist1.push_back(60);
int_slist1.push_back(180);
int_slist2 = int_slist;
int_slist3 = int_slist1;
int_slist2.merge(int_slist3);
AZ_TEST_VALIDATE_LIST(int_slist2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist3);
for (fixed_forward_list<int, 100>::iterator iter = int_slist2.begin(); iter != int_slist2.last(); ++iter)
{
AZ_TEST_ASSERT(*iter < *next(iter));
}
int_slist.reverse();
int_slist1.reverse();
int_slist2 = int_slist;
int_slist3 = int_slist1;
int_slist2.merge(int_slist3, AZStd::greater<int>());
AZ_TEST_VALIDATE_LIST(int_slist2, 8);
AZ_TEST_VALIDATE_EMPTY_LIST(int_slist3);
for (fixed_forward_list<int, 100>::iterator iter = int_slist2.begin(); iter != int_slist2.last(); ++iter)
{
AZ_TEST_ASSERT(*iter > *next(iter));
}
}
TEST_F(FixedListContainers, ForwardListExtensions)
{
fixed_forward_list<int, 100> int_slist;
// Push_back()
int_slist.push_back();
AZ_TEST_VALIDATE_LIST(int_slist, 1);
int_slist.front() = 100;
// Push_front()
int_slist.push_front();
AZ_TEST_VALIDATE_LIST(int_slist, 2);
AZ_TEST_ASSERT(int_slist.back() == 100);
// Insert without value to copy from.
int_slist.insert(int_slist.begin());
AZ_TEST_VALIDATE_LIST(int_slist, 3);
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)&int_slist.front() % 4) == 0); // default int alignment
// make sure every allocation is aligned.
fixed_forward_list<MyClass, 100> aligned_list(5, MyClass(99));
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_list.front() & (alignment_of<MyClass>::value - 1)) == 0);
}
}
#undef AZ_TEST_VALIDATE_EMPTY_LIST
#undef AZ_TEST_VALIDATE_LIST
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,231 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/parallel/containers/lock_free_queue.h>
#include <AzCore/std/parallel/containers/lock_free_stamped_queue.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
class LockFreeQueue
: public AllocatorsFixture
{
public:
LockFreeQueue() : AllocatorsFixture() {}
protected:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 5000;
#else
static const int NUM_ITERATIONS = 100000;
#endif
public:
template <class Q>
void Push(Q* queue)
{
for (int i = 0; i < NUM_ITERATIONS; ++i)
{
queue->push(i);
}
}
template <class Q>
void Pop(Q* queue)
{
int expected = 0;
while (expected < NUM_ITERATIONS)
{
typename Q::value_type value = NUM_ITERATIONS;
if (queue->pop(&value))
{
if (value == expected)
{
++m_counter;
}
++expected;
}
}
}
atomic<int> m_counter;
};
TEST_F(LockFreeQueue, LockFreeQueue)
{
lock_free_queue<int, MyLockFreeAllocator> queue;
int result;
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
queue.push(30);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
{
m_counter = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
AZ_TEST_ASSERT(queue.empty());
}
}
struct SharedInt {
SharedInt() : m_ptr(nullptr) {}
SharedInt(int i) : m_ptr(new int(i)) {}
bool operator==(const SharedInt& other)
{
if (!m_ptr || !other.m_ptr)
{
return false;
}
return *m_ptr == *other.m_ptr;
}
private:
AZStd::shared_ptr<int> m_ptr;
};
TEST_F(LockFreeQueue, LockFreeQueueNonTrivialDestructor)
{
lock_free_queue<SharedInt, MyLockFreeAllocator> queue;
SharedInt result;
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
queue.push(30);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
{
m_counter = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
AZ_TEST_ASSERT(queue.empty());
}
}
TEST_F(LockFreeQueue, LockFreeStampedQueue)
{
lock_free_stamped_queue<int, MyLockFreeAllocator> queue;
int result;
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
queue.push(30);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
{
m_counter = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
AZ_TEST_ASSERT(queue.empty());
}
}
TEST_F(LockFreeQueue, LockFreeStampedQueueNonTrivialDestructor)
{
lock_free_stamped_queue<SharedInt, MyLockFreeAllocator> queue;
SharedInt result;
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
queue.push(20);
queue.push(30);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(!queue.empty());
AZ_TEST_ASSERT(queue.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(queue.empty());
AZ_TEST_ASSERT(!queue.pop(&result));
{
m_counter = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
AZ_TEST_ASSERT(queue.empty());
}
}
}
@@ -0,0 +1,384 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/parallel/containers/lock_free_stack.h>
#include <AzCore/std/parallel/containers/lock_free_stamped_stack.h>
#include <AzCore/std/parallel/containers/lock_free_intrusive_stack.h>
#include <AzCore/std/parallel/containers/lock_free_intrusive_stamped_stack.h>
#include <AzCore/std/parallel/thread.h>
#include <AzCore/std/functional.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
class LockFreeStack
: public AllocatorsFixture
{
public:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 5000;
#else
static const int NUM_ITERATIONS = 50000;
#endif
template <class S>
void Push(S* stack)
{
for (int i = 1; i <= NUM_ITERATIONS; ++i)
{
stack->push(i);
m_total.fetch_add(i);
}
}
template <class S>
void Pop(S* stack)
{
int numPopped = 0;
while (numPopped != NUM_ITERATIONS)
{
int value = 0;
if (stack->pop(&value))
{
m_total.fetch_sub(value);
++numPopped;
}
else
{
// for some schedulers we need the yield otherwise we will deadlock
AZStd::this_thread::yield();
}
}
}
atomic<int> m_total;
};
TEST_F(LockFreeStack, LockFreeStack)
{
lock_free_stack<int, MyLockFreeAllocator> stack;
int result = 0;
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
stack.push(20);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
stack.push(20);
stack.push(30);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
AZStd::thread thread1(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
AZStd::thread thread2(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
AZStd::thread thread3(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
TEST_F(LockFreeStack, LockFreeStampedStack)
{
lock_free_stamped_stack<int, MyLockFreeAllocator> stack;
int result = 0;
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
stack.push(20);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
stack.push(20);
stack.push(30);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 30);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop(&result));
AZ_TEST_ASSERT(result == 20);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop(&result));
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
AZStd::thread thread1(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
AZStd::thread thread2(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
AZStd::thread thread3(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
thread2.join();
thread3.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
struct MyStackItem
: public lock_free_intrusive_stack_node<MyStackItem>
{
public:
MyStackItem() {}
MyStackItem(int data)
: m_data(data) {}
lock_free_intrusive_stack_node<MyStackItem> m_listHook; // Public member hook.
int m_data; // This is left public only for testing purpose.
};
class LockFreeIntrusiveStack
: public AllocatorsFixture
{
public:
#ifdef _DEBUG
static const int NUM_ITERATIONS = 5000;
#else
static const int NUM_ITERATIONS = 50000;
#endif
template <class S>
void Push(S* stack, vector<MyStackItem>* items)
{
for (int i = 0; i < NUM_ITERATIONS; ++i)
{
stack->push((*items)[i]);
m_total.fetch_add((*items)[i].m_data);
}
}
template <class S>
void Pop(S* stack)
{
int numPopped = 0;
while (numPopped != NUM_ITERATIONS)
{
MyStackItem* item = stack->pop();
if (item)
{
m_total.fetch_sub(item->m_data);
++numPopped;
}
}
}
atomic<int> m_total;
};
typedef lock_free_intrusive_stack<MyStackItem,
lock_free_intrusive_stack_base_hook<MyStackItem> > MyIntrusiveStackBase;
typedef lock_free_intrusive_stack<MyStackItem,
lock_free_intrusive_stack_member_hook<MyStackItem, & MyStackItem::m_listHook> > MyIntrusiveStackMember;
typedef lock_free_intrusive_stamped_stack<MyStackItem,
lock_free_intrusive_stack_base_hook<MyStackItem> > MyIntrusiveStampedStackBase;
typedef lock_free_intrusive_stamped_stack<MyStackItem,
lock_free_intrusive_stack_member_hook<MyStackItem, & MyStackItem::m_listHook> > MyIntrusiveStampedStackMember;
TEST_F(LockFreeIntrusiveStack, MyIntrusiveStackBase)
{
MyIntrusiveStackBase stack;
MyStackItem item1(100);
MyStackItem item2(200);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
stack.push(item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
vector<MyStackItem> items;
items.reserve(NUM_ITERATIONS);
for (int i = 1; i <= NUM_ITERATIONS; ++i)
{
items.push_back(MyStackItem(i));
}
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
TEST_F(LockFreeIntrusiveStack, MyIntrusiveStackMember)
{
MyIntrusiveStackMember stack;
MyStackItem item1(100);
MyStackItem item2(200);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
stack.push(item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
vector<MyStackItem> items;
items.reserve(NUM_ITERATIONS);
for (int i = 1; i <= NUM_ITERATIONS; ++i)
{
items.push_back(MyStackItem(i));
}
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
TEST_F(LockFreeIntrusiveStack, MyIntrusiveStampedStackBase)
{
MyIntrusiveStampedStackBase stack;
MyStackItem item1(100);
MyStackItem item2(200);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
stack.push(item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
vector<MyStackItem> items;
items.reserve(NUM_ITERATIONS);
for (int i = 1; i <= NUM_ITERATIONS; ++i)
{
items.push_back(MyStackItem(i));
}
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
TEST_F(LockFreeIntrusiveStack, MyIntrusiveStampedStackMember)
{
MyIntrusiveStampedStackMember stack;
MyStackItem item1(100);
MyStackItem item2(200);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
stack.push(item1);
stack.push(item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item2);
AZ_TEST_ASSERT(!stack.empty());
AZ_TEST_ASSERT(stack.pop() == &item1);
AZ_TEST_ASSERT(stack.empty());
AZ_TEST_ASSERT(!stack.pop());
vector<MyStackItem> items;
items.reserve(NUM_ITERATIONS);
for (int i = 1; i <= NUM_ITERATIONS; ++i)
{
items.push_back(MyStackItem(i));
}
{
m_total = 0;
AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
thread0.join();
thread1.join();
AZ_TEST_ASSERT(m_total == 0);
AZ_TEST_ASSERT(stack.empty());
}
}
}
@@ -0,0 +1,175 @@
/*
* 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 <AzCore/std/parallel/lock.h>
#include "UserTypes.h"
namespace UnitTest
{
// Fixture for non-typed tests
class LockTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
struct LockableBoolHelper
{
LockableBoolHelper() = default;
void lock()
{
m_locked = true;
}
void unlock()
{
m_locked = false;
}
bool try_lock()
{
if (!m_locked)
{
m_locked = true;
return true;
}
return false;
}
bool m_locked{};
};
struct TryLockSetBoolFalse
{
TryLockSetBoolFalse() = default;
void lock()
{
m_locked = true;
}
void unlock()
{
m_locked = false;
}
bool try_lock()
{
m_locked = false;
return m_locked;
}
bool m_locked{};
};
static uint32_t m_nonAtomicAccumulator1{ 0 };
static uint32_t m_nonAtomicAccumulator2{ 0 };
template<typename Lockable1, typename Lockable2>
static void Thread1Test(Lockable1& lockable1, Lockable2& lockable2)
{
LockableBoolHelper lockableBoolHelper;
AZStd::lock(lockable1, lockable2, lockableBoolHelper);
m_nonAtomicAccumulator1 += 2;
m_nonAtomicAccumulator2 += 3;
EXPECT_TRUE(lockableBoolHelper.m_locked);
lockable2.unlock();
lockable1.unlock();
}
template<typename Lockable1, typename Lockable2>
static void Thread2Test(Lockable1& lockable1, Lockable2& lockable2)
{
LockableBoolHelper lockableBoolHelper;
AZStd::lock(lockable1, lockableBoolHelper, lockable2);
m_nonAtomicAccumulator1 += 5;
m_nonAtomicAccumulator2 += 1;
EXPECT_TRUE(lockableBoolHelper.m_locked);
lockable1.unlock();
lockable2.unlock();
}
TEST_F(LockTest, lock_LockTwoMutexesAtOnce_BothLockedSuccess)
{
// lockable bool helper case
LockableBoolHelper testLockable1;
LockableBoolHelper testLockable2;
AZStd::lock(testLockable1, testLockable2);
EXPECT_TRUE(testLockable1.m_locked);
EXPECT_TRUE(testLockable2.m_locked);
}
TEST_F(LockTest, lock_LockMultipleMutexesAtOnceOnMultipleThreadsInDifferentOrders_LockedWithoutDeadlock)
{
// Multi thread case
constexpr size_t numThreads = 2;
AZStd::mutex testMutex1;
AZStd::mutex testMutex2;
AZStd::thread threads[numThreads];
threads[0] = AZStd::thread([&mutex1 = testMutex1, &mutex2 = testMutex2]()
{
Thread1Test(mutex1, mutex2);
});
threads[1] = AZStd::thread([&mutex1 = testMutex1, &mutex2 = testMutex2]()
{
Thread2Test(mutex2, mutex1);
});
threads[0].join();
threads[1].join();
}
TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnce_AllMutexesAreLocked)
{
LockableBoolHelper lockableBool1;
LockableBoolHelper lockableBool2;
EXPECT_EQ(-1, AZStd::try_lock(lockableBool1, lockableBool2));
// LockableBoolHelper locks are still engaged, therefore lockable bool should fail first
EXPECT_EQ(0, AZStd::try_lock(lockableBool1, lockableBool2));
lockableBool1.unlock();
EXPECT_EQ(1, AZStd::try_lock(lockableBool1, lockableBool2));
lockableBool2.unlock();
}
TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnceWith_TryLockSetBoolFalse_Type_ThatAlwaysReturnsFalseForTryLock_TryLockReturnsMutexThatFailedToLock)
{
LockableBoolHelper lockableBool1;
LockableBoolHelper lockableBool2;
TryLockSetBoolFalse lockableTryLockFalse1;
EXPECT_EQ(1, AZStd::try_lock(lockableBool2, lockableTryLockFalse1, lockableBool1));
}
TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnceWithMutexThatIsLocked_TryLockReturnsFirstMutexThatHasBeenLocked)
{
LockableBoolHelper lockableBool1;
LockableBoolHelper lockableBool2;
TryLockSetBoolFalse lockableTryLockFalse1;
EXPECT_EQ(2, AZStd::try_lock(lockableBool1, lockableBool2, lockableTryLockFalse1));
lockableBool1.lock();
// Note lockableBool1 has been swapped with the lockableBool2 parameter and locked
EXPECT_EQ(1, AZStd::try_lock(lockableBool2, lockableBool1, lockableTryLockFalse1));
}
}
@@ -0,0 +1,55 @@
/*
* 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 <AzCore/std/numeric.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
class AccumulateFixture
: public AllocatorsTestFixture
{
};
TEST_F(AccumulateFixture, AccumulateWithoutBinaryOperator)
{
using ::testing::Eq;
AZStd::vector<int> numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const int total = AZStd::accumulate(AZStd::cbegin(numbers), AZStd::cend(numbers), 0);
EXPECT_THAT(total, Eq(55));
}
TEST_F(AccumulateFixture, AccumulateWithBinaryOperator)
{
using ::testing::Eq;
using ::testing::ElementsAre;
const AZStd::vector<int> numbers{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
const AZStd::vector<int> evenNumbers =
AZStd::accumulate(
AZStd::cbegin(numbers), AZStd::cend(numbers), AZStd::vector<int>{},
[](AZStd::vector<int> acc, const int number)
{
if (number % 2 == 0)
{
acc.push_back(number);
}
return acc;
});
EXPECT_THAT(evenNumbers.size(), Eq(5));
EXPECT_THAT(evenNumbers, ElementsAre(2, 4, 6, 8, 10));
}
} // namespace UnitTest
@@ -0,0 +1,136 @@
/*
* 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 <AzCore/std/hash.h>
#include <AzCore/std/optional.h>
#include "UserTypes.h"
using AZStd::optional;
using AZStd::nullopt;
using AZStd::in_place;
namespace OptionalTestClasses
{
class NonTriviallyDestructableClass
{
public:
~NonTriviallyDestructableClass() {}
};
class NonTriviallyConstructibleClass
{
public:
NonTriviallyConstructibleClass() {}
};
class ConstructibleClass
{
public:
enum Tag
{
TheTag
};
ConstructibleClass(Tag) {}
};
class ConstructibleWithInitializerListClass
{
public:
ConstructibleWithInitializerListClass(std::initializer_list<const char*> il, int theInt)
: m_char(*il.begin())
, m_int(theInt)
{
}
const char* m_char;
int m_int;
};
} // end namespace OptionalTestClasses
namespace UnitTest
{
using namespace OptionalTestClasses;
class OptionalFixture
: public AllocatorsFixture
{
};
static_assert(!(optional<int>()), "optional constructed with no args should be false");
static_assert(!(optional<int>(nullopt)), "optional constructed with nullopt should be false");
TEST_F(OptionalFixture, ConstructorNonTrivial)
{
optional<NonTriviallyConstructibleClass> opt;
EXPECT_FALSE(bool(opt)) << "optional constructed with no args should be false";
}
TEST_F(OptionalFixture, ConstructorInPlace)
{
const optional<int> opt(in_place, 5);
EXPECT_TRUE(bool(opt)) << "optional constructed with args should be true";
EXPECT_EQ(*opt, 5);
}
TEST_F(OptionalFixture, ConstructorInPlaceNonTriviallyDestructable)
{
const optional<NonTriviallyDestructableClass> opt(in_place);
EXPECT_TRUE(bool(opt)) << "optional constructed with args should be true";
}
TEST_F(OptionalFixture, ConstructorInPlaceWithInitializerList)
{
const optional<ConstructibleWithInitializerListClass> opt(in_place, {"Lumberyard"}, 4);
EXPECT_TRUE(bool(opt)) << "optional constructed with args should be true";
}
TEST_F(OptionalFixture, ConstructorCopyTrivial)
{
const optional<int> opt(in_place, 5);
const optional<int> optCopy(opt);
EXPECT_EQ(bool(opt), bool(optCopy)) << "Copying an optional should result in the same bool() value";
}
TEST_F(OptionalFixture, ConstructorMoveTrivial)
{
const optional<int> opt(in_place, 5);
const optional<int> optMoved(AZStd::move(opt));
EXPECT_EQ(bool(opt), bool(optMoved)) << "Moving an optional should result in the same bool() value";
}
TEST_F(OptionalFixture, ConstructorMoveNonTrivial)
{
AZStd::string s1 {"Hello"};
AZStd::string s2(AZStd::move(s1));
EXPECT_NE(s1, s2);
optional<AZStd::string> opt {"Hello"};
const optional<AZStd::string> optMoved(AZStd::move(opt));
EXPECT_EQ(bool(opt), bool(optMoved)) << "Moving an optional should result in the same bool() value";
EXPECT_NE(opt.value(), optMoved.value());
}
TEST_F(OptionalFixture, CanAssignFromEmptyOptional)
{
optional<int> opt1;
opt1 = {};
EXPECT_FALSE(bool(opt1)) << "Optional should still be empty";
}
TEST_F(OptionalFixture, AZStdHashActuallyCompiles)
{
constexpr optional<int> opt1{ 5 };
constexpr size_t hashValue = AZStd::hash<optional<int>>{}(opt1);
static_assert(hashValue != 0, "Hash of engaged optional of int within non-zero value should not be 0");
EXPECT_NE(0, hashValue);
}
} // end namespace UnitTest
File diff suppressed because it is too large Load Diff
+171
View File
@@ -0,0 +1,171 @@
/*
* 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 <AzCore/std/containers/compressed_pair.h>
#include "UserTypes.h"
namespace UnitTest
{
//////////////////////////////////////////////////////////////////////////
// Fixtures
// Fixture for non-typed tests
template<typename TestConfig>
class CompressedPairTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
template<typename TestConfig>
class CompressedPairSizeTest
: public CompressedPairTest<TestConfig>
{};
namespace CompressedPairInternal
{
struct EmptyStruct
{};
struct EmptyStructNonDerived
{};
struct FinalEmptyStruct final
{};
struct DerivedFromEmptyStruct
: EmptyStruct
{
};
struct DerivedWithDataFromEmptyStruct
: EmptyStruct
{
uint32_t m_value{};
};
}
template<typename T1, typename T2, size_t MaxExpectedSize>
struct CompressedPairTestConfig
{
using first_type = T1;
using second_type = T2;
static constexpr size_t max_expected_size = MaxExpectedSize;
};
constexpr size_t pairSize = sizeof(AZStd::compressed_pair<CompressedPairInternal::EmptyStruct, int32_t>);
using CompressedPairTestConfigs = ::testing::Types<
CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, CompressedPairInternal::FinalEmptyStruct, 1>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, int32_t, 4>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, CompressedPairInternal::EmptyStructNonDerived, 1>
, CompressedPairTestConfig<int32_t, CompressedPairInternal::EmptyStruct, 4>
, CompressedPairTestConfig<int32_t, int32_t, 8>
>;
TYPED_TEST_CASE(CompressedPairTest, CompressedPairTestConfigs);
using CompressedPairSizeTestConfigs = ::testing::Types<
CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, CompressedPairInternal::FinalEmptyStruct, 1>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, CompressedPairInternal::EmptyStructNonDerived, 1>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStruct, int32_t, 4>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStructNonDerived, CompressedPairInternal::FinalEmptyStruct, 1>
, CompressedPairTestConfig<CompressedPairInternal::EmptyStructNonDerived, CompressedPairInternal::DerivedFromEmptyStruct, 1>
, CompressedPairTestConfig<CompressedPairInternal::FinalEmptyStruct, int32_t, 8>
, CompressedPairTestConfig<CompressedPairInternal::FinalEmptyStruct, CompressedPairInternal::FinalEmptyStruct, 2>
, CompressedPairTestConfig<CompressedPairInternal::FinalEmptyStruct, CompressedPairInternal::DerivedWithDataFromEmptyStruct, 8>
, CompressedPairTestConfig<CompressedPairInternal::DerivedWithDataFromEmptyStruct, CompressedPairInternal::EmptyStructNonDerived, 4>
, CompressedPairTestConfig<CompressedPairInternal::DerivedWithDataFromEmptyStruct, CompressedPairInternal::DerivedWithDataFromEmptyStruct, 8>
, CompressedPairTestConfig<int32_t, int32_t, 8>
>;
TYPED_TEST_CASE(CompressedPairSizeTest, CompressedPairSizeTestConfigs);
TYPED_TEST(CompressedPairTest, CompressedPairDefaultConstructorSucceeds)
{
AZStd::compressed_pair<typename TypeParam::first_type, typename TypeParam::second_type> testPair;
(void)testPair;
}
TYPED_TEST(CompressedPairTest, CompressedPairFirstElementConstructorSucceeds)
{
AZStd::compressed_pair<typename TypeParam::first_type, typename TypeParam::second_type> testPair(typename TypeParam::first_type{});
(void)testPair;
}
TYPED_TEST(CompressedPairTest, CompressedPairSecondElementConstructorSucceeds)
{
AZStd::compressed_pair<typename TypeParam::first_type, typename TypeParam::second_type> testPair(AZStd::skip_element_tag{}, typename TypeParam::second_type{});
(void)testPair;
}
TYPED_TEST(CompressedPairTest, CompressedPairPiecewiseElementConstructorSucceeds)
{
AZStd::compressed_pair<typename TypeParam::first_type, typename TypeParam::second_type> testPair(AZStd::piecewise_construct_t{}, std::tuple<>{}, std::tuple<>{});
(void)testPair;
}
TYPED_TEST(CompressedPairSizeTest, CompressedPairUsesEmptyBaseOptimizationForClassSize)
{
static_assert(sizeof(TypeParam) <= TypeParam::max_expected_size, "Compressed Pair is not expected size");
}
class PairTestFixture
: public ScopedAllocatorSetupFixture
{};
TEST_F(PairTestFixture, StructuredBinding_ToConstAutoVar_CompilesSuccessfully)
{
constexpr AZStd::pair<int, int> testPair{ 3, 4 };
const auto [firstElement, secondElement] = testPair;
static_assert(AZStd::is_same_v<const int, decltype(firstElement)>);
static_assert(AZStd::is_same_v<const int, decltype(secondElement)>);
EXPECT_EQ(3, firstElement);
EXPECT_EQ(4, secondElement);
}
TEST_F(PairTestFixture, CanGetFromAPairRValue)
{
{
int myValue = 42;
AZStd::pair<int&, int> myPair(myValue, 0);
int& valueRef = AZStd::get<0>(AZStd::move(myPair));
EXPECT_EQ(&myValue, &valueRef);
static_assert(AZStd::is_same_v<decltype(AZStd::get<0>(AZStd::move(myPair))), int&>,
"Invoking AZStd::get on an lvalue reference element in a pair should return an lvalue reference");
}
{
struct MoveOnlyType
{
MoveOnlyType() = default;
MoveOnlyType(const MoveOnlyType&) = delete;
MoveOnlyType(MoveOnlyType&&) = default;
};
MoveOnlyType myValue;
const AZStd::pair<MoveOnlyType&&, int> myPair(AZStd::move(myValue), 42);
MoveOnlyType&& valueRef = AZStd::get<0>(AZStd::move(myPair));
EXPECT_EQ(&myValue, &valueRef);
static_assert(AZStd::is_same_v<decltype(AZStd::get<0>(AZStd::move(myPair))), MoveOnlyType&&>,
"Invoking AZStd::get on an rvalue reference element in a pair should return an rvalue reference");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,352 @@
/*
* 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 <AzCore/std/parallel/scoped_lock.h>
#include <AzCore/std/parallel/shared_mutex.h>
#include "UserTypes.h"
namespace UnitTest
{
// Fixture for AZStd::scoped_lock unit tests
class ScopedLockTest
: public AllocatorsFixture
{
protected:
void SetUp() override
{
AllocatorsFixture::SetUp();
}
void TearDown() override
{
AllocatorsFixture::TearDown();
}
};
struct ScopedTestMutex
{
ScopedTestMutex() = default;
void lock()
{
m_locked = true;
}
void unlock()
{
m_locked = false;
}
bool try_lock()
{
if (m_locked)
{
return false;
}
m_locked = true;
return m_locked;
}
bool m_locked{};
};
struct AtomicScopedTestMutex
{
AtomicScopedTestMutex() = default;
void lock()
{
AZStd::exponential_backoff waitForCounter;
constexpr bool lockedCounterValue{ true };
while (true)
{
bool unlockedCounterValue{ false };
if (m_locked.compare_exchange_weak(unlockedCounterValue, lockedCounterValue, AZStd::memory_order_release))
{
break;
}
waitForCounter.wait();
}
}
void unlock()
{
m_locked.store(false, AZStd::memory_order_release);
}
bool try_lock()
{
bool unlockedCounterValue{ false };
constexpr bool lockedCounterValue{ true };
return m_locked.compare_exchange_strong(unlockedCounterValue, lockedCounterValue, AZStd::memory_order_release);;
}
AZStd::atomic<bool> m_locked{};
};
TEST_F(ScopedLockTest, scoped_lock_NoArgument_Construct_ConstructorSucceeds)
{
// Validates empty mutex scoped_lock is constructible and destructible
AZStd::scoped_lock<> emptyLock;
(void)emptyLock;
}
TEST_F(ScopedLockTest, scoped_lock_OneArgument_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
{
AZStd::scoped_lock<decltype(testMutex1)> oneArgLock(testMutex1);
EXPECT_TRUE(testMutex1.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
}
TEST_F(ScopedLockTest, scoped_lock_TwoArgument_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
ScopedTestMutex testMutex2;
{
AZStd::scoped_lock<decltype(testMutex1), decltype(testMutex2)> twoArgLock(testMutex1, testMutex2);
EXPECT_TRUE(testMutex1.m_locked);
EXPECT_TRUE(testMutex2.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
EXPECT_FALSE(testMutex2.m_locked);
}
TEST_F(ScopedLockTest, scoped_lock_VariadicArgument_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
ScopedTestMutex testMutex2;
ScopedTestMutex testMutex3;
{
AZStd::scoped_lock<decltype(testMutex1), decltype(testMutex2), decltype(testMutex3)> threeArgLock(testMutex1, testMutex2, testMutex3);
EXPECT_TRUE(testMutex1.m_locked);
EXPECT_TRUE(testMutex2.m_locked);
EXPECT_TRUE(testMutex3.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
EXPECT_FALSE(testMutex2.m_locked);
EXPECT_FALSE(testMutex3.m_locked);
}
inline namespace ScopedLockInternal
{
struct NotAType {};
template<typename T, typename = void>
struct has_mutex_type
{
static constexpr bool value{ false };
};
template<typename T>
struct has_mutex_type<T, AZStd::void_t<typename T::mutex_type>>
{
static constexpr bool value{ true };
};
template<typename T>
constexpr bool has_mutex_type_v = has_mutex_type<T>::value;
}
TEST_F(ScopedLockTest, scoped_lock_mutex_type_trait_ExistOnlyForOneArgumentTemplate)
{
static_assert(!ScopedLockInternal::has_mutex_type_v<AZStd::scoped_lock<>>, "ScopedLock with empty parameter should not have the mutex_type alias");
{
using MutexType = ScopedTestMutex;
using ScopedLockType = AZStd::scoped_lock<MutexType>;
static_assert(ScopedLockInternal::has_mutex_type_v<ScopedLockType>, "ScopedLock with one parameter type should have a mutex_type alias");
static_assert(AZStd::is_same<typename ScopedLockType::mutex_type, MutexType>::value, "ScopedLock mutex_type alias should match MutexType alias");
}
{
using MutexType = AZStd::recursive_mutex;
using ScopedLockType = AZStd::scoped_lock<MutexType>;
static_assert(ScopedLockInternal::has_mutex_type_v<ScopedLockType>, "ScopedLock with one parameter type should have a mutex_type alias");
static_assert(AZStd::is_same<typename ScopedLockType::mutex_type, MutexType>::value, "ScopedLock mutex_type alias should match MutexType alias");
}
static_assert(!ScopedLockInternal::has_mutex_type_v<AZStd::scoped_lock<ScopedTestMutex, ScopedTestMutex>>, "ScopedLock with two template parameter types should not have a mutex_type alias");
static_assert(!ScopedLockInternal::has_mutex_type_v<AZStd::scoped_lock<AZStd::recursive_mutex, ScopedTestMutex>>, "ScopedLock with two template parameter types should not have a mutex_type alias");
static_assert(!ScopedLockInternal::has_mutex_type_v<AZStd::scoped_lock<ScopedTestMutex, AZStd::recursive_mutex, ScopedTestMutex>>, "ScopedLock with two template parameter types should not have a mutex_type alias");
}
TEST_F(ScopedLockTest, scoped_lock_NoArgument_adopt_lock_Construct_ConstructorSucceeds)
{
// Validates empty mutex scoped_lock is construticble and destructible
AZStd::scoped_lock<> emptyLock(AZStd::adopt_lock);
}
TEST_F(ScopedLockTest, scoped_lock_OneArgument_adopt_lock_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
{
AZStd::scoped_lock<decltype(testMutex1)> oneArgLock(AZStd::adopt_lock, testMutex1);
EXPECT_FALSE(testMutex1.m_locked);
}
testMutex1.lock();
{
AZStd::scoped_lock<decltype(testMutex1)> oneArgLock(AZStd::adopt_lock, testMutex1);
EXPECT_TRUE(testMutex1.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
}
TEST_F(ScopedLockTest, scoped_lock_TwoArgument_adopt_lock_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
ScopedTestMutex testMutex2;
testMutex2.lock();
{
AZStd::scoped_lock<decltype(testMutex1), decltype(testMutex2)> twoArgLock(AZStd::adopt_lock, testMutex1, testMutex2);
EXPECT_FALSE(testMutex1.m_locked);
EXPECT_TRUE(testMutex2.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
EXPECT_FALSE(testMutex2.m_locked);
}
TEST_F(ScopedLockTest, scoped_lock_VariadicArgument_adopt_lock_Construct_ConstructorSucceeds)
{
ScopedTestMutex testMutex1;
ScopedTestMutex testMutex2;
ScopedTestMutex testMutex3;
testMutex1.lock();
testMutex3.lock();
{
AZStd::scoped_lock<decltype(testMutex1), decltype(testMutex2), decltype(testMutex3)> threeArgLock(AZStd::adopt_lock, testMutex1, testMutex2, testMutex3);
EXPECT_TRUE(testMutex1.m_locked);
EXPECT_FALSE(testMutex2.m_locked);
EXPECT_TRUE(testMutex3.m_locked);
}
EXPECT_FALSE(testMutex1.m_locked);
EXPECT_FALSE(testMutex2.m_locked);
EXPECT_FALSE(testMutex3.m_locked);
}
TEST_F(ScopedLockTest, Deadlock_AvoidanceTest_Recursive_And_Shared_Mutex)
{
constexpr size_t threadCount = 8;
enum : size_t { cycleCount = 1000 };
constexpr uint64_t expectedCounterResult = threadCount * cycleCount;
AZStd::thread threads[threadCount];
AZStd::recursive_mutex recursiveMutex1;
AZStd::shared_mutex sharedMutex1;
uint64_t testCounter{};
auto work = [&testCounter, &recursiveMutex1, &sharedMutex1]()
{
for (size_t cycleIndex = 0; cycleIndex != cycleCount; ++cycleIndex)
{
AZStd::scoped_lock<AZStd::recursive_mutex, AZStd::shared_mutex> recursiveAndSharedMutexLock(recursiveMutex1, sharedMutex1);
++testCounter;
}
};
for (AZStd::thread& thread : threads)
{
thread = AZStd::thread(work);
}
for (AZStd::thread& thread : threads)
{
thread.join();
}
EXPECT_EQ(expectedCounterResult, testCounter);
}
TEST_F(ScopedLockTest, Deadlock_AvoidanceTest_Swap_Lock_Order_For_Half_Threads)
{
constexpr size_t threadCount = 8;
enum : size_t { cycleCount = 1000 };
constexpr uint64_t expectedCounterResult = threadCount * cycleCount;
AZStd::thread threads[threadCount];
AZStd::recursive_mutex recursiveMutex1;
AZStd::shared_mutex sharedMutex1;
uint64_t testCounter{};
auto evenThreadWorkerFunc = [&testCounter, &recursiveMutex1, &sharedMutex1]()
{
for (size_t cycleIndex = 0; cycleIndex != cycleCount; ++cycleIndex)
{
AZStd::scoped_lock<AZStd::recursive_mutex, AZStd::shared_mutex> recursiveAndSharedMutexLock(recursiveMutex1, sharedMutex1);
++testCounter;
}
};
// Just swaps the parameter order so that the shared_mutex comes before the recursive mutex
auto oddThreadWorkerFunc = [&testCounter, &recursiveMutex1, &sharedMutex1]()
{
for (size_t cycleIndex = 0; cycleIndex != cycleCount; ++cycleIndex)
{
AZStd::scoped_lock<AZStd::shared_mutex, AZStd::recursive_mutex> recursiveAndSharedMutexLock(sharedMutex1, recursiveMutex1);
++testCounter;
}
};
size_t threadIndex = 0;
for (AZStd::thread& threadRef : threads)
{
if (threadIndex % 2 == 0)
{
threadRef = AZStd::thread(evenThreadWorkerFunc);
}
else
{
threadRef = AZStd::thread(oddThreadWorkerFunc);
}
++threadIndex;
}
for (AZStd::thread& threadRef : threads)
{
threadRef.join();
}
EXPECT_EQ(expectedCounterResult, testCounter);
}
TEST_F(ScopedLockTest, Deadlock_AvoidanceTest_Atomic_Test_Mutex)
{
constexpr size_t threadCount = 8;
enum : size_t { cycleCount = 1000 };
constexpr uint64_t expectedCounterResult = threadCount * cycleCount;
AZStd::thread threads[threadCount];
AtomicScopedTestMutex testMutex1;
AtomicScopedTestMutex testMutex2;
uint64_t testCounter{};
auto work = [&testCounter, &testMutex1, &testMutex2]()
{
for (size_t cycleIndex = 0; cycleIndex != cycleCount; ++cycleIndex)
{
AZStd::scoped_lock<AtomicScopedTestMutex, AtomicScopedTestMutex> recursiveAndSharedMutexLock(testMutex1, testMutex2);
++testCounter;
}
};
for (AZStd::thread& thread : threads)
{
thread = AZStd::thread(work);
}
for (AZStd::thread& thread : threads)
{
thread.join();
}
EXPECT_EQ(expectedCounterResult, testCounter);
}
}
@@ -0,0 +1,297 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/intrusive_set.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/containers/array.h>
using namespace AZStd;
using namespace UnitTestInternal;
#define AZ_TEST_VALIDATE_EMPTY_SET(_set) \
EXPECT_EQ(0, _set.size()); \
EXPECT_TRUE(_set.begin() == _set.end()); \
EXPECT_TRUE(_set.rbegin() == _set.rend()); \
EXPECT_TRUE(_set.empty());
#define AZ_TEST_VALIDATE_SET(_set, _NumElements) \
EXPECT_EQ(_NumElements, _set.size()); \
EXPECT_TRUE((_NumElements > 0) ? !_set.empty() : _set.empty()); \
EXPECT_TRUE((_NumElements > 0) ? _set.begin() != _set.end() : _set.begin() == _set.end()); \
EXPECT_FALSE(_set.empty());
namespace UnitTest
{
// My intrusive set class.
// We have 2 hooks in this class. One of each supported type. Base hook which we inherit from the intrusive_set_node node
// and public member hook (m_setHook).
struct MySetClass
: public intrusive_multiset_node<MySetClass>
{
public:
MySetClass(int data = 101)
: m_data(data) {}
intrusive_multiset_node<MySetClass> m_setHook; // Public member hook.
int m_data; // This is left public only for testing purpose.
};
// Compare operators, used for sort, merge, etc.
AZ_FORCE_INLINE bool operator==(const MySetClass& a, const MySetClass& b) { return a.m_data == b.m_data; }
AZ_FORCE_INLINE bool operator!=(const MySetClass& a, const MySetClass& b) { return a.m_data != b.m_data; }
AZ_FORCE_INLINE bool operator<(const MySetClass& a, const MySetClass& b) { return a.m_data < b.m_data; }
AZ_FORCE_INLINE bool operator>(const MySetClass& a, const MySetClass& b) { return a.m_data > b.m_data; }
class IntrusiveSetContainers
: public AllocatorsFixture
{
public:
template <class T>
struct RemoveLessThan401
{
AZ_FORCE_INLINE bool operator()(const T& element) const { return element.m_data < 401; }
};
template <class T>
struct UniqueForLessThan401
{
AZ_FORCE_INLINE bool operator()(const T& el1, const T& el2) const { return (el1.m_data == el2.m_data && el1.m_data < 401); }
};
template <class T, size_t S>
void FillArray(array<T, S>& myclassArray)
{
for (int i = 0; i < (int)myclassArray.size(); ++i)
{
myclassArray[i].m_data = 101 + i * 100;
}
}
};
TEST_F(IntrusiveSetContainers, CtorAssign)
{
// Create 2 set types, one which hooks to the base hook and one to the public member hook.
typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
// A static array of MyClass, init with default ctor. Used as source for some of the tests.
array<MySetClass, 20> myclassArray;
myclass_base_set_type myclass_base_set;
myclass_member_set_type myclass_member_set;
// default ctor
AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
myclass_base_set_type myclass_base_set21(myclassArray.begin(), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_base_set21, myclassArray.size());
for (myclass_base_set_type::const_iterator iter = myclass_base_set21.begin(); iter != myclass_base_set21.end(); ++iter)
{
AZ_TEST_ASSERT((*iter).m_data == 101);
}
myclass_member_set_type myclass_member_set21(myclassArray.begin(), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_member_set21, myclassArray.size());
for (myclass_member_set_type::const_iterator iter = myclass_member_set21.begin(); iter != myclass_member_set21.end(); ++iter)
{
AZ_TEST_ASSERT((*iter).m_data == 101);
}
// assign
myclass_base_set21.clear();
myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
myclass_member_set21.clear();
myclass_member_set.insert(myclassArray.begin(), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
}
TEST_F(IntrusiveSetContainers, SetResizeInsertEraseClear)
{
// Create 2 set types, one which hooks to the base hook and one to the public member hook.
typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
// A static array of MyClass, init with default ctor. Used as source for some of the tests.
array<MySetClass, 20> myclassArray;
myclass_base_set_type myclass_base_set;
myclass_member_set_type myclass_member_set;
// insert
myclass_base_set.clear();
myclass_member_set.clear();
myclass_base_set.insert(*myclassArray.begin());
AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
AZ_TEST_ASSERT(myclass_base_set.begin()->m_data == myclassArray.begin()->m_data);
myclass_member_set.insert(*myclassArray.begin());
AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
AZ_TEST_ASSERT(myclass_member_set.begin()->m_data == myclassArray.begin()->m_data);
myclass_base_set.insert(next(myclassArray.begin()), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
AZ_TEST_ASSERT(myclass_base_set.begin()->m_data == prev(myclassArray.end())->m_data);
myclass_member_set.insert(next(myclassArray.begin()), myclassArray.end());
AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
AZ_TEST_ASSERT(myclass_member_set.rbegin()->m_data == prev(myclassArray.end())->m_data);
// erase
myclass_base_set.erase(prev(myclass_base_set.end()));
AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 1);
myclass_base_set.erase(myclass_base_set.begin());
AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 2);
myclass_member_set.erase(prev(myclass_member_set.end()));
AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 1);
myclass_member_set.erase(myclass_member_set.begin());
AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 2);
myclass_base_set.erase(next(myclass_base_set.begin()), myclass_base_set.end());
AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
myclass_member_set.erase(next(myclass_member_set.begin()), myclass_member_set.end());
AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
// clear
myclass_base_set.clear();
AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
myclass_member_set.clear();
AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
}
// TODO: LY-87175 Add move and swap operations to intrusive_set
//TEST_F(IntrusiveSetContainers, Swap)
//{
// typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
// typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
// // A static array of MyClass, init with default ctor. Used as source for some of the tests.
// array<MySetClass, 20> myclassArray;
// myclass_base_set_type myclass_base_set;
// myclass_base_set_type myclass_base_set2;
// myclass_member_set_type myclass_member_set;
// myclass_member_set_type myclass_member_set2;
// // swap
// myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
// myclass_member_set.insert(myclassArray.begin(), myclassArray.end());
// myclass_base_set2.swap(myclass_base_set);
// AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
// AZ_TEST_VALIDATE_SET(myclass_base_set2, myclassArray.size());
// myclass_member_set2.swap(myclass_member_set);
// AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
// AZ_TEST_VALIDATE_SET(myclass_member_set2, myclassArray.size());
// myclass_base_set2.swap(myclass_base_set);
// AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set2);
// AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
// myclass_member_set2.swap(myclass_member_set);
// AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set2);
// AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
// myclass_base_set.erase(*myclass_base_set.rbegin());
// AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 1);
// myclass_member_set.erase(*myclass_base_set.rbegin());
// AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 1);
// myclass_base_set2.insert(myclassArray.back());
// myclass_member_set2.insert(myclassArray.back());
// myclass_base_set.swap(myclass_base_set2);
// AZ_TEST_VALIDATE_SET(myclass_base_set2, myclassArray.size() - 1);
// AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
// myclass_member_set.swap(myclass_member_set2);
// AZ_TEST_VALIDATE_SET(myclass_member_set2, myclassArray.size() - 1);
// AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
// myclass_base_set.clear();
// myclass_base_set2.clear();
// myclass_member_set.clear();
// myclass_member_set2.clear();
//}
TEST_F(IntrusiveSetContainers, RemoveWhileIterating)
{
typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
// A static array of MyClass, init with default ctor. Used as source for some of the tests.
array<MySetClass, 2> myclassArray;
myclass_base_set_type myclass_base_set;
FillArray(myclassArray);
// remove
myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
myclass_base_set_type::const_iterator it = myclass_base_set.begin();
while (it != myclass_base_set.end())
{
myclass_base_set.erase(it++);
}
AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
// repopulate and remove in reverse order
myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
myclass_base_set_type::const_reverse_iterator rit = myclass_base_set.crbegin();
while (rit != myclass_base_set.crend())
{
const MySetClass* item = rit.operator->();
++rit;
myclass_base_set.erase(item);
}
AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
}
TEST_F(IntrusiveSetContainers, ReverseIterator)
{
using myclass_base_set_type = intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> >;
// A static array of MyClass, init with default ctor. Used as source for some of the tests.
constexpr size_t arraySize = 20;
array<MySetClass, arraySize> myclassArray;
myclass_base_set_type myclass_base_set;
FillArray(myclassArray);
myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
// iterate in reverse order
auto myClassReverseBeginIt = myclass_base_set.crbegin();
auto myClassReverseEndIt = myclass_base_set.crend();
EXPECT_NE(myClassReverseEndIt, myClassReverseBeginIt);
// Reverse iterator must be decremented that in order the base() iterator call
// to return an iterator to a valid element
++myClassReverseBeginIt;
size_t reverseArrayIndex = arraySize - 1;
for (; myClassReverseBeginIt != myClassReverseEndIt; ++myClassReverseBeginIt, --reverseArrayIndex)
{
EXPECT_EQ(reverseArrayIndex * 100 + 101, myClassReverseBeginIt.base()->m_data);
}
// The crend() element base() call should refer to the first element of the array which
// should have a value of 101 + arrayIndex * 100, where arrayIndex is 0.
EXPECT_EQ(101, myClassReverseBeginIt.base()->m_data);
{
// Test empty intrusive container case
myclass_base_set.clear();
auto reverseIterBegin = myclass_base_set.rbegin();
auto reverseIterEnd = myclass_base_set.rend();
EXPECT_EQ(reverseIterEnd, reverseIterEnd);
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,490 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/typetraits/internal/is_template_copy_constructible.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/containers/unordered_map.h>
using namespace AZStd;
using namespace UnitTestInternal;
namespace UnitTest
{
/**
* Tests the AZSTD type traits as much as possible for each target.
* \cond
* The following templates require compiler support for a fully conforming implementation:
* template <class T> struct is_class;
* template <class T> struct is_union;
* template <class T> struct is_enum;
* template <class T> struct is_polymorphic;
* template <class T> struct is_empty;
* template <class T> struct has_trivial_constructor;
* template <class T> struct has_trivial_copy;
* template <class T> struct has_trivial_assign;
* template <class T> struct has_trivial_destructor;
* template <class T> struct has_nothrow_constructor;
* template <class T> struct has_nothrow_copy;
* template <class T> struct has_nothrow_assign;
* template <class T> struct is_pod;
* template <class T> struct is_abstract;
* \endcond
*/
TEST(TypeTraits, All)
{
//////////////////////////////////////////////////////////////////////////
// Primary type categories:
// alignment_of and align_to
AZ_TEST_STATIC_ASSERT(alignment_of<int>::value == 4);
AZ_TEST_STATIC_ASSERT(alignment_of<char>::value == 1);
AZ_TEST_STATIC_ASSERT(alignment_of<MyClass>::value == 16);
aligned_storage<sizeof(int)*100, 16>::type alignedArray;
AZ_TEST_ASSERT((((AZStd::size_t)&alignedArray) & 15) == 0);
AZ_TEST_STATIC_ASSERT((alignment_of< aligned_storage<sizeof(int)*5, 8>::type >::value) == 8);
AZ_TEST_STATIC_ASSERT(sizeof(aligned_storage<sizeof(int), 16>::type) == 16);
// is_void
AZ_TEST_STATIC_ASSERT(is_void<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_void<void>::value == true);
AZ_TEST_STATIC_ASSERT(is_void<void const>::value == true);
AZ_TEST_STATIC_ASSERT(is_void<void volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_void<void const volatile>::value == true);
// is_integral
AZ_TEST_STATIC_ASSERT(is_integral<unsigned char>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<unsigned short>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<unsigned int>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<unsigned long>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<signed char>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<signed short>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<signed int>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<signed long>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<bool>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<char>::value == true);
//AZ_TEST_STATIC_ASSERT(is_integral<wchar_t>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<char const >::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<short const>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<int const>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<long const>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<char volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<short volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<int volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<long volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<char const volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<short const volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<int const volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<long const volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_integral<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_integral<MyStruct const>::value == false);
AZ_TEST_STATIC_ASSERT(is_integral<MyStruct const volatile>::value == false);
// is_floating_point
AZ_TEST_STATIC_ASSERT(is_floating_point<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_floating_point<float>::value == true);
AZ_TEST_STATIC_ASSERT(is_floating_point<float const>::value == true);
AZ_TEST_STATIC_ASSERT(is_floating_point<float volatile>::value == true);
AZ_TEST_STATIC_ASSERT(is_floating_point<float const volatile>::value == true);
// is_array
AZ_TEST_STATIC_ASSERT(is_array<int*>::value == false);
AZ_TEST_STATIC_ASSERT(is_array<int[5]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<const int[5]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<volatile int[5]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<const volatile int[5]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<float[]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<const float[]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<volatile float[]>::value == true);
AZ_TEST_STATIC_ASSERT(is_array<const volatile float[]>::value == true);
// is_pointer
AZ_TEST_STATIC_ASSERT(is_pointer<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_pointer<int*>::value == true);
AZ_TEST_STATIC_ASSERT(is_pointer<const MyStruct*>::value == true);
AZ_TEST_STATIC_ASSERT(is_pointer<volatile int*>::value == true);
AZ_TEST_STATIC_ASSERT(is_pointer<const volatile MyStruct*>::value == true);
// is_reference
AZ_TEST_STATIC_ASSERT(is_reference<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_reference<int&>::value == true);
AZ_TEST_STATIC_ASSERT(is_reference<const MyStruct&>::value == true);
AZ_TEST_STATIC_ASSERT(is_reference<volatile int&>::value == true);
AZ_TEST_STATIC_ASSERT(is_reference<const volatile MyStruct&>::value == true);
// is_member_object_pointer
AZ_TEST_STATIC_ASSERT(is_member_object_pointer<MyStruct*>::value == false);
AZ_TEST_STATIC_ASSERT(is_member_object_pointer<int (MyStruct::*)()>::value == false);
AZ_TEST_STATIC_ASSERT(is_member_object_pointer<int MyStruct::*>::value == true);
// is_member_function_pointer
AZ_TEST_STATIC_ASSERT(is_member_function_pointer<MyStruct*>::value == false);
AZ_TEST_STATIC_ASSERT(is_member_function_pointer<int (MyStruct::*)()>::value == true);
AZ_TEST_STATIC_ASSERT(is_member_function_pointer<int MyStruct::*>::value == false);
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() volatile>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const volatile>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() &>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const&>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const volatile&>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() &&>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const&&>::value));
AZ_TEST_STATIC_ASSERT((is_member_function_pointer<int (MyStruct::*)() const volatile&&>::value));
// is_enum
AZ_TEST_STATIC_ASSERT(is_enum<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_enum<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_enum<MyEnum>::value == true);
// is_union
AZ_TEST_STATIC_ASSERT(is_union<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_union<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_union<MyUnion>::value == true);
// is_class
AZ_TEST_STATIC_ASSERT(is_class<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_class<MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_class<MyClass>::value == true);
// is_function
AZ_TEST_STATIC_ASSERT(is_function<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_function<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_function<int(float, char)>::value == true);
//////////////////////////////////////////////////////////////////////////
// composite type categories:
// is_arithmetic
AZ_TEST_STATIC_ASSERT(is_arithmetic<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_arithmetic<int>::value == true);
AZ_TEST_STATIC_ASSERT(is_arithmetic<float>::value == true);
// is_fundamental
AZ_TEST_STATIC_ASSERT(is_fundamental<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_fundamental<int>::value == true);
AZ_TEST_STATIC_ASSERT(is_fundamental<const float>::value == true);
AZ_TEST_STATIC_ASSERT(is_fundamental<void>::value == true);
// is_object
AZ_TEST_STATIC_ASSERT(is_object<MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_object<MyStruct&>::value == false);
AZ_TEST_STATIC_ASSERT(is_object<int(short, float)>::value == false);
AZ_TEST_STATIC_ASSERT(is_object<void>::value == false);
// is_scalar
AZ_TEST_STATIC_ASSERT(is_scalar<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_scalar<MyStruct*>::value == true);
AZ_TEST_STATIC_ASSERT(is_scalar<const float>::value == true);
AZ_TEST_STATIC_ASSERT(is_scalar<int>::value == true);
// is_compound
AZ_TEST_STATIC_ASSERT(is_compound<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_compound<MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_compound<int(short, float)>::value == true);
AZ_TEST_STATIC_ASSERT(is_compound<float[]>::value == true);
AZ_TEST_STATIC_ASSERT(is_compound<int&>::value == true);
AZ_TEST_STATIC_ASSERT(is_compound<const void*>::value == true);
// is_member_pointer
AZ_TEST_STATIC_ASSERT(is_member_pointer<MyStruct*>::value == false);
AZ_TEST_STATIC_ASSERT(is_member_pointer<int MyStruct::*>::value == true);
AZ_TEST_STATIC_ASSERT(is_member_pointer<int (MyStruct::*)()>::value == true);
//////////////////////////////////////////////////////////////////////////
// type properties:
// is_const
AZ_TEST_STATIC_ASSERT(is_const<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_const<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_const<const MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_const<const float>::value == true);
// is_volatile
AZ_TEST_STATIC_ASSERT(is_volatile<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_volatile<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_volatile<volatile MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_volatile<volatile float>::value == true);
// is_pod
AZ_TEST_STATIC_ASSERT(is_pod<MyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_pod<int>::value == true);
AZ_TEST_STATIC_ASSERT(is_pod<const MyClass>::value == false);
AZ_TEST_STATIC_ASSERT((is_pod< aligned_storage<30, 32>::type >::value) == true);
// is_empty
AZ_TEST_STATIC_ASSERT(is_empty<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_empty<MyEmptyStruct>::value == true);
AZ_TEST_STATIC_ASSERT(is_empty<int>::value == false);
// is_polymorphic
AZ_TEST_STATIC_ASSERT(is_polymorphic<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_polymorphic<MyClass>::value == true);
// is_abstract
AZ_TEST_STATIC_ASSERT(is_abstract<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_abstract<MyInterface>::value == true);
// has_trivial_constructor
static_assert(is_trivially_constructible_v<MyStruct>);
static_assert(is_trivially_constructible_v<int>);
static_assert(!is_trivially_constructible_v<MyClass>);
// has_trivial_copy
static_assert(is_trivially_copy_constructible_v<MyStruct>);
static_assert(is_trivially_copy_constructible_v<int>);
static_assert(!is_trivially_copy_constructible_v<MyClass>);
// has_trivial_assign
static_assert(is_trivially_copy_assignable_v<MyStruct>);
static_assert(is_trivially_copy_assignable_v<int>);
static_assert(!is_trivially_copy_assignable_v<MyClass>);
// has_trivial_destructor
static_assert(is_trivially_destructible_v<MyStruct>);
static_assert(is_trivially_destructible_v<int>);
static_assert(!is_trivially_destructible_v<MyClass>);
// has_nothrow_constructr
// has_nothrow_copy
// has_nothrow_assign
// is_signed
AZ_TEST_STATIC_ASSERT(is_signed<int>::value == true);
AZ_TEST_STATIC_ASSERT(is_signed<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_signed<unsigned int>::value == false);
static_assert(is_signed<float>::value);
// is_unsigned
AZ_TEST_STATIC_ASSERT(is_unsigned<int>::value == false);
AZ_TEST_STATIC_ASSERT(is_unsigned<MyStruct>::value == false);
AZ_TEST_STATIC_ASSERT(is_unsigned<unsigned int>::value == true);
AZ_TEST_STATIC_ASSERT(is_unsigned<float>::value == false);
// true and false types
AZ_TEST_STATIC_ASSERT(true_type::value == true);
AZ_TEST_STATIC_ASSERT(false_type::value == false);
//! function traits tests
struct NotMyStruct
{
int foo() { return 0; }
};
struct FunctionTestStruct
{
bool operator()(FunctionTestStruct&) const { return true; };
};
using PrimitiveFunctionPtr = int(*)(bool, float, double, AZ::u8, AZ::s8, AZ::u16, AZ::s16, AZ::u32, AZ::s32, AZ::u64, AZ::s64);
using NotMyStructMemberPtr = int(NotMyStruct::*)();
using ComplexFunctionPtr = float(*)(MyEmptyStruct&, NotMyStructMemberPtr, MyUnion*);
using ComplexFunction = remove_pointer_t<ComplexFunctionPtr>;
using MemberFunctionPtr = void(MyInterface::*)(int);
using ConstMemberFunctionPtr = bool(FunctionTestStruct::*)(FunctionTestStruct&) const;
AZ_TEST_STATIC_ASSERT((AZStd::is_same<AZStd::function_traits<PrimitiveFunctionPtr>::result_type, int>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<AZStd::function_traits<PrimitiveFunctionPtr>::get_arg_t<10>, AZ::s64>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<AZStd::function_traits_get_arg_t<PrimitiveFunctionPtr, 5>, AZ::u16>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<PrimitiveFunctionPtr>::arity == 11));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<AZStd::function_traits_get_result_t<ComplexFunction>, float>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<AZStd::function_traits_get_arg_t<ComplexFunction, 1>, int(NotMyStruct::*)()>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<ComplexFunction>::arity == 3));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<MemberFunctionPtr>::class_fp_type, void(MyInterface::*)(int)>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<MemberFunctionPtr>::raw_fp_type, void(*)(int)>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<MemberFunctionPtr>::class_type, MyInterface>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<MemberFunctionPtr>::arity == 1));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<ConstMemberFunctionPtr>::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const> ::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<ConstMemberFunctionPtr>::raw_fp_type, bool(*)(FunctionTestStruct&)> ::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<ConstMemberFunctionPtr>::class_type, FunctionTestStruct>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits_get_arg_t<ConstMemberFunctionPtr, 0>, FunctionTestStruct&>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<ConstMemberFunctionPtr>::arity == 1));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<decltype(&FunctionTestStruct::operator())>::class_fp_type, bool(FunctionTestStruct::*)(FunctionTestStruct&) const>::value));
auto lambdaFunction = [](FunctionTestStruct, int) -> bool
{
return false;
};
using LambdaType = decltype(lambdaFunction);
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<LambdaType>::raw_fp_type, bool(*)(FunctionTestStruct, int)>::value));
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<LambdaType>::class_fp_type, bool(LambdaType::*)(FunctionTestStruct, int) const>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<LambdaType>::arity == 2));
static_assert(AZStd::is_same<AZStd::function_traits<LambdaType>::return_type, bool>::value, "Lambda result type should be bool");
AZStd::function<void(LambdaType*, ComplexFunction&)> stdFunction;
using StdFunctionType = decay_t<decltype(stdFunction)>;
AZ_TEST_STATIC_ASSERT((AZStd::is_same<typename AZStd::function_traits<StdFunctionType>::raw_fp_type, void(*)(LambdaType*, ComplexFunction&)>::value));
AZ_TEST_STATIC_ASSERT((AZStd::function_traits<StdFunctionType>::arity == 2));
}
struct ConstMethodTestStruct
{
void ConstMethod() const { }
void NonConstMethod() { }
};
AZ_TEST_STATIC_ASSERT((static_cast<uint32_t>(function_traits<decltype(&ConstMethodTestStruct::ConstMethod)>::qual_flags) & static_cast<uint32_t>(Internal::qualifier_flags::const_)) != 0);
AZ_TEST_STATIC_ASSERT((static_cast<uint32_t>(function_traits<decltype(&ConstMethodTestStruct::NonConstMethod)>::qual_flags) & static_cast<uint32_t>(Internal::qualifier_flags::const_)) == 0);
}
TEST(TypeTraits, StdRemoveConstCompiles)
{
static_assert(AZStd::is_same_v<int, AZStd::remove_const_t<const int>>, "C++11 std::remove_const_t has failed");
static_assert(AZStd::is_same_v<int, AZStd::remove_const_t<int>>, "C++11 std::remove_const_t has failed");
static_assert(AZStd::is_same_v<int*, AZStd::remove_const_t<int* const>>, "C++11 std::remove_const_t has failed");
static_assert(AZStd::is_same_v<const int*, AZStd::remove_const_t<const int*>>, "C++11 std::remove_const_t has failed");
static_assert(AZStd::is_same_v<const volatile int*, AZStd::remove_const_t<const volatile int* const>>, "C++11 std::remove_const_t has failed");
static_assert(AZStd::is_same_v<int, AZStd::remove_const_t<AZStd::remove_reference_t<const int&>>>, "C++11 std::remove_const_t has failed");
}
TEST(TypeTraits, StdRemoveVolatileCompiles)
{
static_assert(AZStd::is_same_v<int, AZStd::remove_volatile_t<volatile int>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<int, AZStd::remove_volatile_t<int>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<int*, AZStd::remove_volatile_t<int* volatile>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<volatile int*, AZStd::remove_volatile_t<volatile int*>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<const volatile int*, AZStd::remove_volatile_t<const volatile int*>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<const int*, AZStd::remove_volatile_t<const int* volatile>>, "C++11 std::remove_volatile_t has failed");
static_assert(AZStd::is_same_v<int, AZStd::remove_volatile_t<AZStd::remove_reference_t<volatile int&>>>, "C++11 std::remove_volatile_t has failed");
}
TEST(TypeTraits, StdIsConstCompiles)
{
static_assert(!AZStd::is_const_v<int>, "C++11 std::is_const has failed");
static_assert(AZStd::is_const_v<const int>, "C++11 std::is_const has failed");
// references are never const
static_assert(!AZStd::is_const_v<const int&>, "C++11 std::is_const has failed");
// pointer checks for constness
static_assert(!AZStd::is_const_v<const int*>, "C++11 std::is_const has failed");
static_assert(AZStd::is_const_v<const int* const>, "C++11 std::is_const has failed");
static_assert(AZStd::is_const_v<int* const>, "C++11 std::is_const has failed");
}
TEST(TypeTraits, StdIsVolatileCompiles)
{
static_assert(!AZStd::is_volatile_v<int>, "C++11 std::is_volatile has failed");
static_assert(AZStd::is_volatile_v<volatile int>, "C++11 std::is_volatile has failed");
// references are never volatile
static_assert(!AZStd::is_volatile_v<volatile int&>, "C++11 std::is_volatile has failed");
// pointer checks for volatile
static_assert(!AZStd::is_volatile_v<volatile int*>, "C++11 std::is_volatile has failed");
static_assert(AZStd::is_volatile_v<const int* volatile>, "C++11 std::is_volatile has failed");
static_assert(!AZStd::is_volatile_v<volatile int* const>, "C++11 std::is_volatile has failed");
static_assert(AZStd::is_volatile_v<int* volatile>, "C++11 std::is_volatile has failed");
}
TEST(TypeTraits, TemplateIsCopyConstructible_WithCopyConstructibleValueType_ReturnsTrue)
{
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::vector<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::list<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::forward_list<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::map<int, int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::multimap<int, int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::unordered_map<int, int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::unordered_multimap<int, int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::set<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::multiset<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::unordered_set<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::unordered_multiset<int>>::value, "");
static_assert(AZStd::Internal::template_is_copy_constructible<AZStd::pair<int, int>>::value, "");
struct CopyableType
{
CopyableType() = default;
CopyableType(const CopyableType&) = default;
};
static_assert(AZStd::Internal::template_is_copy_constructible<CopyableType>::value, "");
}
TEST(TypeTraits, TemplateIsCopyConstructible_WithOutCopyConstructibleValueType_ReturnsFalse)
{
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::vector<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::list<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::forward_list<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::map<AZStd::unique_ptr<int>, int>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::map<int, AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::multimap<AZStd::unique_ptr<int>, int>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::multimap<int, AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_map<AZStd::unique_ptr<int>, int>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_map<int, AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_multimap<AZStd::unique_ptr<int>, int>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_multimap<int, AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::set<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::multiset<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_set<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::unordered_multiset<AZStd::unique_ptr<int>>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::pair<AZStd::unique_ptr<int>, int>>::value, "");
static_assert(!AZStd::Internal::template_is_copy_constructible<AZStd::pair<int, AZStd::unique_ptr<int>>>::value, "");
struct MoveOnly
{
MoveOnly() = default;
MoveOnly(const MoveOnly&) = delete;
MoveOnly(MoveOnly&&) = default;
};
static_assert(!AZStd::Internal::template_is_copy_constructible<MoveOnly>::value, "");
}
TEST(TypeTraits, MakeSignedCompiles)
{
static_assert(AZStd::is_same_v<typename AZStd::make_signed<AZ::s8>::type, AZ::s8>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::u8>, AZ::s8>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::s16>, AZ::s16>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::u16>, AZ::s16>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::s32>, AZ::s32>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::u32>, AZ::s32>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::s64>, AZ::s64>);
static_assert(AZStd::is_same_v<AZStd::make_signed_t<AZ::s64>, AZ::s64>);
}
TEST(TypeTraits, MakeUnsignedCompiles)
{
static_assert(AZStd::is_same_v<typename AZStd::make_unsigned<AZ::s8>::type, AZ::u8>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::u8>, AZ::u8>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::s16>, AZ::u16>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::u16>, AZ::u16>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::s32>, AZ::u32>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::u32>, AZ::u32>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::s64>, AZ::u64>);
static_assert(AZStd::is_same_v<AZStd::make_unsigned_t<AZ::s64>, AZ::u64>);
}
// VS2017 workaround, calling decltype directly on the fully specialized aznumeric_cast template
// function fails with error C3556: 'aznumeric_cast': incorrect argument to 'decltype'
// So invoke the attempt to invoke function in a non-evaluated context and SFINAE to prevent a compile
// error
template <typename T, typename = void>
constexpr bool NumericCastInvocable = false;
template <typename T>
constexpr bool NumericCastInvocable<T, AZStd::void_t<decltype(aznumeric_cast<int>(AZStd::declval<T>()))>> = true;
TEST(TypeTraits, NumericCastConversionOperatorCompiles)
{
struct AzNumericCastConvertibleCompileTest
{
constexpr operator int() { return {}; };
};
static_assert(NumericCastInvocable<AzNumericCastConvertibleCompileTest>, "aznumeric_cast conversion operator overload is should be compilable");
}
@@ -0,0 +1,227 @@
/*
* 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.
*
*/
#ifndef AZSTD_UNITTEST_USERTYPES_H
#define AZSTD_UNITTEST_USERTYPES_H
// enable checked iterators (in debug) to test. we can't really do this with AZCore since the lib is compiled without AZSTD_CHECKED_ITERATORS by default
// define AZSTD_CHECKED_ITERATORS in the lib and then test
//#define AZSTD_CHECKED_ITERATORS 1
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/base.h>
#include <AzCore/std/typetraits/typetraits.h>
//for the temporary "lock-free" allocator, remove when we have a real one
#include <AzCore/std/allocator.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace UnitTestInternal
{
/**
* Examples test class with an aligned int data member.
*/
class MyClass
{
public:
MyClass(int data = 10)
: m_data(data)
, m_isMoved(false) {}
MyClass(int data, bool, float)
: m_data(data) {}
MyClass(const MyClass& rhs)
: m_data(rhs.m_data)
, m_isMoved(false) {}
MyClass(MyClass&& rhs)
{
m_isMoved = true;
m_data = rhs.m_data;
}
virtual ~MyClass() {}
virtual void make_polymorphic() {}
MyClass& operator=(const MyClass& rhs)
{
m_data = rhs.m_data;
m_isMoved = rhs.m_isMoved;
return *this;
}
// We use this class on the stack often, so alignment more than 16 bytes will not work on all platforms.
AZ_ALIGN(int m_data, 16);
bool m_isMoved;
};
AZ_FORCE_INLINE bool operator==(const MyClass& a, const MyClass& b) { return a.m_data == b.m_data; }
AZ_FORCE_INLINE bool operator!=(const MyClass& a, const MyClass& b) { return a.m_data != b.m_data; }
AZ_FORCE_INLINE bool operator<(const MyClass& a, const MyClass& b) { return a.m_data < b.m_data; }
AZ_FORCE_INLINE bool operator>(const MyClass& a, const MyClass& b) { return a.m_data > b.m_data; }
struct MyNoCopyClass
{
// Allowed construction mechanisms
MyNoCopyClass() = default;
MyNoCopyClass(int i, bool b, float f)
: m_int(i)
, m_bool(b)
, m_float(f) { }
MyNoCopyClass(MyNoCopyClass&& rhs)
: m_int(rhs.m_int)
, m_bool(rhs.m_bool)
, m_float(rhs.m_float)
{
rhs = { };
}
MyNoCopyClass& operator=(MyNoCopyClass&& rhs)
{
m_int = rhs.m_int;
m_bool = rhs.m_bool;
m_float = rhs.m_float;
rhs.m_int = 0;
rhs.m_bool = false;
rhs.m_float = 0.0f;
return *this;
}
// Disallowed construction mechanisms
MyNoCopyClass(const MyNoCopyClass&) = delete;
MyNoCopyClass& operator=(const MyNoCopyClass&) = delete;
int m_int = 0;
bool m_bool = false;
float m_float = 0.0f;
};
/**
* Example Interface class.
*/
class MyInterface
{
public:
virtual ~MyInterface() {}
virtual int Add() = 0;
virtual void Remove(int i) = 0;
};
/**
* Example if POD struct.
*/
struct MyStruct
{
int foo() { return 0; }
int m_value;
};
struct MyEmptyStruct
{};
enum MyEnum
{
zero = 0,
one,
two
};
union MyUnion
{
int m_int;
long m_long;
};
class MyLockFreeAllocator
: public AZStd::allocator
{
struct DelayedFreeItem
{
pointer_type m_ptr;
size_type m_byteSize;
size_type m_alignment;
};
public:
bool is_lock_free() { return true; } //ahem
bool is_stale_read_allowed() { return true; }
bool is_delayed_recycling() { return true; }
~MyLockFreeAllocator()
{
do_delayed_frees();
}
void deallocate(pointer_type ptr, size_type byteSize, size_type alignment)
{
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
DelayedFreeItem item;
item.m_ptr = ptr;
item.m_byteSize = byteSize;
item.m_alignment = alignment;
m_delayedFreeItems.push_back(item);
}
void do_delayed_frees()
{
for (unsigned int i = 0; i < m_delayedFreeItems.size(); ++i)
{
DelayedFreeItem& item = m_delayedFreeItems[i];
AZStd::allocator::deallocate(item.m_ptr, item.m_byteSize, item.m_alignment);
}
m_delayedFreeItems.clear();
}
private:
AZStd::mutex m_mutex;
AZStd::vector<DelayedFreeItem> m_delayedFreeItems;
};
struct MyLifetimeTrackedClass
{
MyLifetimeTrackedClass() = default;
MyLifetimeTrackedClass(MyLifetimeTrackedClass&& rhs)
: m_bool(rhs.m_bool)
{
m_moved = true;
}
MyLifetimeTrackedClass& operator=(MyLifetimeTrackedClass&& rhs)
{
m_moveassigned = true;
m_bool = rhs.m_bool;
return *this;
}
MyLifetimeTrackedClass(const MyLifetimeTrackedClass& rhs)
: m_bool(rhs.m_bool)
{
m_copied = true;
}
MyLifetimeTrackedClass& operator=(const MyLifetimeTrackedClass& rhs)
{
m_bool = rhs.m_bool;
m_assigned = true;
return *this;
}
bool m_bool = false;
bool m_moved = false;
bool m_moveassigned = false;
bool m_copied = false;
bool m_assigned = false;
};
}
#endif // AZSTD_UNITTEST_USERTYPES_H
#pragma once
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,832 @@
/*
* 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 <SerializeContextFixture.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/Utils.h>
namespace UnitTest
{
class VariantSerializationTest
: public AllocatorsFixture
{
public:
// We must expose the class for serialization first.
void SetUp() override
{
AllocatorsFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
AZ::Entity::Reflect(m_serializeContext.get());
}
void TearDown() override
{
m_serializeContext->EnableRemoveReflection();
AZ::Entity::Reflect(m_serializeContext.get());
m_serializeContext->DisableRemoveReflection();
m_serializeContext.reset();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AllocatorsFixture::TearDown();
}
protected:
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
};
struct VariantWrapper
{
AZ_TYPE_INFO(VariantWrapper, "{B086FD5B-1E6F-4CB1-9379-80C35DA3B430}");
AZ_CLASS_ALLOCATOR(VariantWrapper, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<VariantWrapper>()
->Field("WrappedField", &VariantWrapper::m_wrappedVariant)
;
}
}
AZStd::variant<uint64_t>* m_wrappedVariant{};
};
TEST_F(VariantSerializationTest, VariantWithMonostateAlternativeSerializesCorrectly)
{
using TestVariant1 = AZStd::variant<AZStd::monostate>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
} });
TestVariant1 testVariant;
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&testVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
TestVariant1 loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
EXPECT_EQ(testVariant, loadVariant);
}
TEST_F(VariantSerializationTest, VariantWithOneAlternativeSerializesCorrectly)
{
using TestVariant1 = AZStd::variant<AZ::Entity>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
}});
TestVariant1 testVariant{ AZ::Entity("Variant") };
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&testVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
TestVariant1 loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
EXPECT_EQ(testVariant.index(), loadVariant.index());
ASSERT_EQ(0U, testVariant.index());
EXPECT_EQ(AZStd::get<0>(testVariant).GetName(), AZStd::get<0>(loadVariant).GetName());
}
TEST_F(VariantSerializationTest, VariantWithOneAlternativeWhichIsPointerTypeSerializesCorrectly)
{
using TestVariant1 = AZStd::variant<AZ::Entity*>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
} });
TestVariant1 testVariant{ aznew AZ::Entity(AZ::EntityId(42), "Variant Pointer") };
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&testVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
TestVariant1 loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
EXPECT_EQ(testVariant.index(), loadVariant.index());
ASSERT_EQ(0U, testVariant.index());
AZ::Entity* testEntity = AZStd::get<0>(testVariant);
AZ::Entity* loadEntity = AZStd::get<0>(loadVariant);
EXPECT_NE(testEntity, loadEntity);
ASSERT_NE(loadEntity, nullptr);
EXPECT_EQ(testEntity->GetId(), loadEntity->GetId());
EXPECT_EQ(testEntity->GetName(), loadEntity->GetName());
delete testEntity;
delete loadEntity;
AZStd::get<0>(testVariant) = nullptr;
AZStd::get<0>(loadVariant) = nullptr;
}
TEST_F(VariantSerializationTest, MultipleAlternativeSerializesCorrectly)
{
using TestVariant1 = AZStd::variant<int32_t, AZStd::string>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
} });
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 0x85;
TestVariant1 sourceVariant{ expectedIntValue };
TestVariant1 loadIntVariant;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadIntVariant, m_serializeContext.get()));
}
EXPECT_EQ(sourceVariant.index(), loadIntVariant.index());
ASSERT_EQ(0U, loadIntVariant.index());
int32_t loadIntValue = AZStd::get<0>(loadIntVariant);
EXPECT_EQ(expectedIntValue, loadIntValue);
// Update source variant with string value and attempt to serialize it out and back in
const AZStd::string expectedStringValue = "Our Dog Food Eats the Dog";
sourceVariant = expectedStringValue;
TestVariant1 loadStringVariant;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadStringVariant, m_serializeContext.get()));
}
EXPECT_EQ(sourceVariant.index(), loadStringVariant.index());
ASSERT_EQ(1U, loadStringVariant.index());
const AZStd::string& loadStringValue = AZStd::get<1>(loadStringVariant);
EXPECT_EQ(expectedStringValue, loadStringValue);
}
TEST_F(VariantSerializationTest, VariantStoringAnyAlternativeSerializesCorrectly)
{
using TestVariant1 = AZStd::variant<AZStd::any>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
} });
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 0x2A;
TestVariant1 sourceVariant{ AZStd::make_any<int32_t>(expectedIntValue) };
AZStd::any& sourceAnyValue = AZStd::get<0>(sourceVariant);
EXPECT_TRUE(sourceAnyValue.is<int32_t>());
int32_t* sourceIntValue = AZStd::any_cast<int32_t>(&sourceAnyValue);
ASSERT_NE(nullptr, sourceIntValue);
EXPECT_EQ(expectedIntValue, *sourceIntValue);
TestVariant1 loadAnyVariant1;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadAnyVariant1, m_serializeContext.get()));
}
EXPECT_EQ(sourceVariant.index(), loadAnyVariant1.index());
ASSERT_EQ(0U, loadAnyVariant1.index());
AZStd::any& loadAnyValue = AZStd::get<0>(loadAnyVariant1);
EXPECT_TRUE(loadAnyValue.is<int32_t>());
int32_t* loadIntValue = AZStd::any_cast<int32_t>(&loadAnyValue);
ASSERT_NE(nullptr, loadIntValue);
EXPECT_EQ(expectedIntValue, *loadIntValue);
}
TEST_F(VariantSerializationTest, AnyStoringVariantSavesAlternativeAndLoadsAlternativeCorrectly)
{
using TestVariant1 = AZStd::variant<int32_t>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<TestVariant1>();
} });
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 0170;
AZStd::any sourceAny = AZStd::make_any<TestVariant1>(AZStd::in_place_type_t<int32_t>{}, expectedIntValue);
EXPECT_TRUE(sourceAny.is<TestVariant1>());
TestVariant1* sourceVariantValue = AZStd::any_cast<TestVariant1>(&sourceAny);
ASSERT_NE(nullptr, sourceVariantValue);
EXPECT_EQ(expectedIntValue, AZStd::get<0>(*sourceVariantValue));
AZStd::any loadAny;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_JSON);
objStream->WriteClass(&sourceAny);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadAny, m_serializeContext.get()));
}
// Due to the Variant Serialization only writing out the alternative to disk and the AZStd::any class
// being only able to determine the type dynamically, the type that is stored in the any is the
// alternative, not the variant
EXPECT_TRUE(loadAny.is<int>());
int* loadIntValue = AZStd::any_cast<int>(&loadAny);
ASSERT_NE(nullptr, loadIntValue);
EXPECT_EQ(expectedIntValue, *loadIntValue);
}
TEST_F(VariantSerializationTest, TypeWhichWrapsVariantSavesAndLoadsCorrectly)
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
VariantWrapper::Reflect(serializeContext);
} });
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 7001;
VariantWrapper saveWrapper;
saveWrapper.m_wrappedVariant = new AZStd::variant<uint64_t>(expectedIntValue);
EXPECT_EQ(expectedIntValue, AZStd::get<0>(*saveWrapper.m_wrappedVariant));
VariantWrapper loadWrapper;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_JSON);
objStream->WriteClass(&saveWrapper);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadWrapper, m_serializeContext.get()));
}
ASSERT_NE(nullptr, loadWrapper.m_wrappedVariant);
EXPECT_NE(saveWrapper.m_wrappedVariant, loadWrapper.m_wrappedVariant);
ASSERT_EQ(0U, loadWrapper.m_wrappedVariant->index());
EXPECT_EQ(expectedIntValue, AZStd::get<0>(*loadWrapper.m_wrappedVariant));
delete saveWrapper.m_wrappedVariant;
// SerializeCotnext IObjectFactory allocates memory for types without AZClassAllocator using azmalloc
// None of the AZStd::containers implement the AZ_CLASS_ALLOCATOR, so it uses the os allocator by default
azdestroy(loadWrapper.m_wrappedVariant);
}
TEST_F(VariantSerializationTest, VariantStoringVariantSerializesCorrectly)
{
using InnerVariant = AZStd::variant<float, int32_t>;
using VariantCeption = AZStd::variant<bool, InnerVariant, bool>;
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<VariantCeption>();
} });
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = -43;
// Sets the int32_t element of the inner variant
// Therefore the outer variant index should be 1 and the inner variant index should be 1
VariantCeption sourceCeptionVariant{ InnerVariant{expectedIntValue} };
VariantCeption loadCeptionVariant;
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceCeptionVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadCeptionVariant, m_serializeContext.get()));
}
EXPECT_EQ(sourceCeptionVariant.index(), loadCeptionVariant.index());
ASSERT_EQ(1U, loadCeptionVariant.index());
InnerVariant& innerVariant = AZStd::get<1>(loadCeptionVariant);
EXPECT_EQ(1U, innerVariant.index());
EXPECT_EQ(expectedIntValue, AZStd::get<1>(innerVariant));
}
TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeCanBeLoadedByVariantWithIntAlternativeAtDifferentIndex)
{
using SaveVariant = AZStd::variant<int32_t>;
using LoadVariant = AZStd::variant<bool, double, const int32_t, int32_t>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 72;
// Sets the int32_t element of the source variant which is the zeroth index
SaveVariant sourceVariant{ expectedIntValue };
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
}}
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
// Source variant should have different index than loaded variant
EXPECT_EQ(0U, sourceVariant.index());
EXPECT_NE(sourceVariant.index(), loadVariant.index());
// The AZ Serialization system does not distinguish between const and non-const.
// As the LoadVariant type has const int32_t as the 2nd index, it is the alternative that will be loaded
constexpr size_t expectedLoadIndex = 2U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
EXPECT_TRUE(AZStd::holds_alternative<const int32_t>(loadVariant));
EXPECT_EQ(AZStd::get<0>(sourceVariant), AZStd::get<expectedLoadIndex>(loadVariant));
}
TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantPointerSucceeds)
{
using SaveVariant = AZStd::variant<int32_t>;
using LoadVariant = AZStd::variant<bool, AZStd::variant<int32_t>*>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 146;
// Sets the int32_t element of the source variant which is the zeroth index
SaveVariant sourceVariant{ expectedIntValue };
EXPECT_EQ(0U, sourceVariant.index());
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
constexpr size_t expectedLoadIndex = 1U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
ASSERT_TRUE(AZStd::holds_alternative<AZStd::variant<int32_t>*>(loadVariant));
AZStd::variant<int32_t>*& innerVariant = AZStd::get<expectedLoadIndex>(loadVariant);
ASSERT_NE(nullptr, innerVariant);
EXPECT_TRUE(AZStd::holds_alternative<int32_t>(*innerVariant));
int32_t* loadInt = AZStd::get_if<int32_t>(innerVariant);
ASSERT_NE(nullptr, loadInt);
EXPECT_EQ(expectedIntValue, *loadInt);
azdestroy(innerVariant);
}
TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantPointerWhichHasAnIntPointerSucceeds)
{
using SaveVariant = AZStd::variant<int32_t>;
using LoadVariant = AZStd::variant<AZStd::variant<int32_t*>*, double>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 146;
// Sets the int32_t element of the source variant which is the zeroth index
SaveVariant sourceVariant{ expectedIntValue };
EXPECT_EQ(0U, sourceVariant.index());
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
constexpr size_t expectedLoadIndex = 0U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
ASSERT_TRUE(AZStd::holds_alternative<AZStd::variant<int32_t*>*>(loadVariant));
AZStd::variant<int32_t*>*& innerVariant = AZStd::get<expectedLoadIndex>(loadVariant);
ASSERT_NE(nullptr, innerVariant);
EXPECT_TRUE(AZStd::holds_alternative<int32_t*>(*innerVariant));
int32_t* loadInt = AZStd::get<0>(*innerVariant);
ASSERT_NE(nullptr, loadInt);
EXPECT_EQ(expectedIntValue, *loadInt);
azdestroy(loadInt);
azdestroy(innerVariant);
}
TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithInnerVariantIntTypeAndIntPointerTypeAndIntValueTypeChoosesIntValueType)
{
using SaveVariant = AZStd::variant<int32_t>;
using LoadVariant = AZStd::variant<AZStd::variant<int32_t*>*, int32_t*, int32_t>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 146;
// Sets the int32_t element of the source variant which is the zeroth index
SaveVariant sourceVariant{ expectedIntValue };
EXPECT_EQ(0U, sourceVariant.index());
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
constexpr size_t expectedLoadIndex = 2U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
int32_t loadInt = AZStd::get<expectedLoadIndex>(loadVariant);
EXPECT_EQ(expectedIntValue, loadInt);
}
TEST_F(VariantSerializationTest, SavingIntAlternativeAndLoadingToRootVariantSucceeds)
{
using LoadVariant = AZStd::variant<int32_t*>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 146;
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&expectedIntValue);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
constexpr size_t expectedLoadIndex = 0U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
int32_t* loadInt = AZStd::get<expectedLoadIndex>(loadVariant);
EXPECT_EQ(expectedIntValue, *loadInt);
azdestroy(loadInt);
}
TEST_F(VariantSerializationTest, SavingAssetAlternativeAndLoadingToRootVariantSucceeds)
{
using SaveVariant = AZStd::variant<AZ::Data::Asset<AZ::Data::AssetData>>;
using LoadVariant = AZStd::variant<AZ::Data::Asset<AZ::Data::AssetData>>;
AZ::Data::AssetType sliceAssetTypeId("{C62C7A87-9C09-4148-A985-12F2C99C0A45}");
AZ::Data::Asset<AZ::Data::AssetData> saveAsset(AZ::Data::AssetId{}, sliceAssetTypeId);
SaveVariant saveVariant(saveAsset);
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&saveVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
constexpr size_t expectedLoadIndex = 0U;
ASSERT_EQ(expectedLoadIndex, loadVariant.index());
AZ::Data::Asset<AZ::Data::AssetData>& loadAsset= AZStd::get<expectedLoadIndex>(loadVariant);
EXPECT_FALSE(loadAsset.GetId().IsValid());
}
TEST_F(VariantSerializationTest, SavingVariantWithIntAlternativeAndLoadingToVariantWithoutIntAlternativeFails)
{
using SaveVariant = AZStd::variant<int32_t>;
using LoadVariant = AZStd::variant<bool, double>;
// Store integer in variant and attempt to serialize it out and back in
constexpr int32_t expectedIntValue = 72;
// Sets the int32_t element of the source variant which is the zeroth index
SaveVariant sourceVariant{ expectedIntValue };
EXPECT_EQ(0U, sourceVariant.index());
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_XML);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
AZ_TEST_START_TRACE_SUPPRESSION;
EXPECT_FALSE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
}
TEST_F(VariantSerializationTest, SavingVariantWithVectorOfStringAlternativeAndIsAbleToLoadCorrectly)
{
using SaveVariant = AZStd::variant<AZStd::vector<AZStd::string>, float>;
using LoadVariant = AZStd::variant<bool, AZStd::vector<AZStd::string>>;
// Store a vector of strings and attempt serialized to a stream and back
const AZStd::string expectedStringValue1{ "ChimeYard" };
const AZStd::string expectedStringValue2{ "BirdCakeFactory" };
// Sets the vector of string element which corresponds to index 1 of the source variant
SaveVariant sourceVariant{ AZStd::vector<AZStd::string>{expectedStringValue1, expectedStringValue2} };
EXPECT_EQ(0U, sourceVariant.index());
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveVariant>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
objStream->WriteClass(&sourceVariant);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
ASSERT_EQ(1, loadVariant.index());
EXPECT_EQ(AZStd::get<0>(sourceVariant), AZStd::get<1>(loadVariant));
}
TEST_F(VariantSerializationTest, SavingVectorOfVectorTypeIsAbleToLoadIntoVariantCorrectly)
{
using SaveType = AZStd::vector<AZStd::vector<AZStd::string>>;
using LoadVariant = AZStd::variant<SaveType>;
// Store a vector of vector of strings and attempt serialized to a stream and back
const AZStd::string expectedStringValue1{ "Zubat Key" };
const AZStd::string expectedStringValue2{ "Yubioh Key" };
const AZStd::string expectedStringValue3{ "Gelato Token" };
SaveType twoStepsVectorAndTwoStepsBack;
// Set the inner vector first element to have an expected string value of "Yubioh Key" and "Gelato Token"
twoStepsVectorAndTwoStepsBack.emplace_back();
twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue2);
twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue3);
// Set the inner vector second element to have an expected string value of "Gelato Token" and "Zubat Key"
twoStepsVectorAndTwoStepsBack.emplace_back();
twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue3);
twoStepsVectorAndTwoStepsBack.back().push_back(expectedStringValue1);
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
{
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<SaveType>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
objStream->WriteClass(&twoStepsVectorAndTwoStepsBack);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
}
ScopedSerializeContextReflector loadReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<LoadVariant>();
} }
);
LoadVariant loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
ASSERT_EQ(0, loadVariant.index());
EXPECT_EQ(twoStepsVectorAndTwoStepsBack, AZStd::get<0>(loadVariant));
}
TEST_F(VariantSerializationTest, SavingandLoadingVectorOfVariants_IsAbleToLoadAlternativesAtIndex1OrHigher_WithoutCrashing)
{
using VariantVectorA = AZStd::vector<AZStd::variant<AZStd::string, int32_t>>;
using VariantVectorB = AZStd::vector<AZStd::variant<int32_t, AZStd::string>>;
VariantVectorA varA;
VariantVectorB varB;
varA.push_back(1);
varA.push_back("str");
varB.push_back(1);
varB.push_back("str");
// VariantVectorA, works fine.
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<VariantVectorA>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
objStream->WriteClass(&varA);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
VariantVectorA loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
}
// VariantVectorB, should also work fine.
{
AZStd::vector<char> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
ScopedSerializeContextReflector scopedReflector(*m_serializeContext, {
[](AZ::SerializeContext* serializeContext)
{
serializeContext->RegisterGenericType<VariantVectorB>();
} }
);
auto objStream = AZ::ObjectStream::Create(&byteStream, *m_serializeContext, AZ::ObjectStream::ST_BINARY);
objStream->WriteClass(&varB);
objStream->Finalize();
byteStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
VariantVectorB loadVariant;
EXPECT_TRUE(AZ::Utils::LoadObjectFromStreamInPlace(byteStream, loadVariant, m_serializeContext.get()));
}
}
}
@@ -0,0 +1,974 @@
/*
* 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 "UserTypes.h"
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/bitset.h>
#include <AzCore/std/allocator_static.h>
#include <AzCore/std/allocator_ref.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/utils.h>
using namespace AZStd;
using namespace UnitTestInternal;
/**
* Make sure a vector is empty, and control all functions to return the proper values.
* Empty vector as all AZStd containers should not have allocated any memory. Empty and clean containers are not the same.
*/
#define AZ_TEST_VALIDATE_EMPTY_VECTOR(_Vector) \
EXPECT_TRUE(_Vector.validate()); \
EXPECT_EQ(0, _Vector.size()); \
EXPECT_TRUE(_Vector.empty()); \
EXPECT_EQ(0, _Vector.capacity()); \
EXPECT_TRUE(_Vector.begin() == _Vector.end()); \
EXPECT_EQ(nullptr, _Vector.data())
/**
* Validate a vector for certain number of elements.
*/
#define AZ_TEST_VALIDATE_VECTOR(_Vector, _NumElements) \
EXPECT_TRUE(_Vector.validate()); \
EXPECT_EQ(_NumElements, _Vector.size()); \
EXPECT_TRUE((_NumElements > 0) ? !_Vector.empty() : _Vector.empty()); \
EXPECT_TRUE((_NumElements > 0) ? _Vector.capacity() >= _NumElements : true); \
EXPECT_TRUE((_NumElements > 0) ? _Vector.begin() != _Vector.end() : _Vector.begin() == _Vector.end()); \
EXPECT_NE(nullptr, _Vector.data())
namespace UnitTest
{
#if !AZ_UNIT_TEST_SKIP_STD_VECTOR_AND_ARRAY_TESTS
struct MyCtorClass
{
MyCtorClass() { ++s_numConstructedObjects; }
~MyCtorClass() { --s_numConstructedObjects; }
static int s_numConstructedObjects;
};
int MyCtorClass::s_numConstructedObjects = 0;
struct VectorMoveOnly
{
VectorMoveOnly() = default;
VectorMoveOnly(int num)
: m_num(num)
{
}
VectorMoveOnly(const VectorMoveOnly&) = delete;
VectorMoveOnly& operator=(const VectorMoveOnly&) = delete;
VectorMoveOnly(VectorMoveOnly&& other)
: m_num(other.m_num)
{
other.m_num = 0;
}
VectorMoveOnly& operator=(VectorMoveOnly&& other)
{
m_num = other.m_num;
other.m_num = 0;
return *this;
}
int m_num = 0;
};
class Arrays
: public AllocatorsFixture
{
void SetUp() override
{
AllocatorsFixture::SetUp();
MyCtorClass::s_numConstructedObjects = 0;
}
};
TEST_F(Arrays, Pair)
{
int val1 = 20;
int val2 = 30;
AZStd::pair<int, int> pi1(val1, val2);
AZStd::pair<int, int> pi2(val2, val1);
AZStd::pair<int&, int&> pr1(val1, val2);
AZStd::pair<int&, int&> pr2(val2, val1);
AZ_TEST_ASSERT(pi1 == pi1);
AZ_TEST_ASSERT(pr1 == pr1);
AZ_TEST_ASSERT(pi1 == pr1);
AZ_TEST_ASSERT(pi1 != pi2);
AZ_TEST_ASSERT(pr1 != pr2);
AZ_TEST_ASSERT(pi1 != pr2);
AZ_TEST_ASSERT(pi1 <= pi2);
AZ_TEST_ASSERT(pr1 <= pr2);
AZ_TEST_ASSERT(pi1 <= pr2);
AZ_TEST_ASSERT(pi1 < pi2);
AZ_TEST_ASSERT(pr1 < pr2);
AZ_TEST_ASSERT(pi1 < pr2);
AZ_TEST_ASSERT(pi2 >= pi1);
AZ_TEST_ASSERT(pr2 >= pr1);
AZ_TEST_ASSERT(pi2 >= pr1);
AZ_TEST_ASSERT(pi2 > pi1);
AZ_TEST_ASSERT(pr2 > pr1);
AZ_TEST_ASSERT(pi2 > pr1);
}
TEST_F(Arrays, PairConstructSucceeds)
{
struct FirstElement
{
FirstElement() = default;
FirstElement(int32_t value)
: m_value(value)
{
}
int32_t m_value{};
};
struct SecondElement
{
SecondElement() = default;
SecondElement(double value, bool selected)
: m_value{ value }
, m_selected{ selected }
{
}
double m_value{};
bool m_selected{};
};
AZStd::pair<FirstElement, SecondElement> testPair(AZStd::piecewise_construct_t{}, AZStd::forward_as_tuple(42), AZStd::forward_as_tuple(16.0, true));
EXPECT_EQ(42, testPair.first.m_value);
EXPECT_DOUBLE_EQ(16.0, testPair.second.m_value);
EXPECT_TRUE(testPair.second.m_selected);
}
TEST_F(Arrays, Vector)
{
// VectorContainerTest-Begin
typedef vector<int> vector_int_type;
//////////////////////////////////////////////////////////////////////////////////////////
// Vector functionality
// Default vector (integral type).
vector_int_type int_vector_default;
AZ_TEST_VALIDATE_EMPTY_VECTOR(int_vector_default);
// Default vector (non-integral type).
vector<MyClass> myclass_vector_default;
AZ_TEST_VALIDATE_EMPTY_VECTOR(myclass_vector_default);
// Create a vector (using fill ctor, with memset optimization to set the values)
vector<char> char_vector(10, 'A');
AZ_TEST_VALIDATE_VECTOR(char_vector, 10);
for (vector<char>::iterator iter = char_vector.begin(); iter != char_vector.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 'A');
}
// Fill ctor with out memset optimization. validate iterators too.
vector_int_type int_vector(33, 55);
AZ_TEST_VALIDATE_VECTOR(int_vector, 33);
for (vector_int_type::iterator iter = int_vector.begin(); iter != int_vector.end(); ++iter)
{
AZ_TEST_ASSERT(int_vector.validate_iterator(iter));
AZ_TEST_ASSERT(*iter == 55);
}
AZ_TEST_ASSERT(int_vector.validate_iterator(int_vector.end()) == isf_valid);
// Fill ctor non-intergral type
vector<MyClass> myclass_vector(22, MyClass(11));
AZ_TEST_VALIDATE_VECTOR(myclass_vector, 22);
for (vector<MyClass>::iterator iter = myclass_vector.begin(); iter != myclass_vector.end(); ++iter)
{
AZ_TEST_ASSERT(iter->m_data == 11);
}
// Iter copy ctor
vector_int_type int_vector2(int_vector.begin(), int_vector.end());
AZ_TEST_VALIDATE_VECTOR(int_vector2, 33);
AZ_TEST_ASSERT(int_vector2 == int_vector);
AZ_TEST_ASSERT(int_vector2 != int_vector_default);
// Copy ctor.
vector_int_type int_vector1(int_vector);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 33);
AZ_TEST_ASSERT(int_vector1 == int_vector);
AZ_TEST_ASSERT(int_vector1 != int_vector_default);
// reserve
int_vector1.reserve(200);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 33);
AZ_TEST_ASSERT(int_vector1.capacity() == 200);
// resize with default value
int_vector1.resize(int_vector1.size() + 1);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 34);
AZ_TEST_ASSERT(int_vector1.front() == 55);
AZ_TEST_ASSERT(int_vector1.back() == 0); // default value
// resize with provided value
int_vector1.resize(int_vector1.size() + 1, 60);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 35);
AZ_TEST_ASSERT(int_vector1.back() == 60);
// use the we have 3 different values and check the access operators
for (AZStd::size_t i = 0; i < int_vector1.size(); ++i)
{
AZ_TEST_ASSERT(int_vector1[i] == int_vector1.at(i));
}
// resize with trim
int_vector1.resize(10);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
AZ_TEST_ASSERT(int_vector1.front() == 55);
AZ_TEST_ASSERT(int_vector1.back() == 55);
// push back
int_vector1.push_back();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 11);
// pop back
int_vector1.pop_back();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
// push_back with value.
int_vector1.push_back(100);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 11);
AZ_TEST_ASSERT(int_vector1.back() == 100);
// set capacity
int_vector1.set_capacity(11);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 11);
AZ_TEST_ASSERT(int_vector1.capacity() == 11);
AZ_TEST_ASSERT(int_vector1.back() == 100);
// push back with capacity and change capacity!
int_vector1.push_back();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 12);
AZ_TEST_ASSERT(int_vector1.capacity() >= 12);
int_vector1.set_capacity(11);
int_vector1.push_back(101);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 12);
AZ_TEST_ASSERT(int_vector1.capacity() >= 12);
AZ_TEST_ASSERT(int_vector1.back() == 101);
// push back with capacity and change capacity!
int_vector1.set_capacity(11);
int_vector1.push_back(101);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 12);
AZ_TEST_ASSERT(int_vector1.capacity() >= 12);
AZ_TEST_ASSERT(int_vector1.back() == 101);
// insert
// insert at the end with capacity change
int_vector1.set_capacity(12);
int_vector1.insert(int_vector1.end(), 201);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 13);
AZ_TEST_ASSERT(int_vector1.capacity() >= 13);
AZ_TEST_ASSERT(int_vector1.back() == 201);
// insert without capacity change
int_vector1.insert(int_vector1.end() - 1, 5, 202);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 18);
AZ_TEST_ASSERT(int_vector1.back() == 201);
AZ_TEST_ASSERT(*(int_vector1.end() - 2) == 202);
// insert with overlapping areas
int_vector1.insert(int_vector1.end() - 2, 1, 203);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 19);
AZ_TEST_ASSERT(int_vector1.back() == 201);
AZ_TEST_ASSERT(*(int_vector1.end() - 3) == 203);
// insert from another vector.
int_vector1.insert(int_vector1.end(), int_vector.begin(), int_vector.end());
AZ_TEST_VALIDATE_VECTOR(int_vector1, 19 + int_vector.size());
AZ_TEST_ASSERT(int_vector1.back() == 55); // last element from int_vector.
// insert with initializer list.
int_vector1.insert(int_vector1.end(), { 23, 24, 25 });
AZ_TEST_VALIDATE_VECTOR(int_vector1, 55);
AZ_TEST_ASSERT(int_vector1.back() == 25);
// erase
int_vector1.erase(int_vector1.begin(), int_vector1.end());
AZ_TEST_VALIDATE_VECTOR(int_vector1, 0); // Zero elements but valid capacity.
int_vector1.push_back(10);
int_vector1.push_back(20);
int_vector1.push_back(30);
vector_int_type::iterator iter = int_vector1.erase(int_vector1.begin() + 1);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 2);
AZ_TEST_ASSERT(*iter == 30);
AZ_TEST_ASSERT(int_vector1.front() == 10);
// clear
int_vector1.clear();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 0); // Zero elements but valid capacity.
// swap
int_vector1.swap(int_vector);
AZ_TEST_VALIDATE_VECTOR(int_vector, 0);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 33);
AZ_TEST_ASSERT(int_vector1.front() == 55);
// swap rvalue reference binding to temporary
int_vector1.swap(vector_int_type());
AZ_TEST_VALIDATE_EMPTY_VECTOR(int_vector1);
// assign
int_vector1.assign(10, 15);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
int_vector.assign(int_vector1.begin(), int_vector1.end());
AZ_TEST_VALIDATE_VECTOR(int_vector, 10);
AZ_TEST_ASSERT(int_vector.front() == 15);
// alignment
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)int_vector.data() % 4) == 0); // default int alignment
// make sure every vector allocation is aligned.
vector<MyClass> aligned_vector(5, 99);
AZ_TEST_ASSERT(((AZStd::size_t)aligned_vector.data() & (alignment_of<MyClass>::value - 1)) == 0);
AZ_TEST_ASSERT(((AZStd::size_t)&aligned_vector[0] & (alignment_of<MyClass>::value - 1)) == 0);
// reverse iterators
int_vector.clear();
int_vector.push_back(1);
int_vector.push_back(2);
int_vector.push_back(3);
int_vector.push_back(4);
int i = 4;
for (auto it = int_vector.rbegin(); it != int_vector.rend(); ++it, --i)
{
AZ_TEST_ASSERT(int_vector.validate_iterator(it));
AZ_TEST_ASSERT(*it == i);
}
i = 4;
for (auto it = int_vector.rbegin(); it != int_vector.rend(); ++it, --i)
{
AZ_TEST_ASSERT(int_vector.validate_iterator(it));
AZ_TEST_ASSERT(*it == i);
}
// resize_no_construct test (it technically behaves like reserve that updates the size of the container)
vector<MyCtorClass> myCtorClassArray;
AZ_TEST_ASSERT(MyCtorClass::s_numConstructedObjects == 0);
myCtorClassArray.resize_no_construct(10);
AZ_TEST_ASSERT(myCtorClassArray.size() == 10);
// Test that no constructors have been called even though we resized the container to 10 elements
AZ_TEST_ASSERT(MyCtorClass::s_numConstructedObjects == 0);
// test that resize_no_construct shrinks the container if eneded same as resize
myCtorClassArray.resize_no_construct(0);
AZ_TEST_ASSERT(myCtorClassArray.empty());
AZ_TEST_ASSERT(MyCtorClass::s_numConstructedObjects == -10); // we should destroy all objects that we did not construct
// Vector functionality
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Vector allocator tests
typedef static_buffer_allocator<16*1024, 1> static_buffer_16KB;
static_buffer_16KB myMemoryManager1;
static_buffer_16KB myMemoryManager2;
typedef allocator_ref<static_buffer_16KB> static_allocator_ref_type;
static_allocator_ref_type allocator1(myMemoryManager1, "Mystack allocator 1");
static_allocator_ref_type allocator2(myMemoryManager2, "Mystack allocator 2");
typedef vector<int, static_allocator_ref_type > IntVectorMyAllocator;
IntVectorMyAllocator int_vector10(100, 13, allocator1); /// Allocate 100 elements using memory manager 1
AZ_TEST_VALIDATE_VECTOR(int_vector10, 100);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() == 100 * sizeof(int));
// leak_and_reset
int_vector10.leak_and_reset(); /// leave the allocated memory and reset the vector.
AZ_TEST_VALIDATE_EMPTY_VECTOR(int_vector10);
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() == 100 * sizeof(int));
myMemoryManager1.reset(); /// discard the memory
// allocate again from myMemoryManager1
int_vector10.resize(100, 15);
int_vector10.set_allocator(allocator2);
AZ_TEST_VALIDATE_VECTOR(int_vector10, 100);
// now we move the allocated size from menager1 to manager2 (without freeing menager1)
AZ_TEST_ASSERT(myMemoryManager1.get_allocated_size() == myMemoryManager2.get_allocated_size());
myMemoryManager1.reset(); // flush manager 1 again (int_vector10 is stored in manager 2)
// swap with different allocators
IntVectorMyAllocator int_vector11(50, 25, allocator1); // create copy in manager1
AZ_TEST_VALIDATE_VECTOR(int_vector11, 50);
int_vector11.swap(int_vector10); // swap the vectors content (since the allocators are different)
AZ_TEST_VALIDATE_VECTOR(int_vector10, 50);
AZ_TEST_VALIDATE_VECTOR(int_vector11, 100);
AZ_TEST_ASSERT(int_vector11.front() == 15);
AZ_TEST_ASSERT(int_vector10.front() == 25);
//////////////////////////////////////////////////////////////////////////////////////////
// Test asserts (which don't cause throw exceptions)
int_vector10.clear();
AZ_TEST_START_TRACE_SUPPRESSION;
int_vector10.reserve(1000000); // too many elements, 1 assert on too many, 1 assert on allocator returning NULL
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
#ifdef AZSTD_HAS_CHECKED_ITERATORS
int_vector.clear();
iter = int_vector.end();
AZ_TEST_START_TRACE_SUPPRESSION;
int b = *iter; // the end if is valid but can not dereferenced
(void)b;
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
int_vector.push_back(1);
AZ_TEST_START_TRACE_SUPPRESSION;
int_vector.validate_iterator(iter); // The push back should make the end iterator invalid.
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
iter = int_vector.begin();
int_vector.clear();
AZ_TEST_START_TRACE_SUPPRESSION;
int_vector.validate_iterator(iter); // The clear should invalidate all iterators
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
#endif
//////////////////////////////////////////////////////////////////////////////////////////
// Vector rvalue refs test
int_vector.clear();
int_vector.resize(33, 55);
void* data = int_vector.data();
int_vector1 = AZStd::move(int_vector);
AZ_TEST_ASSERT(int_vector1.data() == data);
AZStd::vector<int> int_moved_vector(AZStd::move(int_vector1));
AZ_TEST_ASSERT(int_moved_vector.data() == data);
myclass_vector.clear();
myclass_vector.push_back(MyClass(23));
AZ_TEST_ASSERT(myclass_vector.size() == 1);
AZ_TEST_ASSERT(myclass_vector[0].m_data == 23);
AZ_TEST_ASSERT(myclass_vector[0].m_isMoved == true); // the compiler should move the class automatically
myclass_vector.emplace_back(44);
AZ_TEST_ASSERT(myclass_vector.size() == 2);
AZ_TEST_ASSERT(myclass_vector[1].m_data == 44);
AZ_TEST_ASSERT(myclass_vector[1].m_isMoved == false);
myclass_vector.emplace(myclass_vector.begin(), 33);
AZ_TEST_ASSERT(myclass_vector.size() == 3);
AZ_TEST_ASSERT(myclass_vector[0].m_data == 33);
AZ_TEST_ASSERT(myclass_vector[0].m_isMoved == false);
myclass_vector.insert(myclass_vector.begin() + 1, 22);
AZ_TEST_ASSERT(myclass_vector.size() == 4);
AZ_TEST_ASSERT(myclass_vector[0].m_data == 33);
AZ_TEST_ASSERT(myclass_vector[1].m_data == 22);
AZ_TEST_ASSERT(myclass_vector[1].m_isMoved == true);
AZ_TEST_ASSERT(myclass_vector[2].m_data == 23);
// move iterator
AZStd::vector<VectorMoveOnly> move_only_vector;
move_only_vector.push_back(1);
move_only_vector.push_back(2);
move_only_vector.push_back(3);
move_only_vector.push_back(4);
AZStd::vector<VectorMoveOnly> result_move_only_vector{ AZStd::make_move_iterator(move_only_vector.begin()), AZStd::make_move_iterator(move_only_vector.end()) };
for (const auto& move_only1 : move_only_vector)
{
EXPECT_EQ(0, move_only1.m_num);
}
int uniquePtrIntValue = 1;
for (const auto& result_move_only : result_move_only_vector)
{
EXPECT_EQ(uniquePtrIntValue, result_move_only.m_num);
++uniquePtrIntValue;
}
// VectorContainerTest-End
}
TEST_F(Arrays, FixedVector)
{
// FixedVectorContainerTest-Begin
//////////////////////////////////////////////////////////////////////////////////////////
// Fixed Vector functionality
// Default vector (integral type).
fixed_vector<int, 50> int_vector_default;
AZ_TEST_VALIDATE_VECTOR(int_vector_default, 0);
// Default vector (non-integral type).
fixed_vector<MyClass, 10> myclass_vector_default;
AZ_TEST_VALIDATE_VECTOR(myclass_vector_default, 0);
// Create a vector (using fill ctor, with memset optimization to set the values)
typedef fixed_vector<char, 10> char_10_type;
char_10_type char_vector(10, 'A');
AZ_TEST_VALIDATE_VECTOR(char_vector, 10);
for (char_10_type::iterator iter = char_vector.begin(); iter != char_vector.end(); ++iter)
{
AZ_TEST_ASSERT(*iter == 'A');
}
// Fill ctor with out memset optimization. validate iterators too.
typedef fixed_vector<int, 50> int_50_t;
int_50_t int_vector(33, 55);
AZ_TEST_VALIDATE_VECTOR(int_vector, 33);
for (int_50_t::iterator iter = int_vector.begin(); iter != int_vector.end(); ++iter)
{
AZ_TEST_ASSERT(int_vector.validate_iterator(iter));
AZ_TEST_ASSERT(*iter == 55);
}
AZ_TEST_ASSERT(int_vector.validate_iterator(int_vector.end()) == isf_valid);
// Fill ctor non-intergral type
typedef fixed_vector<MyClass, 22> myclass_22_t;
myclass_22_t myclass_vector(22, MyClass(11));
AZ_TEST_VALIDATE_VECTOR(myclass_vector, 22);
for (myclass_22_t::iterator iter = myclass_vector.begin(); iter != myclass_vector.end(); ++iter)
{
AZ_TEST_ASSERT(iter->m_data == 11);
}
// Iter copy ctor
int_50_t int_vector2(int_vector.begin(), int_vector.end());
AZ_TEST_VALIDATE_VECTOR(int_vector2, 33);
AZ_TEST_ASSERT(int_vector2 == int_vector);
//AZ_TEST_ASSERT(int_vector2!=int_vector_default);
// Copy ctor.
int_50_t int_vector1(int_vector);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 33);
AZ_TEST_ASSERT(int_vector1 == int_vector);
//AZ_TEST_ASSERT(int_vector1!=int_vector_default);
// resize with default value
int_vector1.resize(int_vector1.size() + 1);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 34);
AZ_TEST_ASSERT(int_vector1.front() == 55);
AZ_TEST_ASSERT(int_vector1.back() == 0);
// resize with provided value
int_vector1.resize(int_vector1.size() + 1, 60);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 35);
AZ_TEST_ASSERT(int_vector1.back() == 60);
// use the we have 3 different values and check the access operators
for (AZStd::size_t i = 0; i < int_vector1.size(); ++i)
{
AZ_TEST_ASSERT(int_vector1[i] == int_vector1.at(i));
}
// resize with trim
int_vector1.resize(10);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
AZ_TEST_ASSERT(int_vector1.front() == 55);
AZ_TEST_ASSERT(int_vector1.back() == 55);
// push back
int_vector1.push_back();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 11);
// pop back
int_vector1.pop_back();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
// push_back with value.
int_vector1.push_back(100);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 11);
AZ_TEST_ASSERT(int_vector1.back() == 100);
//// insert
// insert at the end
int_vector1.insert(int_vector1.end(), 201);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 12);
AZ_TEST_ASSERT(int_vector1.back() == 201);
// insert without capacity change
int_vector1.insert(int_vector1.end() - 1, 5, 202);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 17);
AZ_TEST_ASSERT(int_vector1.back() == 201);
AZ_TEST_ASSERT(*(int_vector1.end() - 2) == 202);
// insert with overlapping areas
int_vector1.insert(int_vector1.end() - 2, 1, 203);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 18);
AZ_TEST_ASSERT(int_vector1.back() == 201);
AZ_TEST_ASSERT(*(int_vector1.end() - 3) == 203);
// insert from another vector.
int_vector1.insert(int_vector1.end(), int_vector.begin(), int_vector.begin() + 3);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 21);
AZ_TEST_ASSERT(int_vector1.back() == 55); // last element from int_vector.
// erase
int_vector1.erase(int_vector1.begin(), int_vector1.end());
AZ_TEST_VALIDATE_VECTOR(int_vector1, 0);
int_vector1.push_back(10);
int_vector1.push_back(20);
int_vector1.push_back(30);
int_50_t::iterator iter = int_vector1.erase(int_vector1.begin() + 1);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 2);
AZ_TEST_ASSERT(*iter == 30);
AZ_TEST_ASSERT(int_vector1.front() == 10);
// clear
int_vector1.clear();
AZ_TEST_VALIDATE_VECTOR(int_vector1, 0);
// swap
int_vector1.swap(int_vector);
AZ_TEST_VALIDATE_VECTOR(int_vector, 0);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 33);
AZ_TEST_ASSERT(int_vector1.front() == 55);
// assign
int_vector1.assign(10, 15);
AZ_TEST_VALIDATE_VECTOR(int_vector1, 10);
int_vector.assign(int_vector1.begin(), int_vector1.end());
AZ_TEST_VALIDATE_VECTOR(int_vector, 10);
AZ_TEST_ASSERT(int_vector.front() == 15);
// alignment
// default int alignment
AZ_TEST_ASSERT(((AZStd::size_t)int_vector.data() % 4) == 0); // default int alignment
// make sure every vector allocation is aligned. My class is aligned on 32 bytes.
myclass_vector_default.push_back(MyClass(10));
AZ_TEST_ASSERT(((AZStd::size_t)myclass_vector_default.data() & (alignment_of<MyClass>::value - 1)) == 0);
AZ_TEST_ASSERT(((AZStd::size_t)&myclass_vector_default[0] & (alignment_of<MyClass>::value - 1)) == 0);
// reverse iterators
// Fixed Vector functionality
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Test asserts (which don't cause throw exceptions)
#ifdef AZSTD_HAS_CHECKED_ITERATORS
int_vector.clear();
iter = int_vector.end();
AZ_TEST_START_TRACE_SUPPRESSION;
int b = *iter; // the end if is valid but can not dereferenced
(void)b;
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
int_vector.push_back(1);
AZ_TEST_START_TRACE_SUPPRESSION;
int_vector.validate_iterator(iter); // The push back should make the end iterator invalid.
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
iter = int_vector.begin();
int_vector.clear();
AZ_TEST_START_TRACE_SUPPRESSION;
int_vector.validate_iterator(iter); // The clear should invalidate all iterators
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
#endif
// FixedVectorContainerTest-End
}
TEST_F(Arrays, FixedVectorSwapSucceeds)
{
// Test dealing with fixed_vectors with big sizes.
// Have to heap allocated since they wont fit in the stack
constexpr int bigFixedVectorSize = 10000000; // enough to make it fail without the fix
AZStd::unique_ptr<fixed_vector<char, bigFixedVectorSize>> big_fixed_vector0 = AZStd::make_unique<fixed_vector<char, bigFixedVectorSize>>();
AZStd::unique_ptr<fixed_vector<char, bigFixedVectorSize>> big_fixed_vector1 = AZStd::make_unique<fixed_vector<char, bigFixedVectorSize>>();
big_fixed_vector0->insert(big_fixed_vector0->end(), bigFixedVectorSize, 0);
big_fixed_vector1->insert(big_fixed_vector1->end(), bigFixedVectorSize, 1);
EXPECT_EQ(big_fixed_vector0->at(0), 0);
EXPECT_EQ(big_fixed_vector1->at(0), 1);
// test swap
big_fixed_vector0->swap(*big_fixed_vector1);
EXPECT_EQ(big_fixed_vector0->at(0), 1);
EXPECT_EQ(big_fixed_vector1->at(0), 0);
}
TEST_F(Arrays, FixedVectorMoveOperationsSucceed)
{
AZStd::fixed_vector<AZStd::unique_ptr<int>, 2> testVector;
testVector.emplace_back(AZStd::make_unique<int>(21));
ASSERT_EQ(1, testVector.size());
EXPECT_EQ(21, *(testVector[0]));
auto moveConstructedVector(AZStd::move(testVector));
EXPECT_EQ(0, testVector.size());
ASSERT_EQ(1, moveConstructedVector.size());
EXPECT_EQ(21, *(moveConstructedVector[0]));
AZStd::fixed_vector<AZStd::unique_ptr<int>, 2> moveAssignedVector;
moveAssignedVector = AZStd::move(moveConstructedVector);
EXPECT_EQ(0, moveConstructedVector.size());
ASSERT_EQ(1, moveAssignedVector.size());
EXPECT_EQ(21, *(moveAssignedVector[0]));
}
TEST_F(Arrays, FixedVectorCanCopyAndMoveWithDifferentCapacity)
{
constexpr AZStd::fixed_vector<int, 32> sourceVector{ 1,2,3,4,5 };
AZStd::fixed_vector<int, 8> copyConstructVector{ sourceVector };
EXPECT_EQ(sourceVector, copyConstructVector);
AZStd::fixed_vector<int, 16> copyAssignVector;
copyAssignVector = sourceVector;
EXPECT_EQ(sourceVector, copyConstructVector);
// Test Move constructor/assignment
AZStd::fixed_vector<int, 32> sourceVector2{ 1,2,3,4,5,6 };
AZStd::fixed_vector<int, 8> moveConstructVector = AZStd::move(sourceVector2);
AZStd::fixed_vector<int, 16> moveAssignVector = AZStd::move(moveConstructVector);
constexpr AZStd::fixed_vector expectedVector{ 1,2,3,4,5,6 };
EXPECT_EQ(expectedVector, moveAssignVector);
}
TEST_F(Arrays, FixedVectorComparisonOperatorsSucceedAsExpected)
{
constexpr AZStd::fixed_vector<int, 32> testVector{ 1,2,3,4,5 };
constexpr AZStd::fixed_vector<int, 32> equalVector{ 1,2,3,4,5 };
constexpr AZStd::fixed_vector<int, 32> notEqualVectorDifferentSize{ 1,2,3,4,5,6 };
constexpr AZStd::fixed_vector<int, 32> lessVector{ 1,2,3,4,4 };
constexpr AZStd::fixed_vector<int, 32> greaterVectorDifferentSize{ 1,2,3,4,5, 1 };
static_assert(testVector == equalVector);
static_assert(testVector != notEqualVectorDifferentSize);
static_assert(testVector != lessVector);
static_assert(lessVector < testVector);
static_assert(lessVector < greaterVectorDifferentSize);
static_assert(lessVector <= lessVector);
static_assert(lessVector <= testVector);
static_assert(lessVector <= greaterVectorDifferentSize);
static_assert(testVector > lessVector);
static_assert(testVector > lessVector);
static_assert(notEqualVectorDifferentSize > testVector);
static_assert(testVector >= testVector);
static_assert(testVector >= lessVector);
static_assert(greaterVectorDifferentSize > lessVector);
}
TEST_F(Arrays, VectorSwap)
{
vector<void*> vec1(42, nullptr);
vector<void*> vec2(3, reinterpret_cast<void*>((intptr_t)0xdeadbeef));
vector<void*> vec3(3, reinterpret_cast<void*>((intptr_t)0xcdcdcdcd));
vec1.swap(vec2);
EXPECT_EQ(3, vec1.size());
EXPECT_EQ(reinterpret_cast<void*>((intptr_t)0xdeadbeef), vec1[0]);
EXPECT_EQ(42, vec2.size());
EXPECT_EQ(nullptr, vec2[0]);
vec2.swap(vec3);
EXPECT_EQ(3, vec2.size());
EXPECT_EQ(reinterpret_cast<void*>((intptr_t)0xcdcdcdcd), vec2.back());
EXPECT_EQ(42, vec3.size());
EXPECT_EQ(nullptr, vec3.back());
vec3.swap(vec1);
}
TEST_F(Arrays, Array)
{
// ArrayContainerTest-Begin
array<int, 10> myArr = {
{1, 2, 3, 4}
};
AZ_TEST_ASSERT(myArr.empty() == false);
AZ_TEST_ASSERT(myArr.data() != 0);
AZ_TEST_ASSERT(myArr.size() == 10);
AZ_TEST_ASSERT(myArr.front() == 1);
AZ_TEST_ASSERT(myArr.back() == 0);
AZ_TEST_ASSERT(myArr[1] == 2);
AZ_TEST_ASSERT(myArr.at(2) == 3);
using iteratorType = int;
auto testValue = myArr;
reverse_iterator<iteratorType*> rend = testValue.rend();
reverse_iterator<const iteratorType*> crend1 = testValue.rend();
reverse_iterator<const iteratorType*> crend2 = testValue.crend();
reverse_iterator<iteratorType*> rbegin = testValue.rbegin();
reverse_iterator<const iteratorType*> crbegin1 = testValue.rbegin();
reverse_iterator<const iteratorType*> crbegin2 = testValue.crbegin();
AZ_TEST_ASSERT(rend == crend1);
AZ_TEST_ASSERT(crend1 == crend2);
AZ_TEST_ASSERT(rbegin == crbegin1);
AZ_TEST_ASSERT(crbegin1 == crbegin2);
AZ_TEST_ASSERT(rbegin != rend);
array<int, 10> myArr1 = {
{10, 11, 12, 13}
};
AZ_TEST_ASSERT(myArr != myArr1);
myArr = myArr1;
AZ_TEST_ASSERT(myArr == myArr1);
AZ_TEST_ASSERT(myArr.front() == 10);
AZ_TEST_ASSERT(myArr.back() == 0);
myArr1.fill(33);
AZ_TEST_ASSERT(myArr1.front() == 33);
AZ_TEST_ASSERT(myArr1.back() == 33);
myArr.swap(myArr1);
AZ_TEST_ASSERT(myArr.front() == 33);
AZ_TEST_ASSERT(myArr.back() == 33);
AZ_TEST_ASSERT(myArr1.front() == 10);
AZ_TEST_ASSERT(myArr1.back() == 0);
// ArrayContainerTest-End
}
TEST_F(Arrays, ZeroLengthArray)
{
// ArrayContainerTest-Begin
array<int, 0> myArr;
EXPECT_TRUE(myArr.empty());
EXPECT_EQ(0, myArr.size());
EXPECT_EQ(0, myArr.max_size());
AZ_TEST_START_TRACE_SUPPRESSION;
myArr.front();
myArr.back();
myArr.at(0);
myArr[0];
AZ_TEST_STOP_TRACE_SUPPRESSION(4);
array<int, 0> myArr2;
EXPECT_EQ(myArr, myArr2);
myArr.data();
myArr.fill(33);
myArr.swap(myArr2);
EXPECT_EQ(myArr.begin(), myArr.end());
EXPECT_EQ(myArr.cbegin(), myArr.cend());
EXPECT_EQ(myArr.rbegin(), myArr.rend());
EXPECT_EQ(myArr.crbegin(), myArr.crend());
// ArrayContainerTest-End
}
TEST_F(Arrays, VectorDeepCopy)
{
struct MyDeepClass
{
MyDeepClass(int value = 10)
: m_moved(false)
, m_data(value)
, m_intVector(10, value + 1)
{}
MyDeepClass(const MyDeepClass& rhs)
: m_moved(rhs.m_moved)
, m_data(rhs.m_data)
, m_intVector(rhs.m_intVector)
{}
MyDeepClass(MyDeepClass&& rhs)
{
m_moved = true;
m_data = rhs.m_data;
m_intVector = AZStd::move(rhs.m_intVector);
}
MyDeepClass& operator=(const MyDeepClass& rhs)
{
m_moved = rhs.m_moved;
m_data = rhs.m_data;
m_intVector = rhs.m_intVector;
return *this;
}
bool m_moved;
int m_data;
vector<int> m_intVector;
};
typedef vector<MyDeepClass> deep_vector_type;
deep_vector_type deep_vec_1;
AZ_TEST_VALIDATE_EMPTY_VECTOR(deep_vec_1);
deep_vector_type deep_vec_2(10);
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 10);
for (size_t i = 0; i < deep_vec_2.size(); ++i)
{
AZ_TEST_ASSERT(deep_vec_2[i].m_moved == false);
}
// reserve some space
deep_vec_2.set_capacity(15);
for (size_t i = 0; i < deep_vec_2.size(); ++i)
{
AZ_TEST_ASSERT(deep_vec_2[i].m_moved == true);
}
// insert at the end
deep_vec_2.insert(deep_vec_2.end(), MyDeepClass(100));
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 11);
AZ_TEST_ASSERT(deep_vec_2.back().m_data == 100);
AZ_TEST_ASSERT(deep_vec_2.back().m_intVector.size() == 10);
// insert with unitialized_copy
deep_vec_2.insert(prev(deep_vec_2.end()), MyDeepClass(200));
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 12);
AZ_TEST_ASSERT(deep_vec_2.back().m_data == 100);
AZ_TEST_ASSERT(deep_vec_2.back().m_intVector.size() == 10);
// insert with uninitilized_copy and move
deep_vec_2.insert(prev(deep_vec_2.end(), 2), MyDeepClass(300));
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 13);
AZ_TEST_ASSERT(deep_vec_2.back().m_data == 100);
AZ_TEST_ASSERT(deep_vec_2.back().m_intVector.size() == 10);
deep_vec_2.erase(deep_vec_2.begin());
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 12);
deep_vec_2.clear();
AZ_TEST_VALIDATE_VECTOR(deep_vec_2, 0);
}
#endif // AZ_UNIT_TEST_SKIP_STD_VECTOR_AND_ARRAY_TESTS
}