summaryrefslogtreecommitdiffstats
path: root/UnitTests
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2019-12-05 04:19:44 +0000
committerEgor Yusov <egor.yusov@gmail.com>2019-12-05 04:19:44 +0000
commit30a1be62b64b07d3e48eb35e95294859c42ae41f (patch)
tree8550cd0a698469a388075ffaa814d43ab865b538 /UnitTests
parentappveyor: fixed run test instruction (diff)
downloadDiligentCore-30a1be62b64b07d3e48eb35e95294859c42ae41f.tar.gz
DiligentCore-30a1be62b64b07d3e48eb35e95294859c42ae41f.zip
Added Allocator test, ring buffer test and RefCntAutoPtr test
Diffstat (limited to 'UnitTests')
-rw-r--r--UnitTests/CMakeLists.txt8
-rw-r--r--UnitTests/src/Common/AllocatorTest.cpp82
-rw-r--r--UnitTests/src/Common/MathLibTest.cpp81
-rw-r--r--UnitTests/src/Common/RefCntAutoPtrTest.cpp909
-rw-r--r--UnitTests/src/GraphicsAccessories/RingBufferTest.cpp291
5 files changed, 1328 insertions, 43 deletions
diff --git a/UnitTests/CMakeLists.txt b/UnitTests/CMakeLists.txt
index 6793bd63..ed203451 100644
--- a/UnitTests/CMakeLists.txt
+++ b/UnitTests/CMakeLists.txt
@@ -5,8 +5,11 @@ project(DiligentCoreTest)
file(GLOB COMMON_SOURCE src/Common/*)
file(GLOB COMMON_INCLUDE include/Common/*)
-set(SOURCE ${COMMON_SOURCE})
-set(INCLUDE ${COMMON_INCLUDE})
+file(GLOB GRAPHICS_ACCESSORIES_SOURCE src/GraphicsAccessories/*)
+file(GLOB GRAPHICS_ACCESSORIES_INCLUDE include/GraphicsAccessories/*)
+
+set(SOURCE ${COMMON_SOURCE} ${GRAPHICS_ACCESSORIES_SOURCE})
+set(INCLUDE ${COMMON_INCLUDE} ${GRAPHICS_ACCESSORIES_INCLUDE})
add_executable(DiligentCoreTest ${SOURCE} ${INCLUDE})
set_common_target_properties(DiligentCoreTest)
@@ -16,6 +19,7 @@ PRIVATE
gtest_main
Diligent-BuildSettings
Diligent-TargetPlatform
+ Diligent-GraphicsAccessories
Diligent-Common
)
diff --git a/UnitTests/src/Common/AllocatorTest.cpp b/UnitTests/src/Common/AllocatorTest.cpp
new file mode 100644
index 00000000..8ab37914
--- /dev/null
+++ b/UnitTests/src/Common/AllocatorTest.cpp
@@ -0,0 +1,82 @@
+/* Copyright 2019 Diligent Graphics LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+// EngineSandbox.cpp : Defines the entry point for the application.
+//
+
+#include "DefaultRawMemoryAllocator.h"
+#include "FixedBlockMemoryAllocator.h"
+
+#include "gtest/gtest.h"
+
+using namespace Diligent;
+
+namespace
+{
+
+TEST(Common_FixedBlockMemoryAllocator, AllocDealloc)
+{
+ constexpr Uint32 AllocSize = 32;
+ constexpr Uint32 NumAllocationsPerPage = 16;
+
+ FixedBlockMemoryAllocator TestAllocator(DefaultRawMemoryAllocator::GetAllocator(), AllocSize, NumAllocationsPerPage);
+
+ void* Allocations[NumAllocationsPerPage][2] = {};
+ for (int p = 0; p < 2; ++p)
+ {
+ for (int a = 1; a < NumAllocationsPerPage; ++a)
+ {
+ for (int i = 0; i < a; ++i)
+ Allocations[i][p] = TestAllocator.Allocate(AllocSize, "Fixed block allocator test", __FILE__, __LINE__);
+
+ for (int i = a - 1; i >= 0; --i)
+ TestAllocator.Free(Allocations[i][p]);
+
+ for (int i = 0; i < a; ++i)
+ {
+ auto* NewAlloc = TestAllocator.Allocate(AllocSize, "Fixed block allocator test", __FILE__, __LINE__);
+ EXPECT_EQ(Allocations[i][p], NewAlloc);
+ }
+
+ for (int i = a - 1; i >= 0; --i)
+ TestAllocator.Free(Allocations[i][p]);
+ }
+ for (int i = 0; i < NumAllocationsPerPage; ++i)
+ Allocations[i][p] = TestAllocator.Allocate(AllocSize, "Fixed block allocator test", __FILE__, __LINE__);
+ }
+
+ for (int p = 0; p < 2; ++p)
+ for (int i = 0; i < NumAllocationsPerPage; ++i)
+ TestAllocator.Free(Allocations[i][p]);
+
+ for (int p = 0; p < 2; ++p)
+ for (int i = 0; i < NumAllocationsPerPage; ++i)
+ Allocations[i][p] = TestAllocator.Allocate(AllocSize, "Fixed block allocator test", __FILE__, __LINE__);
+
+ for (int p = 0; p < 2; ++p)
+ for (int s = 0; s < 5; ++s)
+ for (int i = s; i < NumAllocationsPerPage; i += 5)
+ TestAllocator.Free(Allocations[i][p]);
+}
+
+} // namespace
diff --git a/UnitTests/src/Common/MathLibTest.cpp b/UnitTests/src/Common/MathLibTest.cpp
index 9a80d30a..fcef9345 100644
--- a/UnitTests/src/Common/MathLibTest.cpp
+++ b/UnitTests/src/Common/MathLibTest.cpp
@@ -35,7 +35,7 @@ namespace
{
// Constructors
-TEST(MathLibTest, VectorConstructors)
+TEST(Common_BasicMath, VectorConstructors)
{
{
float2 f2{1, 2};
@@ -75,7 +75,7 @@ TEST(MathLibTest, VectorConstructors)
}
// a - b
-TEST(MathLibTest, OpeartorMinus)
+TEST(Common_BasicMath, OpeartorMinus)
{
{
auto v = float2{5, 3} - float2{1, 2};
@@ -100,7 +100,7 @@ TEST(MathLibTest, OpeartorMinus)
}
// a -= b
-TEST(MathLibTest, OpeartorMinusEqual)
+TEST(Common_BasicMath, OpeartorMinusEqual)
{
{
auto v = float2{5, 3};
@@ -128,7 +128,7 @@ TEST(MathLibTest, OpeartorMinusEqual)
}
// -a
-TEST(MathLibTest, UnaryMinus)
+TEST(Common_BasicMath, UnaryMinus)
{
{
auto v = -float2{1, 2};
@@ -153,7 +153,7 @@ TEST(MathLibTest, UnaryMinus)
}
// a + b
-TEST(MathLibTest, OperatorPlus)
+TEST(Common_BasicMath, OperatorPlus)
{
// a + b
{
@@ -179,7 +179,7 @@ TEST(MathLibTest, OperatorPlus)
}
// a += b
-TEST(MathLibTest, OpeartorPlusEqual)
+TEST(Common_BasicMath, OpeartorPlusEqual)
{
{
auto v = float2{5, 3};
@@ -207,7 +207,7 @@ TEST(MathLibTest, OpeartorPlusEqual)
}
// a * b
-TEST(MathLibTest, VectorVectorMultiply)
+TEST(Common_BasicMath, VectorVectorMultiply)
{
{
auto v = float2{5, 3} * float2{1, 2};
@@ -232,7 +232,7 @@ TEST(MathLibTest, VectorVectorMultiply)
}
// a *= b
-TEST(MathLibTest, VectorVectorMultiplyEqual)
+TEST(Common_BasicMath, VectorVectorMultiplyEqual)
{
{
auto v = float2{5, 3};
@@ -261,7 +261,7 @@ TEST(MathLibTest, VectorVectorMultiplyEqual)
// a * s
-TEST(MathLibTest, VectorScalarMultiply)
+TEST(Common_BasicMath, VectorScalarMultiply)
{
{
auto v = float2{5, 3} * 2;
@@ -286,7 +286,7 @@ TEST(MathLibTest, VectorScalarMultiply)
}
// a *= s
-TEST(MathLibTest, VectorScalarMultiplyEqual)
+TEST(Common_BasicMath, VectorScalarMultiplyEqual)
{
{
auto v = float2{5, 3};
@@ -314,7 +314,7 @@ TEST(MathLibTest, VectorScalarMultiplyEqual)
}
// s * a
-TEST(MathLibTest, ScalarVectorMultiply)
+TEST(Common_BasicMath, ScalarVectorMultiply)
{
{
auto v = 2.f * float2{5, 3};
@@ -339,7 +339,7 @@ TEST(MathLibTest, ScalarVectorMultiply)
}
// a / s
-TEST(MathLibTest, VectorScalarDivision)
+TEST(Common_BasicMath, VectorScalarDivision)
{
{
auto v = float2{10, 6} / 2;
@@ -364,7 +364,7 @@ TEST(MathLibTest, VectorScalarDivision)
}
// a / b
-TEST(MathLibTest, VectorVectorDivision)
+TEST(Common_BasicMath, VectorVectorDivision)
{
{
auto v = float2{6, 4} / float2{1, 2};
@@ -389,7 +389,7 @@ TEST(MathLibTest, VectorVectorDivision)
}
// a /= b
-TEST(MathLibTest, VectorVectorDivideEqual)
+TEST(Common_BasicMath, VectorVectorDivideEqual)
{
{
auto v = float2{6, 4};
@@ -418,7 +418,7 @@ TEST(MathLibTest, VectorVectorDivideEqual)
// a /= s
-TEST(MathLibTest, VectorScalarDivideEqual)
+TEST(Common_BasicMath, VectorScalarDivideEqual)
{
{
auto v = float2{6, 4};
@@ -447,7 +447,7 @@ TEST(MathLibTest, VectorScalarDivideEqual)
// max
-TEST(MathLibTest, StdMax)
+TEST(Common_BasicMath, StdMax)
{
{
auto v = std::max(float2{6, 4}, float2{1, 40});
@@ -472,7 +472,7 @@ TEST(MathLibTest, StdMax)
}
// min
-TEST(MathLibTest, StdMin)
+TEST(Common_BasicMath, StdMin)
{
{
auto v = std::min(float2{6, 4}, float2{1, 40});
@@ -497,7 +497,7 @@ TEST(MathLibTest, StdMin)
}
// a == b
-TEST(MathLibTest, ComparisonOperators)
+TEST(Common_BasicMath, ComparisonOperators)
{
{
EXPECT_TRUE(float2(1, 2) == float2(1, 2));
@@ -574,7 +574,7 @@ TEST(MathLibTest, ComparisonOperators)
}
// Functions
-TEST(MathLibTest, Abs)
+TEST(Common_BasicMath, Abs)
{
{
// clang-format off
@@ -625,7 +625,7 @@ TEST(MathLibTest, Abs)
}
-TEST(MathLibTest, MatrixConstructors)
+TEST(Common_BasicMath, MatrixConstructors)
{
// Matrix 2x2
{
@@ -716,7 +716,7 @@ TEST(MathLibTest, MatrixConstructors)
}
}
-TEST(MathLibTest, MatrixInverse)
+TEST(Common_BasicMath, MatrixInverse)
{
{
// clang-format off
@@ -760,7 +760,7 @@ TEST(MathLibTest, MatrixInverse)
}
-TEST(MathLibTest, Hash)
+TEST(Common_BasicMath, Hash)
{
{
EXPECT_NE(std::hash<float2>{}(float2{1, 2}), 0);
@@ -798,7 +798,7 @@ TEST(MathLibTest, Hash)
}
}
-TEST(MathLibTest, OrthoProjection)
+TEST(Common_BasicMath, OrthoProjection)
{
{
float4x4 OrthoProj = float4x4::Ortho(2.f, 4.f, -4.f, 12.f, false);
@@ -837,20 +837,7 @@ TEST(MathLibTest, OrthoProjection)
}
}
-TEST(MathLibTest, Planes)
-{
- Plane3D plane = {};
- EXPECT_NE(std::hash<Plane3D>{}(plane), 0);
-
- ViewFrustum frustum = {};
- EXPECT_NE(std::hash<ViewFrustum>{}(frustum), 0);
-
- ViewFrustumExt frustm_ext = {};
- EXPECT_NE(std::hash<ViewFrustumExt>{}(frustm_ext), 0);
-}
-
-
-TEST(MathLibTest, MakeObject)
+TEST(Common_BasicMath, MakeObject)
{
double data[] = {1, 2, 3, 4,
5, 6, 7, 8,
@@ -865,7 +852,7 @@ TEST(MathLibTest, MakeObject)
EXPECT_EQ(float2x2::MakeMatrix(data), float2x2(1, 2, 3, 4));
}
-TEST(MathLibTest, MatrixMultiply)
+TEST(Common_BasicMath, MatrixMultiply)
{
{
float2x2 m1(1, 2, 3, 4);
@@ -903,7 +890,7 @@ TEST(MathLibTest, MatrixMultiply)
}
}
-TEST(MathLibTest, VectorRecast)
+TEST(Common_BasicMath, VectorRecast)
{
{
EXPECT_EQ(float2(1, 2).Recast<int>(), Vector2<int>(1, 2));
@@ -912,7 +899,7 @@ TEST(MathLibTest, VectorRecast)
}
}
-TEST(MathLibTest, StdFloorCeil)
+TEST(Common_BasicMath, StdFloorCeil)
{
{
EXPECT_EQ(std::floor(float2(0.1f, 1.2f)), float2(0, 1));
@@ -924,7 +911,19 @@ TEST(MathLibTest, StdFloorCeil)
}
}
-TEST(MathLibTest, HermiteSpline)
+TEST(Common_AdvancedMath, Planes)
+{
+ Plane3D plane = {};
+ EXPECT_NE(std::hash<Plane3D>{}(plane), 0);
+
+ ViewFrustum frustum = {};
+ EXPECT_NE(std::hash<ViewFrustum>{}(frustum), 0);
+
+ ViewFrustumExt frustm_ext = {};
+ EXPECT_NE(std::hash<ViewFrustumExt>{}(frustm_ext), 0);
+}
+
+TEST(Common_AdvancedMath, HermiteSpline)
{
EXPECT_NE(HermiteSpline(float3(1, 2, 3), float3(4, 5, 6), float3(7, 8, 9), float3(10, 11, 12), 0.1f), float3(0, 0, 0));
EXPECT_NE(HermiteSpline(double3(1, 2, 3), double3(4, 5, 6), double3(7, 8, 9), double3(10, 11, 12), 0.1), double3(0, 0, 0));
diff --git a/UnitTests/src/Common/RefCntAutoPtrTest.cpp b/UnitTests/src/Common/RefCntAutoPtrTest.cpp
new file mode 100644
index 00000000..46800e69
--- /dev/null
+++ b/UnitTests/src/Common/RefCntAutoPtrTest.cpp
@@ -0,0 +1,909 @@
+/* Copyright 2019 Diligent Graphics LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+// EngineSandbox.cpp : Defines the entry point for the application.
+//
+
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <condition_variable>
+#include <algorithm>
+
+#include "DefaultRawMemoryAllocator.h"
+#include "RefCntAutoPtr.h"
+#include "RefCountedObjectImpl.h"
+#include "ThreadSignal.h"
+
+#include "gtest/gtest.h"
+
+using namespace Diligent;
+
+template <typename Type>
+Type* MakeNewObj()
+{
+ return MakeNewRCObj<Type>{}();
+}
+
+namespace
+{
+
+class Object : public Diligent::RefCountedObject<Diligent::IObject>
+{
+public:
+ static void Create(Object** ppObj)
+ {
+ *ppObj = MakeNewObj<Object>();
+ (*ppObj)->AddRef();
+ }
+
+ virtual void QueryInterface(const Diligent::INTERFACE_ID& IID, Diligent::IObject** ppInterface)
+ {
+ *ppInterface = nullptr;
+ if (IID == Diligent::IID_Unknown)
+ {
+ *ppInterface = this;
+ (*ppInterface)->AddRef();
+ }
+ }
+
+ Object(Diligent::IReferenceCounters* pRefCounters) :
+ RefCountedObject<Diligent::IObject>{pRefCounters},
+ m_Value(0)
+ {
+ }
+
+ ~Object() {}
+ std::atomic_int m_Value;
+};
+
+
+class DerivedObject : public Object
+{
+public:
+ DerivedObject(Diligent::IReferenceCounters* pRefCounters) :
+ Object{pRefCounters},
+ m_Value2{1}
+ {}
+ int m_Value2;
+};
+
+using SmartPtr = Diligent::RefCntAutoPtr<Object>;
+using WeakPtr = Diligent::RefCntWeakPtr<Object>;
+
+TEST(Common_RefCntAutoPtr, Constructors)
+{
+ {
+ SmartPtr SP0;
+ SmartPtr SP1(nullptr);
+ auto* pRawPtr = MakeNewObj<Object>();
+ SmartPtr SP2(pRawPtr);
+ SmartPtr SP2_1(pRawPtr);
+ EXPECT_EQ(SP2, SP2_1);
+
+ SmartPtr SP3(SP0);
+ SmartPtr SP4(SP2);
+ SmartPtr SP5(std::move(SP3));
+ EXPECT_TRUE(!SP3);
+ SmartPtr SP6(std::move(SP4));
+ EXPECT_TRUE(!SP4);
+
+ RefCntAutoPtr<DerivedObject> DerivedSP(MakeNewObj<DerivedObject>());
+
+ SmartPtr SP7(DerivedSP);
+ SmartPtr SP8(std::move(DerivedSP));
+ EXPECT_EQ(SP7, SP8);
+ EXPECT_TRUE(!DerivedSP);
+ }
+}
+
+TEST(Common_RefCntAutoPtr, AttachDetach)
+{
+ {
+ auto* pRawPtr = MakeNewObj<Object>();
+
+ SmartPtr SP0;
+ SP0.Attach(nullptr);
+ EXPECT_TRUE(!SP0);
+ SP0.Attach(pRawPtr);
+ EXPECT_TRUE(SP0);
+ pRawPtr->AddRef();
+ }
+
+ {
+ auto* pRawPtr = MakeNewObj<Object>();
+
+ SmartPtr SP0;
+ pRawPtr->AddRef();
+ SP0.Attach(pRawPtr);
+ EXPECT_TRUE(SP0);
+ SP0.Attach(nullptr);
+ EXPECT_TRUE(!SP0);
+ }
+
+ {
+ auto* pRawPtr = MakeNewObj<Object>();
+
+ SmartPtr SP0(MakeNewObj<Object>());
+ SP0.Attach(pRawPtr);
+ EXPECT_TRUE(SP0);
+ pRawPtr->AddRef();
+ }
+
+ {
+ SmartPtr SP0(MakeNewObj<Object>());
+ EXPECT_TRUE(SP0);
+
+ auto* pRawPtr = MakeNewObj<Object>();
+ pRawPtr->AddRef();
+ SP0.Attach(pRawPtr);
+ auto* pRawPtr2 = SP0.Detach();
+ pRawPtr2->Release();
+
+ auto* pRawPtr3 = SmartPtr().Detach();
+ EXPECT_TRUE(pRawPtr3 == nullptr);
+ auto* pRawPtr4 = SmartPtr(MakeNewObj<Object>()).Detach();
+ EXPECT_TRUE(pRawPtr4 != nullptr);
+ pRawPtr4->Release();
+ }
+}
+
+TEST(Common_RefCntAutoPtr, OperatorEqual)
+{
+ {
+ SmartPtr SP0;
+ auto pRawPtr1 = MakeNewObj<Object>();
+ SmartPtr SP1(pRawPtr1);
+ SmartPtr SP2(pRawPtr1);
+ SP0 = SP0;
+ SP0 = std::move(SP0);
+ SP0 = nullptr;
+ EXPECT_EQ(SP0, nullptr);
+
+ SP1 = pRawPtr1;
+ SP1 = SP1;
+ SP1 = std::move(SP1);
+ EXPECT_EQ(SP1, pRawPtr1);
+
+ SP1 = SP2;
+ SP1 = std::move(SP2);
+ EXPECT_EQ(SP1, pRawPtr1);
+
+ auto pRawPtr2 = MakeNewObj<Object>();
+ SmartPtr SP3(pRawPtr2);
+
+ SP0 = pRawPtr2;
+ SmartPtr SP4;
+ SP4 = SP3;
+ SmartPtr SP5;
+ SP5 = std::move(SP4);
+ EXPECT_TRUE(!SP4);
+
+ SP1 = pRawPtr2;
+ SP1 = nullptr;
+ SP1 = std::move(SP5);
+ EXPECT_TRUE(!SP5);
+
+ RefCntAutoPtr<DerivedObject> DerivedSP(MakeNewObj<DerivedObject>());
+ SP1 = DerivedSP;
+ SP2 = std::move(DerivedSP);
+ EXPECT_TRUE(!DerivedSP);
+ }
+}
+
+TEST(Common_RefCntAutoPtr, LogicalOperators)
+{
+ {
+ auto pRawPtr1 = MakeNewObj<Object>();
+ auto pRawPtr2 = MakeNewObj<Object>();
+ SmartPtr SP0, SP1(pRawPtr1), SP2(pRawPtr1), SP3(pRawPtr2);
+ EXPECT_TRUE(!SP0);
+ bool b1 = SP0.operator bool();
+ EXPECT_TRUE(!b1);
+
+
+ EXPECT_TRUE(!(!SP1));
+ EXPECT_TRUE(SP1);
+ EXPECT_TRUE(SP0 != SP1);
+ EXPECT_TRUE(SP0 == SP0);
+ EXPECT_TRUE(SP1 == SP1);
+ EXPECT_TRUE(SP1 == SP2);
+ EXPECT_TRUE(SP1 != SP3);
+ EXPECT_TRUE(SP0 < SP3);
+ EXPECT_TRUE((SP1 < SP3) == (pRawPtr1 < pRawPtr2));
+ }
+}
+
+TEST(Common_RefCntAutoPtr, OperatorAmpersand)
+{
+ {
+ SmartPtr SP0, SP1(MakeNewObj<Object>()), SP2, SP3, SP4(MakeNewObj<Object>());
+ auto* pRawPtr = MakeNewObj<Object>();
+ pRawPtr->AddRef();
+
+ *static_cast<Object**>(&SP0) = pRawPtr;
+ SP0.Detach();
+ *&SP2 = pRawPtr;
+ SP2.Detach();
+
+ Object::Create(&SP3);
+
+ Object::Create(&SP1);
+ *static_cast<Object**>(&SP4) = pRawPtr;
+
+ {
+ SmartPtr SP5(MakeNewObj<Object>());
+ auto pDblPtr = &SP5;
+ *pDblPtr = MakeNewObj<Object>();
+ (*pDblPtr)->AddRef();
+ auto pDblPtr2 = &SP5;
+ Object::Create(pDblPtr2);
+ }
+
+ SmartPtr SP6(MakeNewObj<Object>());
+ // This will not work:
+ // Object **pDblPtr3 = &SP6;
+ // *pDblPtr3 = new Object;
+ }
+}
+
+TEST(Common_RefCntWeakPtr, Constructors)
+{
+ {
+ SmartPtr SP0, SP1(MakeNewObj<Object>());
+ WeakPtr WP0;
+ WeakPtr WP1(WP0);
+ WeakPtr WP2(SP0);
+ WeakPtr WP3(SP1);
+ WeakPtr WP4(WP3);
+ WeakPtr WP5(std::move(WP0));
+ WeakPtr WP6(std::move(WP4));
+
+ auto* pRawPtr = MakeNewObj<Object>();
+ pRawPtr->AddRef();
+ WeakPtr WP7(pRawPtr);
+ pRawPtr->Release();
+ }
+}
+
+
+TEST(Common_RefCntWeakPtr, OperatorEqual)
+{
+ {
+ auto* pRawPtr = MakeNewObj<Object>();
+ SmartPtr SP0, SP1(pRawPtr);
+ WeakPtr WP0, WP1(SP1), WP2(SP1);
+ WP0 = WP0;
+ WP0 = std::move(WP0);
+ WP1 = WP1;
+ WP1 = std::move(WP1);
+ WP1 = WP2;
+ WP1 = std::move(WP2);
+ WP1 = pRawPtr;
+ WP0 = pRawPtr;
+ WP0.Release();
+ WP0 = WP2;
+
+ WP1 = WP0;
+ WP0 = SP1;
+ WP2 = std::move(WP1);
+ }
+}
+
+TEST(Common_RefCntWeakPtr, Lock)
+{
+ {
+ SmartPtr SP0, SP1(MakeNewObj<Object>());
+ WeakPtr WP0, WP1(SP0), WP2(SP1), WP3(SP1);
+ assert(WP0 == WP1);
+ assert(WP0 != WP2);
+ assert(WP2 == WP3);
+ SP1.Release();
+ assert(WP2 == WP3);
+ }
+
+ // Test Lock()
+ {
+ SmartPtr SP0, SP1(MakeNewObj<Object>());
+ WeakPtr WP0, WP1(SP0), WP2(SP1);
+ WeakPtr WP3(WP2);
+ auto L1 = WP0.Lock();
+ assert(!L1);
+ L1 = WP1.Lock();
+ assert(!L1);
+ L1 = WP2.Lock();
+ assert(L1);
+ L1 = WP3.Lock();
+ assert(L1);
+ auto pRawPtr = SP1.Detach();
+ L1.Release();
+
+ L1 = WP3.Lock();
+ assert(L1);
+ L1.Release();
+
+ pRawPtr->Release();
+
+ L1 = WP3.Lock();
+ assert(!L1);
+ }
+}
+
+TEST(Common_RefCntAutoPtr, Misc)
+{
+ {
+ class OwnerTest : public RefCountedObject<IObject>
+ {
+ public:
+ OwnerTest(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters),
+ Obj(NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", Object, this)())
+ {
+ }
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+ ~OwnerTest()
+ {
+ Obj->~Object();
+ DefaultRawMemoryAllocator::GetAllocator().Free(Obj);
+ }
+
+ private:
+ Object* Obj;
+ };
+
+ OwnerTest* pOwnerObject = MakeNewObj<OwnerTest>();
+ pOwnerObject->AddRef();
+ pOwnerObject->Release();
+ }
+
+ {
+ class SelfRefTest : public RefCountedObject<IObject>
+ {
+ public:
+ SelfRefTest(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters),
+ wpSelf(this)
+ {}
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ RefCntWeakPtr<SelfRefTest> wpSelf;
+ };
+
+ SelfRefTest* pSelfRefTest = MakeNewObj<SelfRefTest>();
+ pSelfRefTest->AddRef();
+ pSelfRefTest->Release();
+ }
+
+ {
+ class ExceptionTest1 : public RefCountedObject<IObject>
+ {
+ public:
+ ExceptionTest1(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters),
+ wpSelf(this)
+ {
+ throw std::runtime_error("test exception");
+ }
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ RefCntWeakPtr<ExceptionTest1> wpSelf;
+ };
+
+ try
+ {
+ auto* pExceptionTest = MakeNewObj<ExceptionTest1>();
+ (void)pExceptionTest;
+ }
+ catch (std::runtime_error&)
+ {
+ }
+ }
+
+ {
+ class ExceptionTest2 : public RefCountedObject<IObject>
+ {
+ public:
+ ExceptionTest2(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters),
+ wpSelf(this)
+ {
+ throw std::runtime_error("test exception");
+ }
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ RefCntWeakPtr<ExceptionTest2> wpSelf;
+ };
+
+ try
+ {
+ auto* pExceptionTest = NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", ExceptionTest2)();
+ (void)pExceptionTest;
+ }
+ catch (std::runtime_error&)
+ {
+ }
+ }
+
+ {
+ class ExceptionTest3 : public RefCountedObject<IObject>
+ {
+ public:
+ ExceptionTest3(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters),
+ m_Member(*this)
+ {
+ }
+
+ class Subclass
+ {
+ public:
+ Subclass(ExceptionTest3& parent) :
+ wpSelf(&parent)
+ {
+ throw std::runtime_error("test exception");
+ }
+
+ private:
+ RefCntWeakPtr<ExceptionTest3> wpSelf;
+ };
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ Subclass m_Member;
+ };
+
+ try
+ {
+ auto* pExceptionTest = NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", ExceptionTest3)();
+ (void)pExceptionTest;
+ }
+ catch (std::runtime_error&)
+ {
+ }
+ }
+
+ {
+ class OwnerObject : public RefCountedObject<IObject>
+ {
+ public:
+ OwnerObject(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters)
+ {}
+
+ void CreateMember()
+ {
+ try
+ {
+ m_pMember = NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", ExceptionTest4, this)(*this);
+ }
+ catch (...)
+ {
+ }
+ }
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ class ExceptionTest4 : public RefCountedObject<IObject>
+ {
+ public:
+ ExceptionTest4(IReferenceCounters* pRefCounters, OwnerObject& owner) :
+ RefCountedObject<IObject>(pRefCounters),
+ m_Member(owner, *this)
+ {
+ }
+
+ class Subclass
+ {
+ public:
+ Subclass(OwnerObject& owner, ExceptionTest4& parent) :
+ wpParent(&parent),
+ wpOwner(&owner)
+ {
+ throw std::runtime_error("test exception");
+ }
+
+ private:
+ RefCntWeakPtr<ExceptionTest4> wpParent;
+ RefCntWeakPtr<OwnerObject> wpOwner;
+ };
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ Subclass m_Member;
+ };
+
+ RefCntAutoPtr<ExceptionTest4> m_pMember;
+ };
+
+ RefCntAutoPtr<OwnerObject> pOwner(NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", OwnerObject)());
+ pOwner->CreateMember();
+ }
+
+
+ {
+ class OwnerObject : public RefCountedObject<IObject>
+ {
+ public:
+ OwnerObject(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters)
+ {
+ m_pMember = NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", ExceptionTest4, this)(*this);
+ }
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ class ExceptionTest4 : public RefCountedObject<IObject>
+ {
+ public:
+ ExceptionTest4(IReferenceCounters* pRefCounters, OwnerObject& owner) :
+ RefCountedObject<IObject>(pRefCounters),
+ m_Member(owner, *this)
+ {
+ }
+
+ class Subclass
+ {
+ public:
+ Subclass(OwnerObject& owner, ExceptionTest4& parent) :
+ wpParent(&parent),
+ wpOwner(&owner)
+ {
+ throw std::runtime_error("test exception");
+ }
+
+ private:
+ RefCntWeakPtr<ExceptionTest4> wpParent;
+ RefCntWeakPtr<OwnerObject> wpOwner;
+ };
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) {}
+
+ private:
+ Subclass m_Member;
+ };
+
+ RefCntAutoPtr<ExceptionTest4> m_pMember;
+ };
+
+ try
+ {
+ RefCntAutoPtr<OwnerObject> pOwner(NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", OwnerObject)());
+ }
+ catch (...)
+ {
+ }
+ }
+
+ {
+ class TestObject : public RefCountedObject<IObject>
+ {
+ public:
+ TestObject(IReferenceCounters* pRefCounters) :
+ RefCountedObject<IObject>(pRefCounters)
+ {
+ }
+
+ virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final {}
+
+ inline virtual Atomics::Long Release() override final
+ {
+ return RefCountedObject<IObject>::Release(
+ [&]() //
+ {
+ ppWeakPtr->Release();
+ } //
+ );
+ }
+ RefCntWeakPtr<TestObject>* ppWeakPtr = nullptr;
+ };
+
+ RefCntAutoPtr<TestObject> pObj(NEW_RC_OBJ(DefaultRawMemoryAllocator::GetAllocator(), "Test object", TestObject)());
+ RefCntWeakPtr<TestObject> pWeakPtr(pObj);
+
+ pObj->ppWeakPtr = &pWeakPtr;
+ pObj.Release();
+ }
+}
+
+class RefCntAutoPtrThreadingTest
+{
+public:
+ ~RefCntAutoPtrThreadingTest();
+
+ void StartConcurrencyTest();
+ void RunConcurrencyTest();
+
+ static void WorkerThreadFunc(RefCntAutoPtrThreadingTest* This, size_t ThreadNum);
+
+ void StartWorkerThreadsAndWait(int SignalIdx);
+ void WaitSiblingWorkerThreads(int SignalIdx);
+
+ std::vector<std::thread> m_Threads;
+
+ Object* m_pSharedObject = nullptr;
+ int m_NumTestsPerformed = 0;
+#ifdef _DEBUG
+ static const int NumThreadInterations = 10000;
+#else
+ static const int NumThreadInterations = 50000;
+#endif
+ ThreadingTools::Signal m_WorkerThreadSignal[2];
+ ThreadingTools::Signal m_MainThreadSignal;
+
+ std::mutex m_NumThreadsCompletedMtx;
+ std::atomic_int m_NumThreadsCompleted[2];
+ std::atomic_int m_NumThreadsReady;
+};
+
+
+RefCntAutoPtrThreadingTest::~RefCntAutoPtrThreadingTest()
+{
+ m_WorkerThreadSignal[0].Trigger(true, -1);
+
+ for (auto& t : m_Threads)
+ t.join();
+
+ LOG_INFO_MESSAGE("Performed ", m_NumTestsPerformed, " concurrency ", (m_NumTestsPerformed > 1 ? "tests" : "test"),
+ " with ", NumThreadInterations, " iterations on ", m_Threads.size(), " threads");
+}
+
+void RefCntAutoPtrThreadingTest::WaitSiblingWorkerThreads(int SignalIdx)
+{
+ auto NumThreads = static_cast<int>(m_Threads.size());
+ if (++m_NumThreadsCompleted[SignalIdx] == NumThreads)
+ {
+ ASSERT_FALSE(m_WorkerThreadSignal[1 - SignalIdx].IsTriggered());
+ m_MainThreadSignal.Trigger();
+ }
+ else
+ {
+ while (m_NumThreadsCompleted[SignalIdx] < NumThreads)
+ std::this_thread::yield();
+ }
+}
+
+void RefCntAutoPtrThreadingTest::StartWorkerThreadsAndWait(int SignalIdx)
+{
+ m_NumThreadsCompleted[SignalIdx] = 0;
+ m_WorkerThreadSignal[SignalIdx].Trigger(true);
+
+ m_MainThreadSignal.Wait(true, 1);
+}
+
+void RefCntAutoPtrThreadingTest::WorkerThreadFunc(RefCntAutoPtrThreadingTest* This, size_t ThreadNum)
+{
+ const int NumThreads = static_cast<int>(This->m_Threads.size());
+ while (true)
+ {
+ for (int i = 0; i < NumThreadInterations; ++i)
+ {
+ // Wait until main() sends data
+ auto SignaledValue = This->m_WorkerThreadSignal[0].Wait(true, NumThreads);
+ if (SignaledValue < 0)
+ {
+ return;
+ }
+
+ {
+ auto* pObject = This->m_pSharedObject;
+ for (int j = 0; j < 100; ++j)
+ {
+ //LOG_INFO_MESSAGE("t",std::this_thread::get_id(), ": AddRef" );
+ pObject->m_Value++;
+ pObject->AddRef();
+ }
+ This->WaitSiblingWorkerThreads(0);
+
+ This->m_WorkerThreadSignal[1].Wait(true, NumThreads);
+ for (int j = 0; j < 100; ++j)
+ {
+ //LOG_INFO_MESSAGE("t",std::this_thread::get_id(), ": Release" );
+ pObject->m_Value--;
+ pObject->Release();
+ }
+ This->WaitSiblingWorkerThreads(1);
+ }
+
+ {
+ This->m_WorkerThreadSignal[0].Wait(true, NumThreads);
+ auto* pObject = This->m_pSharedObject;
+ auto* pRefCounters = pObject->GetReferenceCounters();
+ if (ThreadNum % 3 == 0)
+ {
+ pObject->m_Value++;
+ pObject->AddRef();
+ }
+ else
+ pRefCounters->AddWeakRef();
+ This->WaitSiblingWorkerThreads(0);
+
+ This->m_WorkerThreadSignal[1].Wait(true, NumThreads);
+ if (ThreadNum % 3 == 0)
+ {
+ pObject->m_Value--;
+ pObject->Release();
+ }
+ else
+ pRefCounters->ReleaseWeakRef();
+ This->WaitSiblingWorkerThreads(1);
+ }
+
+ {
+ // Test interferences of ReleaseStrongRef() and GetObject()
+
+ // Goal: catch scenario when GetObject() runs between
+ // AtomicDecrement() and acquiring the lock in ReleaseStrongRef():
+
+ // m_lNumStrongReferences == 1
+ //
+
+ // Scenario I
+ //
+ // Thread 1 | Thread 2 | Thread 3
+ // | |
+ // | |
+ // | |
+ // | 1. Acquire the lock |
+ // | 2. Increment m_lNumStrongReferences |
+ // 1. Decrement m_lNumStrongReferences | 3. Read StrongRefCnt > 1 |
+ // 2. Test RefCount!=0 | 4. Return the reference to object |
+ // 3. DO NOT destroy the object | |
+ // 4. Wait for the lock | |
+
+
+ // Scenario I
+ //
+ // Thread 1 | Thread 2 | Thread 3
+ // | |
+ // | |
+ // 1. Decrement m_lNumStrongReferences | |
+ // | 1. Acquire the lock |
+ // 2. Test RefCount==0 | 2. Increment m_lNumStrongReferences |
+ // 3. Start destroying the object | 3. Read StrongRefCnt == 1 |
+ // 4. Wait for the lock | 4. DO NOT create the object |
+ // | 5. Decrement m_lNumStrongReferences |
+ // | | 1. Acquire the lock
+ // | | 2. Increment m_lNumStrongReferences
+ // | | 3. Read StrongRefCnt == 1
+ // | | 4. DO NOT create the object
+ // | | 5. Decrement m_lNumStrongReferences
+ // 5. Acquire the lock |
+ // 6. DESTROY the object |
+
+ This->m_WorkerThreadSignal[0].Wait(true, NumThreads);
+ auto* pObject = This->m_pSharedObject;
+
+ RefCntWeakPtr<Object> weakPtr(pObject);
+ RefCntAutoPtr<Object> strongPtr, strongPtr2;
+ if (ThreadNum < 2)
+ {
+ strongPtr = pObject;
+ strongPtr->m_Value++;
+ }
+ else
+ weakPtr = WeakPtr(pObject);
+ This->WaitSiblingWorkerThreads(0);
+
+ This->m_WorkerThreadSignal[1].Wait(true, NumThreads);
+ if (ThreadNum == 0)
+ {
+ strongPtr->m_Value--;
+ strongPtr.Release();
+ }
+ else
+ {
+ strongPtr2 = weakPtr.Lock();
+ if (strongPtr2)
+ strongPtr2->m_Value++;
+ weakPtr.Release();
+ }
+ This->WaitSiblingWorkerThreads(1);
+ }
+
+
+ {
+ This->m_WorkerThreadSignal[0].Wait(true, NumThreads);
+ auto* pObject = This->m_pSharedObject;
+
+ RefCntWeakPtr<Object> weakPtr;
+ RefCntAutoPtr<Object> strongPtr;
+ if (ThreadNum % 4 == 0)
+ {
+ strongPtr = pObject;
+ strongPtr->m_Value++;
+ }
+ else
+ weakPtr = WeakPtr(pObject);
+ This->WaitSiblingWorkerThreads(0);
+
+ This->m_WorkerThreadSignal[1].Wait(true, NumThreads);
+ if (ThreadNum % 4 == 0)
+ {
+ strongPtr->m_Value--;
+ strongPtr.Release();
+ }
+ else
+ {
+ auto Ptr = weakPtr.Lock();
+ if (Ptr)
+ Ptr->m_Value++;
+ Ptr.Release();
+ }
+ This->WaitSiblingWorkerThreads(1);
+ }
+ }
+ }
+}
+
+void RefCntAutoPtrThreadingTest::StartConcurrencyTest()
+{
+ auto numCores = std::thread::hardware_concurrency();
+ m_Threads.resize(std::max(numCores, 2u));
+ for (auto& t : m_Threads)
+ t = std::thread(WorkerThreadFunc, this, &t - m_Threads.data());
+}
+
+void RefCntAutoPtrThreadingTest::RunConcurrencyTest()
+{
+ for (int i = 0; i < NumThreadInterations; ++i)
+ {
+ m_pSharedObject = MakeNewObj<Object>();
+
+ StartWorkerThreadsAndWait(0);
+
+ StartWorkerThreadsAndWait(1);
+
+ m_pSharedObject = MakeNewObj<Object>();
+
+ StartWorkerThreadsAndWait(0);
+
+ StartWorkerThreadsAndWait(1);
+
+ m_pSharedObject = MakeNewObj<Object>();
+
+ StartWorkerThreadsAndWait(0);
+
+ StartWorkerThreadsAndWait(1);
+
+ m_pSharedObject = MakeNewObj<Object>();
+
+ StartWorkerThreadsAndWait(0);
+
+ StartWorkerThreadsAndWait(1);
+ }
+ ++m_NumTestsPerformed;
+}
+
+TEST(Common_RefCntAutoPtr, Threading)
+{
+ RefCntAutoPtrThreadingTest ThreadingTest;
+ ThreadingTest.StartConcurrencyTest();
+ ThreadingTest.RunConcurrencyTest();
+}
+
+} // namespace
diff --git a/UnitTests/src/GraphicsAccessories/RingBufferTest.cpp b/UnitTests/src/GraphicsAccessories/RingBufferTest.cpp
new file mode 100644
index 00000000..f89a0441
--- /dev/null
+++ b/UnitTests/src/GraphicsAccessories/RingBufferTest.cpp
@@ -0,0 +1,291 @@
+/* Copyright 2019 Diligent Graphics LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+// EngineSandbox.cpp : Defines the entry point for the application.
+//
+
+#include "RingBuffer.h"
+#include "DefaultRawMemoryAllocator.h"
+
+#include "gtest/gtest.h"
+
+using namespace Diligent;
+
+namespace
+{
+
+TEST(GraphicsAccessories_RingBuffer, AllocDealloc)
+{
+ auto& Allocator = DefaultRawMemoryAllocator::GetAllocator();
+ {
+ RingBuffer RB(1023, Allocator);
+
+ auto Offset = RB.Allocate(120, 16);
+ //
+ // O h
+ // | | |
+ // 0 128
+ EXPECT_EQ(Offset, 0);
+
+
+ Offset = RB.Allocate(10, 1);
+ //
+ // t O h
+ // | | | |
+ // 0 128 138
+ EXPECT_EQ(Offset, 128);
+
+ Offset = RB.Allocate(10, 32);
+ //
+ // t O h
+ // | | | |
+ // 0 128 138 160 192
+ EXPECT_EQ(Offset, 160);
+
+ Offset = RB.Allocate(17, 1);
+ //
+ // t O h
+ // | | | |
+ // 0 128 138 160 192 209
+ EXPECT_EQ(Offset, 192);
+
+ Offset = RB.Allocate(65, 64);
+ //
+ // t O h
+ // | | | |
+ // 0 128 138 160 192 209 256 384
+ EXPECT_EQ(Offset, 256);
+
+ RB.FinishCurrentFrame(1);
+ //
+ // t h1
+ // | | |
+ // 0 384
+
+
+ Offset = RB.Allocate(100, 256);
+ //
+ // t h1 O h
+ // | | | | |
+ // 0 384 512 768
+ EXPECT_EQ(Offset, 512);
+
+
+ Offset = RB.Allocate(127, 1);
+ //
+ // t h1 O h
+ // | | | | |
+ // 0 384 512 768 895 = 1023-128
+ EXPECT_EQ(Offset, 768);
+
+ Offset = RB.Allocate(128, 2);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ Offset = RB.Allocate(128, 1);
+ //
+ // t h1 O h
+ // | | | |
+ // 0 384 512 768 895 1023
+ EXPECT_EQ(Offset, 895);
+
+ RB.FinishCurrentFrame(2);
+ //
+ // t h1 h2,h
+ // | | |
+ // 0 384 1023
+ EXPECT_TRUE(RB.IsFull());
+
+ Offset = RB.Allocate(1, 1);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ RingBuffer RB1(std::move(RB));
+ EXPECT_TRUE(RB.IsEmpty());
+ RB1.ReleaseCompletedFrames(1);
+ //
+ // t h2
+ // | | |
+ // 0 384 1023
+
+ RB1.ReleaseCompletedFrames(2);
+ //
+ // h,t
+ // | |
+ // 0 1023
+ EXPECT_TRUE(RB1.IsEmpty());
+ EXPECT_EQ(RB1.GetUsedSize(), 0);
+
+ Offset = RB1.Allocate(256, 1);
+ //
+ // O h t
+ // | | |
+ // 0 256 1023
+ EXPECT_EQ(Offset, 0);
+
+
+ Offset = RB1.Allocate(256, 16);
+ RB1.ReleaseCompletedFrames(0);
+ //
+ // O h t
+ // | | | |
+ // 0 256 512 1023
+ EXPECT_EQ(Offset, 256);
+ RB1.FinishCurrentFrame(2);
+ RB1.FinishCurrentFrame(3); // ignored
+ //
+ // h2 t
+ // | | |
+ // 0 512 1023
+ RB1.ReleaseCompletedFrames(2);
+ //
+ // h,t
+ // | |
+ // 0 1023
+
+ RB1.ReleaseCompletedFrames(3);
+
+ EXPECT_EQ(RB1.GetUsedSize(), 0);
+ EXPECT_TRUE(RB1.IsEmpty());
+
+ Offset = RB1.Allocate(512, 1);
+ //
+ // O h
+ // | | |
+ // 0 512 1023
+ EXPECT_EQ(Offset, 0);
+
+ RB1.FinishCurrentFrame(4);
+ RB1.FinishCurrentFrame(5);
+ //
+ // t h4
+ // | | |
+ // 0 512 1023
+
+ Offset = RB1.Allocate(129, 1);
+ //
+ // t h4,O h
+ // | | | |
+ // 0 512 641 1023
+ EXPECT_EQ(Offset, 512);
+
+ RB1.ReleaseCompletedFrames(4);
+ //
+ // t h
+ // | | | |
+ // 0 512 641 1023
+
+
+ Offset = RB1.Allocate(128, 128);
+ //
+ // t O h
+ // | | | | |
+ // 0 512 641 768 896 1023
+ EXPECT_EQ(Offset, 768);
+
+ Offset = RB1.Allocate(513, 64);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ Offset = RB1.Allocate(513, 1);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ Offset = RB1.Allocate(255, 1);
+ //
+ // O h t
+ // | | | |
+ // 0 255 512 1023
+ EXPECT_EQ(Offset, 0);
+
+ RB1.FinishCurrentFrame(6);
+ //
+ // O h,h6 t
+ // | | | |
+ // 0 255 512 1023
+
+
+ Offset = RB1.Allocate(256, 2);
+ //
+ // h6 O t,h
+ // | | | | |
+ // 0 255 256 512 1023
+ EXPECT_EQ(Offset, 256);
+
+ EXPECT_TRUE(RB1.IsFull());
+ Offset = RB1.Allocate(1, 1);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+ RB1.ReleaseCompletedFrames(6);
+ //
+ // t h
+ // | | | |
+ // 0 255 512 1023
+
+ Offset = RB1.Allocate(511, 1);
+ //
+ // t O h
+ // | | | |
+ // 0 255 512 1023
+ EXPECT_EQ(Offset, 512);
+
+ Offset = RB1.Allocate(191, 1);
+ //
+ // O h t
+ // | | | |
+ // 0 191 255 1023
+ EXPECT_EQ(Offset, 0);
+
+ Offset = RB1.Allocate(64, 2);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ Offset = RB1.Allocate(64, 1);
+ //
+ // O t,h
+ // | | | |
+ // 0 191 255 1023
+ EXPECT_EQ(Offset, 191);
+
+ Offset = RB1.Allocate(1, 1);
+ EXPECT_EQ(Offset, RingBuffer::InvalidOffset);
+
+ RB1.FinishCurrentFrame(7);
+ RB1.ReleaseCompletedFrames(7);
+ }
+
+ {
+ RingBuffer RB(1024, Allocator);
+
+ auto offset = RB.Allocate(512, 1);
+ RB.FinishCurrentFrame(0);
+ RB.FinishCurrentFrame(1);
+ RB.ReleaseCompletedFrames(1);
+ RB.FinishCurrentFrame(2);
+ RB.FinishCurrentFrame(3);
+ offset = RB.Allocate(512, 1);
+ RB.FinishCurrentFrame(4);
+ RB.ReleaseCompletedFrames(2);
+ RB.ReleaseCompletedFrames(3);
+ RB.ReleaseCompletedFrames(4);
+ offset = RB.Allocate(512, 1);
+ RB.FinishCurrentFrame(5);
+ RB.ReleaseCompletedFrames(5);
+ }
+}
+
+} // namespace