/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2018 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. The code included in this file is provided under the terms of the ISC license http://www.isc.org/downloads/software-support-policy/isc-license. Permission To use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted provided that the above copyright notice and this permission notice appear in all copies. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { #if JUCE_UNIT_TESTS namespace ArrayBaseTestsHelpers { class TriviallyCopyableType { public: TriviallyCopyableType() = default; TriviallyCopyableType (int v) : value (v) {} TriviallyCopyableType (float v) : value ((int) v) {} bool operator== (const TriviallyCopyableType& other) const { return getValue() == other.getValue(); } int getValue() const { return value; } private: int value { -1111 }; }; class NonTriviallyCopyableType { public: NonTriviallyCopyableType() = default; NonTriviallyCopyableType (int v) : value (v) {} NonTriviallyCopyableType (float v) : value ((int) v) {} NonTriviallyCopyableType (const NonTriviallyCopyableType& other) : value (other.value) {} NonTriviallyCopyableType& operator= (const NonTriviallyCopyableType& other) { value = other.value; return *this; } bool operator== (const NonTriviallyCopyableType& other) const { return getValue() == other.getValue(); } int getValue() const { return *ptr; } private: int value { -1111 }; int* ptr = &value; }; } bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct, const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct) { return tct.getValue() == ntct.getValue(); } bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct, const ArrayBaseTestsHelpers::TriviallyCopyableType& tct) { return tct == ntct; } class ArrayBaseTests : public UnitTest { using CopyableType = ArrayBaseTestsHelpers::TriviallyCopyableType; using NoncopyableType = ArrayBaseTestsHelpers::NonTriviallyCopyableType; #if ! (defined(__GNUC__) && __GNUC__ < 5 && ! defined(__clang__)) static_assert (std::is_trivially_copyable::value, "Test TriviallyCopyableType is not trivially copyable"); static_assert (! std::is_trivially_copyable::value, "Test NonTriviallyCopyableType is trivially copyable"); #endif public: ArrayBaseTests() : UnitTest ("ArrayBase", "Containers") {} void runTest() override { beginTest ("grow capacity"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; checkEqual (copyableContainer, noncopyableContainer, referenceContainer); int originalCapacity = 4; referenceContainer.reserve ((size_t) originalCapacity); expectEquals ((int) referenceContainer.capacity(), originalCapacity); copyableContainer.setAllocatedSize (originalCapacity); expectEquals (copyableContainer.capacity(), originalCapacity); noncopyableContainer.setAllocatedSize (originalCapacity); expectEquals (noncopyableContainer.capacity(), originalCapacity); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); addData (referenceContainer, copyableContainer, noncopyableContainer, 33); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); expect ((int) referenceContainer.capacity() != originalCapacity); expect (copyableContainer.capacity() != originalCapacity); expect (noncopyableContainer.capacity() != originalCapacity); } beginTest ("shrink capacity"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; int numElements = 45; addData (referenceContainer, copyableContainer, noncopyableContainer, numElements); copyableContainer.shrinkToNoMoreThan (numElements); noncopyableContainer.setAllocatedSize (numElements + 1); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); referenceContainer.clear(); copyableContainer.removeElements (0, numElements); noncopyableContainer.removeElements (0, numElements); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); copyableContainer.setAllocatedSize (0); noncopyableContainer.setAllocatedSize (0); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); addData (referenceContainer, copyableContainer, noncopyableContainer, numElements); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("equality"); { std::vector referenceContainer = { 1, 2, 3 }; ArrayBase testContainer1, testContainer2; for (auto i : referenceContainer) { testContainer1.add (i); testContainer2.add (i); } expect (testContainer1 == referenceContainer); expect (testContainer2 == testContainer1); testContainer1.ensureAllocatedSize (257); referenceContainer.shrink_to_fit(); expect (testContainer1 == referenceContainer); expect (testContainer2 == testContainer1); testContainer1.removeElements (0, 1); expect (testContainer1 != referenceContainer); expect (testContainer2 != testContainer1); } beginTest ("accessors"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; addData (referenceContainer, copyableContainer, noncopyableContainer, 3); int testValue = -123; referenceContainer[0] = testValue; copyableContainer[0] = testValue; noncopyableContainer[0] = testValue; checkEqual (copyableContainer, noncopyableContainer, referenceContainer); expect (copyableContainer .getFirst().getValue() == testValue); expect (noncopyableContainer.getFirst().getValue() == testValue); auto last = referenceContainer.back().getValue(); expectEquals (copyableContainer .getLast().getValue(), last); expectEquals (noncopyableContainer.getLast().getValue(), last); ArrayBase copyableEmpty; ArrayBase noncopyableEmpty; auto defualtValue = CopyableType().getValue(); expectEquals (defualtValue, NoncopyableType().getValue()); expectEquals (copyableEmpty .getFirst().getValue(), defualtValue); expectEquals (noncopyableEmpty.getFirst().getValue(), defualtValue); expectEquals (copyableEmpty .getLast() .getValue(), defualtValue); expectEquals (noncopyableEmpty.getLast() .getValue(), defualtValue); ArrayBase floatPointers; expect (floatPointers.getValueWithDefault (-3) == nullptr); } beginTest ("add moved"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; for (int i = 0; i < 5; ++i) { CopyableType ref (-i); CopyableType ct (-i); NoncopyableType nct (-i); referenceContainer.push_back (std::move (ref)); copyableContainer.add (std::move (ct)); noncopyableContainer.add (std::move (nct)); } checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("add multiple"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; for (int i = 4; i < 7; ++i) referenceContainer.push_back ({ -i }); copyableContainer.add (CopyableType (-4), CopyableType (-5), CopyableType (-6)); noncopyableContainer.add (NoncopyableType (-4), NoncopyableType (-5), NoncopyableType (-6)); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("add array from a pointer"); { ArrayBase copyableContainer; ArrayBase noncopyableContainer; std::vector copyableData = { 3, 4, 5 }; std::vector noncopyableData = { 3, 4, 5 }; copyableContainer.addArray (copyableData.data(), (int) copyableData.size()); noncopyableContainer.addArray (noncopyableData.data(), (int) noncopyableData.size()); checkEqual (copyableContainer, noncopyableContainer, copyableData); } beginTest ("add array from a pointer of a different type"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; std::vector floatData = { 1.4f, 2.5f, 3.6f }; for (auto f : floatData) referenceContainer.push_back ({ f }); copyableContainer.addArray (floatData.data(), (int) floatData.size()); noncopyableContainer.addArray (floatData.data(), (int) floatData.size()); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("add array from initilizer list"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; std::initializer_list ilct { { 3 }, { 4 }, { 5 } }; std::initializer_list ilnct { { 3 }, { 4 }, { 5 } }; for (auto v : ilct) referenceContainer.push_back ({ v }); copyableContainer.addArray (ilct); noncopyableContainer.addArray (ilnct); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("add array from containers"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; addData (referenceContainer, copyableContainer, noncopyableContainer, 5); std::vector referenceContainerCopy (referenceContainer); std::vector noncopyableReferenceContainerCopy; ArrayBase copyableContainerCopy; ArrayBase noncopyableContainerCopy; for (auto& v : referenceContainerCopy) noncopyableReferenceContainerCopy.push_back ({ v.getValue() }); for (size_t i = 0; i < referenceContainerCopy.size(); ++i) { auto value = referenceContainerCopy[i].getValue(); copyableContainerCopy.add ({ value }); noncopyableContainerCopy.add ({ value }); } // From self-types copyableContainer.addArray (copyableContainerCopy); noncopyableContainer.addArray (noncopyableContainerCopy); for (auto v : referenceContainerCopy) referenceContainer.push_back (v); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); // From std containers copyableContainer.addArray (referenceContainerCopy); noncopyableContainer.addArray (noncopyableReferenceContainerCopy); for (auto v : referenceContainerCopy) referenceContainer.push_back (v); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); // From std containers with offset int offset = 5; copyableContainer.addArray (referenceContainerCopy, offset); noncopyableContainer.addArray (noncopyableReferenceContainerCopy, offset); for (size_t i = 5; i < referenceContainerCopy.size(); ++i) referenceContainer.push_back (referenceContainerCopy[i]); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("insert"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; addData (referenceContainer, copyableContainer, noncopyableContainer, 8); referenceContainer.insert (referenceContainer.begin(), -4); copyableContainer.insert (0, -4, 1); noncopyableContainer.insert (0, -4, 1); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); for (int i = 0; i < 3; ++i) referenceContainer.insert (referenceContainer.begin() + 1, -3); copyableContainer.insert (1, -3, 3); noncopyableContainer.insert (1, -3, 3); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); for (int i = 0; i < 50; ++i) referenceContainer.insert (referenceContainer.end() - 1, -9); copyableContainer.insert (copyableContainer.size() - 2, -9, 50); noncopyableContainer.insert (noncopyableContainer.size() - 2, -9, 50); } beginTest ("insert array"); { ArrayBase copyableContainer; ArrayBase noncopyableContainer; std::vector copyableData = { 3, 4, 5, 6, 7, 8 }; std::vector noncopyableData = { 3, 4, 5, 6, 7, 8 }; std::vector referenceContainer { copyableData }; copyableContainer.insertArray (0, copyableData.data(), (int) copyableData.size()); noncopyableContainer.insertArray (0, noncopyableData.data(), (int) noncopyableData.size()); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); int insertPos = copyableContainer.size() - 1; for (auto it = copyableData.end(); it != copyableData.begin(); --it) referenceContainer.insert (referenceContainer.begin() + insertPos, CopyableType (*(it - 1))); copyableContainer.insertArray (insertPos, copyableData.data(), (int) copyableData.size()); noncopyableContainer.insertArray (insertPos, noncopyableData.data(), (int) noncopyableData.size()); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("remove"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; addData (referenceContainer, copyableContainer, noncopyableContainer, 17); for (int i = 0; i < 4; ++i) { referenceContainer.erase (referenceContainer.begin() + i); copyableContainer.removeElements (i, 1); noncopyableContainer.removeElements (i, 1); } checkEqual (copyableContainer, noncopyableContainer, referenceContainer); addData (referenceContainer, copyableContainer, noncopyableContainer, 17); int blockSize = 3; for (int i = 0; i < 4; ++i) { for (int j = 0; j < blockSize; ++j) referenceContainer.erase (referenceContainer.begin() + i); copyableContainer.removeElements (i, blockSize); noncopyableContainer.removeElements (i, blockSize); } checkEqual (copyableContainer, noncopyableContainer, referenceContainer); auto numToRemove = copyableContainer.size() - 2; for (int i = 0; i < numToRemove; ++i) referenceContainer.erase (referenceContainer.begin() + 1); copyableContainer.removeElements (1, numToRemove); noncopyableContainer.removeElements (1, numToRemove); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } beginTest ("move"); { std::vector referenceContainer; ArrayBase copyableContainer; ArrayBase noncopyableContainer; addData (referenceContainer, copyableContainer, noncopyableContainer, 6); std::vector> testValues; testValues.emplace_back (2, 4); testValues.emplace_back (0, 5); testValues.emplace_back (4, 1); testValues.emplace_back (5, 0); for (auto p : testValues) { if (p.second > p.first) std::rotate (referenceContainer.begin() + p.first, referenceContainer.begin() + p.first + 1, referenceContainer.begin() + p.second + 1); else std::rotate (referenceContainer.begin() + p.second, referenceContainer.begin() + p.first, referenceContainer.begin() + p.first + 1); copyableContainer.move (p.first, p.second); noncopyableContainer.move (p.first, p.second); checkEqual (copyableContainer, noncopyableContainer, referenceContainer); } } beginTest ("After converting move construction, ownership is transferred"); { Derived obj; ArrayBase derived; derived.setAllocatedSize (5); derived.add (&obj); ArrayBase base { std::move (derived) }; expectEquals (base.capacity(), 5); expectEquals (base.size(), 1); expect (base.getFirst() == &obj); expectEquals (derived.capacity(), 0); expectEquals (derived.size(), 0); expect (derived.data() == nullptr); } beginTest ("After converting move assignment, ownership is transferred"); { Derived obj; ArrayBase derived; derived.setAllocatedSize (5); derived.add (&obj); ArrayBase base; base = std::move (derived); expectEquals (base.capacity(), 5); expectEquals (base.size(), 1); expect (base.getFirst() == &obj); expectEquals (derived.capacity(), 0); expectEquals (derived.size(), 0); expect (derived.data() == nullptr); } } private: struct Base { virtual ~Base() = default; }; struct Derived : Base { }; static void addData (std::vector& referenceContainer, ArrayBase& copyableContainer, ArrayBase& NoncopyableContainer, int numValues) { for (int i = 0; i < numValues; ++i) { referenceContainer.push_back ({ i }); copyableContainer.add ({ i }); NoncopyableContainer.add ({ i }); } } template void checkEqual (const ArrayBase& a, const ArrayBase& b) { expectEquals ((int) a.size(), (int) b.size()); for (int i = 0; i < (int) a.size(); ++i) expect (a[i] == b[i]); } template void checkEqual (ArrayBase& a, std::vector& b) { expectEquals ((int) a.size(), (int) b.size()); for (int i = 0; i < (int) a.size(); ++i) expect (a[i] == b[(size_t) i]); } template void checkEqual (ArrayBase& a, ArrayBase& b, std::vector& c) { checkEqual (a, b); checkEqual (a, c); checkEqual (b, c); } }; static ArrayBaseTests arrayBaseTests; #endif } // namespace juce