/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include namespace UnitTest { using namespace AZStd; class ArrayView : public AllocatorsTestFixture { protected: template void ExpectEqual(initializer_list expectedValues, array_view arrayView) { EXPECT_EQ(false, arrayView.empty()); EXPECT_EQ(expectedValues.size(), arrayView.size()); typename AZStd::vector::const_iterator iterator = arrayView.begin(); for (int i = 0; i < expectedValues.size(); ++i, ++iterator) { EXPECT_EQ(expectedValues.begin()[i], arrayView[i]); EXPECT_EQ(expectedValues.begin()[i], *iterator); } EXPECT_EQ(iterator, arrayView.end()); } }; TEST_F(ArrayView, DefaultConstructor) { array_view defaultView; EXPECT_EQ(nullptr, defaultView.begin()); EXPECT_EQ(nullptr, defaultView.end()); EXPECT_EQ(0, defaultView.size()); EXPECT_EQ(true, defaultView.empty()); } TEST_F(ArrayView, PointerConstructor1) { int originalValues[4] = { 2,3,4,5 }; array_view view(originalValues, AZ_ARRAY_SIZE(originalValues)); ExpectEqual({ 2,3,4,5 }, view); EXPECT_EQ(originalValues, view.begin()); EXPECT_EQ(&originalValues[4], view.end()); } TEST_F(ArrayView, PointerConstructor2) { int originalValues[3] = { 6,7,8 }; array_view view(originalValues, &originalValues[3]); ExpectEqual({ 6,7,8 }, view); EXPECT_EQ(originalValues, view.begin()); EXPECT_EQ(&originalValues[3], view.end()); } TEST_F(ArrayView, ArrayConstructor) { array originalValues = { 9,10,11,12 }; array_view view(originalValues); ExpectEqual({ 9,10,11,12 }, view); EXPECT_EQ(originalValues.begin(), view.begin()); EXPECT_EQ(originalValues.end(), view.end()); } TEST_F(ArrayView, VectorConstructor) { vector originalValues = { 13,14,15,16,17,18 }; array_view view(originalValues); ExpectEqual({ 13,14,15,16,17,18 }, view); EXPECT_EQ(originalValues.begin(), view.begin()); EXPECT_EQ(originalValues.end(), view.end()); } TEST_F(ArrayView, FixedVectorConstructor) { fixed_vector originalValues = { 17,18,19 }; // Note that even though the fixed_vector capacity is 10, it's size is 3, so the view size will be 3 as well array_view view(originalValues); ExpectEqual({ 17,18,19 }, view); EXPECT_EQ(originalValues.begin(), view.begin()); EXPECT_EQ(originalValues.end(), view.end()); } TEST_F(ArrayView, CopyConstructor) { fixed_vector originalValues = { 27,28 }; array_view view1(originalValues); array_view view2(view1); ExpectEqual({ 27,28 }, view2); EXPECT_EQ(view1.begin(), view2.begin()); EXPECT_EQ(view1.end(), view2.end()); } TEST_F(ArrayView, MoveConstructor) { int originalValues[] = { 29,30,31 }; array_view view1(originalValues, AZ_ARRAY_SIZE(originalValues)); array_view view2(AZStd::move(view1)); ExpectEqual({ 29,30,31 }, view2); EXPECT_EQ(originalValues, view2.begin()); EXPECT_EQ(&originalValues[3], view2.end()); // This isn't strictly necessary but is a good way to make sure the move // constructor actually exists and it itn't just calling the copy constructor EXPECT_EQ(nullptr, view1.begin()); EXPECT_EQ(nullptr, view1.end()); } TEST_F(ArrayView, AssignmentOperator) { fixed_vector originalValues = { 32,33,34,35 }; array_view view1(originalValues); array_view view2; view2 = view1; ExpectEqual({ 32,33,34,35 }, view2); EXPECT_EQ(view1.begin(), view2.begin()); EXPECT_EQ(view1.end(), view2.end()); } TEST_F(ArrayView, MoveAssignmentOperator) { int originalValues[] = { 36,37,38,39,40 }; array_view view1(originalValues, AZ_ARRAY_SIZE(originalValues)); array_view view2; view2 = AZStd::move(view1); ExpectEqual({ 36,37,38,39,40 }, view2); EXPECT_EQ(originalValues, view2.begin()); EXPECT_EQ(&originalValues[5], view2.end()); // This isn't strictly necessary but is a good way to make sure the move // assignment operator actually exists and it itn't just calling the norm // assignment operator EXPECT_EQ(nullptr, view1.begin()); EXPECT_EQ(nullptr, view1.end()); } TEST_F(ArrayView, Erase) { fixed_vector originalValues = { 1,2,3,4 }; array_view view(originalValues); view.erase(); EXPECT_EQ(nullptr, view.begin()); EXPECT_EQ(nullptr, view.end()); EXPECT_EQ(0, view.size()); EXPECT_EQ(true, view.empty()); } TEST_F(ArrayView, BeginAndEnd) { fixed_vector originalValues = { 1,2,3,4 }; array_view view(originalValues); EXPECT_EQ(1, view.begin()[0]); EXPECT_EQ(4, view.end()[-1]); EXPECT_EQ(1, view.cbegin()[0]); EXPECT_EQ(4, view.cend()[-1]); EXPECT_EQ(4, view.rbegin()[0]); EXPECT_EQ(1, view.rend()[-1]); EXPECT_EQ(4, view.crbegin()[0]); EXPECT_EQ(1, view.crend()[-1]); } TEST_F(ArrayView, ImplicitConstruction) { // This test verifies that we can pass in various non-array_view types // into functions that take an array_view // The compile cannot detect the correct template type so that has to be specified explicitly ExpectEqual({ 1,2,3 }, vector({ 1,2,3 })); ExpectEqual({ 1,2,3 }, fixed_vector({ 1,2,3 })); ExpectEqual({ 1,2,3 }, array({ 1,2,3 })); } void CheckComparisonOperators(bool areEqual, array_view a, array_view b) { EXPECT_EQ(areEqual, a == b); // For less/greater operators, the exact order doesn't really matter; // We just check for internal consistency if (areEqual) { EXPECT_EQ(false, a != b); EXPECT_EQ(false, a < b); EXPECT_EQ(false, a > b); EXPECT_EQ(true, a <= b); EXPECT_EQ(true, a >= b); } else { EXPECT_EQ(true, a != b); EXPECT_EQ(a > b, a >= b); EXPECT_EQ(a < b, a <= b); EXPECT_NE(a > b, a < b); EXPECT_NE(a >= b, a <= b); EXPECT_NE(a >= b, a < b); EXPECT_NE(a > b, a <= b); EXPECT_NE(a <= b, a > b); EXPECT_NE(a < b, a >= b); } } TEST_F(ArrayView, ComparisonOperators) { int arrayA[] = { 1,2,3 }; int arrayB[] = { 1,2,3 }; array_view arrayA_view(arrayA, 3); array_view arrayB_view(arrayB, 3); array_view arrayA_otherView(arrayA, 3); // view of a sub-array aligned to the beginning of the array array_view arrayA_headView(arrayA, 2); array_view arrayB_headView(arrayB, 2); // view of a sub-array aligned to the end of the array array_view arrayA_tailView(&arrayA[1], 2); array_view arrayB_tailView(&arrayB[1], 2); // view of a sub-array in the middle of the array array_view arrayA_centerView(&arrayA[1], 1); array_view arrayB_centerView(&arrayB[1], 1); // Same view CheckComparisonOperators(true, arrayA_view, arrayA_view); // Different view, same array CheckComparisonOperators(true, arrayA_view, arrayA_otherView); CheckComparisonOperators(true, arrayA_otherView, arrayA_view); // Different arrays CheckComparisonOperators(false, arrayA_view, arrayB_view); CheckComparisonOperators(false, arrayB_view, arrayA_view); // Same arrays, but one is a just a subset of the array CheckComparisonOperators(false, arrayA_view, arrayA_headView); CheckComparisonOperators(false, arrayA_view, arrayA_tailView); CheckComparisonOperators(false, arrayA_view, arrayA_centerView); CheckComparisonOperators(false, arrayA_headView, arrayA_view); CheckComparisonOperators(false, arrayA_tailView, arrayA_view); CheckComparisonOperators(false, arrayA_centerView, arrayA_view); // Different arrays, different lengths CheckComparisonOperators(false, arrayA_view, arrayB_headView); CheckComparisonOperators(false, arrayB_view, arrayA_headView); CheckComparisonOperators(false, arrayB_headView, arrayA_view); CheckComparisonOperators(false, arrayA_headView, arrayB_view); } TEST_F(ArrayView, AssertOutOfBounds) { array_view view({ 1,2,3,4 }); UnitTest::TestRunner::Instance().StartAssertTests(); EXPECT_EQ(0, UnitTest::TestRunner::Instance().m_numAssertsFailed); view[4]; EXPECT_EQ(1, UnitTest::TestRunner::Instance().m_numAssertsFailed); view[5]; EXPECT_EQ(2, UnitTest::TestRunner::Instance().m_numAssertsFailed); UnitTest::TestRunner::Instance().StopAssertTests(); } }