You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3957 lines
168 KiB
C++
3957 lines
168 KiB
C++
/*
|
|
* 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 "Tests.h"
|
|
#include "TestProfiler.h"
|
|
|
|
#include <GridMate/Replica/ReplicaFunctions.h>
|
|
|
|
#include <AzCore/Math/Sfmt.h>
|
|
#include <AzCore/std/parallel/thread.h>
|
|
|
|
#include <GridMate/Carrier/DefaultSimulator.h>
|
|
#include <GridMate/Replica/Interpolators.h>
|
|
#include <GridMate/Replica/Replica.h>
|
|
#include <GridMate/Replica/ReplicaMgr.h>
|
|
|
|
#include <GridMate/Serialize/CompressionMarshal.h>
|
|
|
|
#define GM_REPLICA_TEST_SESSION_CHANNEL 1
|
|
|
|
using namespace GridMate;
|
|
|
|
#if defined(max)
|
|
#undef max
|
|
#endif
|
|
|
|
#if defined(min)
|
|
#undef min
|
|
#endif
|
|
|
|
namespace UnitTest {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
class InterpolatorTest
|
|
: public GridMateMPTestFixture
|
|
{
|
|
public:
|
|
float m_zigVals[1000];
|
|
static const int k_actualSampleStart = 100;
|
|
static const int k_offsetBetweenSamples = 10;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
InterpolatorTest()
|
|
{
|
|
for (int a = 0; a < 100; ++a)
|
|
{
|
|
m_zigVals[a ] = static_cast< float >(rand() % 200 - 100);
|
|
m_zigVals[a + 200] = static_cast< float >(rand() % 200 - 100);
|
|
m_zigVals[a + 400] = static_cast< float >(rand() % 200 - 100);
|
|
m_zigVals[a + 600] = static_cast< float >(rand() % 200 - 100);
|
|
m_zigVals[a + 800] = static_cast< float >(rand() % 200 - 100);
|
|
}
|
|
|
|
for (int a = 100; a < 200; ++a)
|
|
{
|
|
m_zigVals[a] = 10.f;
|
|
}
|
|
|
|
for (int a = 300; a < 400; ++a)
|
|
{
|
|
m_zigVals[a] = static_cast< float >((a - 300) * (a - 300));
|
|
}
|
|
|
|
for (int a = 500; a < 600; ++a)
|
|
{
|
|
m_zigVals[a] = static_cast< float >(a - 500) * 0.7f - 20.f;
|
|
}
|
|
|
|
for (int a = 700; a < 800; ++a)
|
|
{
|
|
m_zigVals[a] = AZ::Sqrt(static_cast< float >(a));
|
|
}
|
|
|
|
for (int a = 900; a < 1000; ++a)
|
|
{
|
|
m_zigVals[a] = static_cast< float >(a - 900) * -5.f + 100.f;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template< typename T >
|
|
void AddSamplesConstant(T& interpolator, int numSamples, const float k_constant)
|
|
{
|
|
for (int a = 0; a < numSamples; ++a)
|
|
{
|
|
interpolator.AddSample(k_constant, k_actualSampleStart + a * k_offsetBetweenSamples);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template< typename T >
|
|
void AddSamplesLinear(T& interpolator, int numSamples, float slope, float yIntercept)
|
|
{
|
|
for (int a = 0; a < numSamples; ++a)
|
|
{
|
|
interpolator.AddSample(slope * static_cast< float >(a) + yIntercept, k_actualSampleStart + a * k_offsetBetweenSamples);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template< typename T >
|
|
void AddSamplesZigZag(T& interpolator, int numSamples)
|
|
{
|
|
for (int a = 0; a < numSamples; ++a)
|
|
{
|
|
interpolator.AddSample(m_zigVals[a % AZ_ARRAY_SIZE(m_zigVals)], k_actualSampleStart + a * k_offsetBetweenSamples);
|
|
}
|
|
}
|
|
|
|
void run()
|
|
{
|
|
//////////////////////////////////////////
|
|
// testing point sample
|
|
EpsilonThrottle< float > epsilon;
|
|
epsilon.SetThreshold(0.001f);
|
|
float check = -1.f;
|
|
(void)check;
|
|
|
|
// ensure interpolator returns correct value when it only has one sample
|
|
{
|
|
const int k_time = 0;
|
|
const int k_sample = 1337;
|
|
PointSample< int > interpolator;
|
|
interpolator.AddSample(k_sample, k_time);
|
|
AZ_TEST_ASSERT(interpolator.GetInterpolatedValue(k_time) == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetLastValue() == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetSampleCount() == 1);
|
|
|
|
SampleInfo< int > info = interpolator.GetSampleInfo(0);
|
|
AZ_TEST_ASSERT(info.m_t == k_time);
|
|
AZ_TEST_ASSERT(info.m_v == k_sample);
|
|
}
|
|
|
|
// sample set partway full (pattern constant)
|
|
{
|
|
const int k_sampleArraySize = 100;
|
|
const int k_numSamples = k_sampleArraySize;
|
|
const float k_constant = 5.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
for (int a = -k_offsetBetweenSamples; a < k_offsetBetweenSamples * (k_numSamples + 2); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set partway full (pattern linear)
|
|
{
|
|
const int k_numSamples = 500;
|
|
const int k_sampleArraySize = 800;
|
|
const float k_slope = 1.f;
|
|
const float k_intercept = 10.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to value before any samples
|
|
for (int a = k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate after samples
|
|
for (int a = 0; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(a / k_offsetBetweenSamples) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to value after last sample
|
|
for (int a = k_numSamples * k_offsetBetweenSamples; a < (k_numSamples + 2) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set partway full (pattern zigzag)
|
|
{
|
|
const int k_numSamples = 400;
|
|
const int k_sampleArraySize = 800;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
// interpolate to before earliest remaining sample record
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate from existing samples
|
|
for (int a = 0; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower];
|
|
epsilon.SetBaseline(target);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
// interpolate after last known sample
|
|
for (int a = k_offsetBetweenSamples * k_numSamples; a < k_offsetBetweenSamples * (1 + k_numSamples); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern constant)
|
|
{
|
|
const int k_numSamples = 860;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const float k_constant = 5.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
for (int a = -k_offsetBetweenSamples; a < (k_numSamples + 2) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern linear)
|
|
{
|
|
const int k_sampleArraySize = 600;
|
|
const int k_numSamples = k_sampleArraySize;
|
|
const float k_slope = 1.f;
|
|
const float k_intercept = 10.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to value before any samples
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate after samples
|
|
for (int a = 0; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(a / k_offsetBetweenSamples) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to value after last sample
|
|
for (int a = k_numSamples * k_offsetBetweenSamples; a < (k_numSamples + 2) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern zigzag)
|
|
{
|
|
const int k_sampleArraySize = 1200;
|
|
const int k_numSamples = k_sampleArraySize;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
|
|
// interpolate to before earliest remaining sample record
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate from existing samples
|
|
for (int a = 0; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower];
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
// interpolate after last known sample
|
|
for (int a = k_offsetBetweenSamples * k_numSamples; a < k_offsetBetweenSamples * (1 + k_numSamples); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern constant)
|
|
{
|
|
const int k_sampleArraySize = 80;
|
|
const int k_numSamples = 120;
|
|
const float k_constant = 5.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
for (int a = -k_offsetBetweenSamples; a < (k_numSamples + 2) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern linear)
|
|
{
|
|
const int k_numSamples = 1200;
|
|
const int k_sampleArraySize = 80;
|
|
const float k_slope = 1.f;
|
|
const float k_intercept = 10.f;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
|
|
// interpolate to value before any samples
|
|
for (int a = -k_offsetBetweenSamples + (k_numSamples - k_sampleArraySize) * k_offsetBetweenSamples; a < (k_numSamples - k_sampleArraySize) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_intercept + k_slope * static_cast< float >(k_numSamples - k_sampleArraySize));
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate after samples
|
|
for (int a = (k_numSamples - k_sampleArraySize) * k_offsetBetweenSamples; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(a / k_offsetBetweenSamples) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to value after last sample
|
|
for (int a = k_numSamples * k_offsetBetweenSamples; a < (k_numSamples + 2) * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - 1) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern zig-zag)
|
|
{
|
|
const int k_numSamples = 1500;
|
|
const int k_sampleArraySize = 1000;
|
|
PointSample< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
|
|
// interpolate to before earliest remaining sample record
|
|
for (int a = -k_offsetBetweenSamples + k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a < k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - k_sampleArraySize) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate from existing samples
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower];
|
|
epsilon.SetBaseline(target);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
// interpolate after last known sample
|
|
for (int a = k_offsetBetweenSamples * k_numSamples; a < k_offsetBetweenSamples * (1 + k_numSamples); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// test Break
|
|
{
|
|
PointSample< float > interpolator;
|
|
interpolator.Break();
|
|
}
|
|
|
|
// sample set max size 1
|
|
{
|
|
PointSample< float, 1 > interpolator;
|
|
|
|
// with empty set (uncomment to make sure asserts fire)
|
|
//check = interpolator.GetLastValue();
|
|
//check = interpolator.GetInterpolatedValue( 210 );
|
|
|
|
// with populated set
|
|
interpolator.AddSample(1.f, 100);
|
|
check = interpolator.GetInterpolatedValue(90);
|
|
epsilon.SetBaseline(1.f);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(1.f));
|
|
check = interpolator.GetInterpolatedValue(100);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(110);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced
|
|
interpolator.AddSample(10.f, 200);
|
|
epsilon.SetBaseline(10.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced at the same time stamp
|
|
interpolator.AddSample(20.f, 200);
|
|
epsilon.SetBaseline(20.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
// end testing point-sampling
|
|
//////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////
|
|
// testing linear interpolation
|
|
|
|
// ensure interpolator returns correct value when it only has one sample
|
|
{
|
|
const int k_time = 0;
|
|
const int k_sample = 1337;
|
|
LinearInterp< int > interpolator;
|
|
interpolator.AddSample(k_sample, k_time);
|
|
AZ_TEST_ASSERT(interpolator.GetInterpolatedValue(k_time) == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetLastValue() == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetSampleCount() == 1);
|
|
|
|
SampleInfo< int > info = interpolator.GetSampleInfo(0);
|
|
AZ_TEST_ASSERT(info.m_t == k_time);
|
|
AZ_TEST_ASSERT(info.m_v == k_sample);
|
|
}
|
|
|
|
// sample set partway full (pattern constant)
|
|
{
|
|
const int k_numSamples = 50;
|
|
const int k_sampleArraySize = 100;
|
|
const float k_constant = 10.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 40] : where there are samples to interpolate
|
|
// (40, 50]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set partway full (pattern linear)
|
|
{
|
|
const float k_slope = 0.5f;
|
|
const float k_intercept = 5.f;
|
|
const int k_numSamples = 600;
|
|
const int k_sampleArraySize = 800;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
float target = k_slope * static_cast< float >(k_numSamples - 1) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1) + 1; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set partway full (pattern zigzag)
|
|
{
|
|
const int k_numSamples = 400;
|
|
const int k_sampleArraySize = 500;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower] + static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower]) * static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples)) / static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern constant)
|
|
{
|
|
const int k_numSamples = 500;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const float k_constant = 10.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 40] : where there are samples to interpolate
|
|
// (40, 50]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern linear)
|
|
{
|
|
const int k_numSamples = 850;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const float k_slope = 0.5f;
|
|
const float k_intercept = 5.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
float target = k_slope * static_cast< float >(k_numSamples - 1) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1) + 1; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
AZ_TracePrintf("GridMate", "this pointer: 0x%p\n", this);
|
|
|
|
// sample set full (pattern zig-zag)
|
|
{
|
|
const int k_numSamples = 100;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern constant)
|
|
{
|
|
const int k_sampleArraySize = 80;
|
|
const int k_numSamples = 100;
|
|
const float k_constant = 10.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 40] : where there are samples to interpolate
|
|
// (40, 50]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_numSamples * k_offsetBetweenSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern linear)
|
|
{
|
|
const int k_numSamples = 140;
|
|
const int k_sampleArraySize = 90;
|
|
const float k_slope = 0.5f;
|
|
const float k_intercept = 5.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize - 1); a < k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - k_sampleArraySize) + k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
float target = k_slope * static_cast< float >(k_numSamples - 1) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1) + 1; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern zigzag)
|
|
{
|
|
const int k_numSamples = 250;
|
|
const int k_sampleArraySize = 100;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize - 1); a < k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - k_sampleArraySize) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower] + static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower]) * static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples)) / static_cast< float >(k_offsetBetweenSamples);
|
|
epsilon.SetBaseline(target);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// test Break
|
|
{
|
|
const int k_numSamples = 20;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const int k_break = 10;
|
|
const float k_intercept = 0.f;
|
|
const float k_slope = 1.f;
|
|
LinearInterp< float, k_sampleArraySize > interpolator;
|
|
for (int a = 0; a < k_numSamples; ++a)
|
|
{
|
|
if (a == k_break)
|
|
{
|
|
interpolator.Break();
|
|
}
|
|
interpolator.AddSample(k_slope * static_cast< float >(a) + k_intercept, k_actualSampleStart + a * k_offsetBetweenSamples);
|
|
}
|
|
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target;
|
|
if (a / k_offsetBetweenSamples + 1 == k_break)
|
|
{
|
|
target = k_slope * static_cast< float >(a / k_offsetBetweenSamples) + k_intercept;
|
|
}
|
|
else
|
|
{
|
|
target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
}
|
|
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
float target = k_slope * static_cast< float >(k_numSamples - 1) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1) + 1; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set max size 1
|
|
{
|
|
LinearInterp< float, 1 > interpolator;
|
|
|
|
// with empty set (uncomment to make sure asserts fire)
|
|
//interpolator.GetLastValue();
|
|
//interpolator.GetInterpolatedValue( 210 );
|
|
|
|
// with populated set
|
|
interpolator.AddSample(1.f, 100);
|
|
epsilon.SetBaseline(1.f);
|
|
check = interpolator.GetInterpolatedValue(90);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(100);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(110);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced
|
|
interpolator.AddSample(10.f, 200);
|
|
epsilon.SetBaseline(10.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced at the same time stamp
|
|
interpolator.AddSample(20.f, 200);
|
|
epsilon.SetBaseline(20.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
// end testing linear interpolation
|
|
//////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////
|
|
// testing linear interpolation and extrapolation
|
|
{
|
|
// ensure interpolator returns correct value when it only has one sample
|
|
{
|
|
const int k_time = 0;
|
|
const int k_sample = 1337;
|
|
LinearInterpExtrap< int > interpolator;
|
|
interpolator.AddSample(k_sample, k_time);
|
|
AZ_TEST_ASSERT(interpolator.GetInterpolatedValue(k_time) == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetLastValue() == k_sample);
|
|
AZ_TEST_ASSERT(interpolator.GetSampleCount() == 1);
|
|
|
|
SampleInfo< int > info = interpolator.GetSampleInfo(0);
|
|
AZ_TEST_ASSERT(info.m_t == k_time);
|
|
AZ_TEST_ASSERT(info.m_v == k_sample);
|
|
}
|
|
|
|
// sample set partway full (pattern constant)
|
|
{
|
|
const int k_numSamples = 35;
|
|
const int k_sampleArraySize = 80;
|
|
const float k_constant = 15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 70] : where there are samples to interpolate
|
|
// (70, 80]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set partway full (pattern linear)
|
|
{
|
|
const int k_numSamples = 600;
|
|
const int k_sampleArraySize = 800;
|
|
const float k_slope = 3.f;
|
|
const float k_intercept = -15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) * 1.f / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples) + 1; a < k_offsetBetweenSamples * (k_numSamples + 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
}
|
|
|
|
// sample set partway full (pattern zig-zag)
|
|
{
|
|
const int k_numSamples = 750;
|
|
const int k_sampleArraySize = 1000;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
int idxUpper = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxLower = (idxUpper - 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(k_offsetBetweenSamples + a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern constant)
|
|
{
|
|
const int k_numSamples = 350;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const float k_constant = 15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 70] : where there are samples to interpolate
|
|
// (70, 80]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set full (pattern linear)
|
|
{
|
|
const int k_numSamples = 700;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const float k_slope = 3.f;
|
|
const float k_intercept = -15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
epsilon.SetBaseline(k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples) + 1; a < k_offsetBetweenSamples * (k_numSamples + 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
}
|
|
|
|
// sample set full (pattern zigzag)
|
|
{
|
|
const int k_numSamples = 950;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
epsilon.SetBaseline(m_zigVals[0]);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = 0; a <= k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
int idxUpper = (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxLower = (idxUpper - 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(k_offsetBetweenSamples + a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (pattern constant)
|
|
{
|
|
const int k_numSamples = 150;
|
|
const int k_sampleArraySize = 80;
|
|
const float k_constant = 15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesConstant(interpolator, k_numSamples, k_constant);
|
|
epsilon.SetBaseline(k_constant);
|
|
|
|
// interpolate to before samples start / where there are samples to interpolate / past last sample
|
|
// [-10,0) : before samples start
|
|
// [0, 70] : where there are samples to interpolate
|
|
// (70, 80]: past last sample
|
|
for (int a = -k_offsetBetweenSamples; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// sample set wrapped around (linear)
|
|
{
|
|
const int k_numSamples = 800;
|
|
const int k_sampleArraySize = 600;
|
|
const float k_slope = 3.f;
|
|
const float k_intercept = -15.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
interpolator.Clear();
|
|
AddSamplesLinear(interpolator, k_numSamples, k_slope, k_intercept);
|
|
epsilon.SetBaseline(k_slope * static_cast< float >(k_numSamples - k_sampleArraySize) + k_intercept);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize - 1); a < k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a <= k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples) + 1; a < k_offsetBetweenSamples * (k_numSamples + 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
}
|
|
|
|
// sample set wrapped around (pattern zigzag)
|
|
{
|
|
const int k_numSamples = 1500;
|
|
const int k_sampleArraySize = 1000;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
AddSamplesZigZag(interpolator, k_numSamples);
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - k_sampleArraySize) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
|
|
// interpolate to before samples start
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize - 1); a < k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate to where there are samples to interpolate
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - k_sampleArraySize); a < k_offsetBetweenSamples * (k_numSamples - 1); ++a)
|
|
{
|
|
int idxLower = (a / k_offsetBetweenSamples) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxUpper = (idxLower + 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)target;
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
int idxUpper = (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
int idxLower = (idxUpper - 1) % AZ_ARRAY_SIZE(m_zigVals);
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples - 1); a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
float target = m_zigVals[idxLower]
|
|
+ static_cast< float >(m_zigVals[idxUpper] - m_zigVals[idxLower])
|
|
* static_cast< float >(k_offsetBetweenSamples + a - k_offsetBetweenSamples * (a / k_offsetBetweenSamples))
|
|
/ static_cast< float >(k_offsetBetweenSamples);
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
epsilon.SetBaseline(m_zigVals[ (k_numSamples - 1) % AZ_ARRAY_SIZE(m_zigVals) ]);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(interpolator.GetLastValue()));
|
|
}
|
|
|
|
// test Break
|
|
{
|
|
const int k_numSamples = 20;
|
|
const int k_sampleArraySize = k_numSamples;
|
|
const int k_break = 10;
|
|
const float k_intercept = 0.f;
|
|
const float k_slope = 1.f;
|
|
LinearInterpExtrap< float, k_sampleArraySize > interpolator;
|
|
|
|
for (int a = 0; a < k_numSamples; ++a)
|
|
{
|
|
if (a == k_break)
|
|
{
|
|
interpolator.Break();
|
|
}
|
|
interpolator.AddSample(k_slope * static_cast< float >(a) + k_intercept, k_actualSampleStart + a * k_offsetBetweenSamples);
|
|
}
|
|
|
|
// interpolate to before samples start
|
|
for (int a = -k_offsetBetweenSamples; a < 0; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
epsilon.SetBaseline(k_intercept);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate where there are samples to interpolate
|
|
for (int a = 0; a < k_offsetBetweenSamples * k_numSamples; ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target;
|
|
if (a / k_offsetBetweenSamples + 1 == k_break)
|
|
{
|
|
target = k_slope * static_cast< float >(a / k_offsetBetweenSamples) + k_intercept;
|
|
}
|
|
else
|
|
{
|
|
target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
}
|
|
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
|
|
// interpolate past last sample
|
|
for (int a = k_offsetBetweenSamples * (k_numSamples) + 1; a < k_offsetBetweenSamples * (k_numSamples + 1); ++a)
|
|
{
|
|
check = interpolator.GetInterpolatedValue(k_actualSampleStart + a);
|
|
float target = k_slope * static_cast< float >(a) / static_cast< float >(k_offsetBetweenSamples) + k_intercept;
|
|
|
|
epsilon.SetBaseline(target);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
(void)check;
|
|
}
|
|
}
|
|
|
|
// sample set max size 1
|
|
{
|
|
LinearInterpExtrap< float, 1 > interpolator;
|
|
|
|
// with empty set (uncomment to make sure asserts fire)
|
|
//interpolator.GetLastValue();
|
|
//interpolator.GetInterpolatedValue( 210 );
|
|
|
|
// with populated set
|
|
interpolator.AddSample(1.f, 100);
|
|
epsilon.SetBaseline(1.f);
|
|
check = interpolator.GetInterpolatedValue(90);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(100);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(110);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced
|
|
interpolator.AddSample(10.f, 200);
|
|
epsilon.SetBaseline(10.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
|
|
// with the only sample replaced at the same time stamp
|
|
interpolator.AddSample(20.f, 200);
|
|
epsilon.SetBaseline(20.f);
|
|
check = interpolator.GetInterpolatedValue(190);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(200);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetInterpolatedValue(210);
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
check = interpolator.GetLastValue();
|
|
AZ_TEST_ASSERT(epsilon.WithinThreshold(check));
|
|
}
|
|
|
|
AZ_TracePrintf("GridMate", "this pointer: 0x%p", this);
|
|
}
|
|
// end testing linear interpolation and extrapolation
|
|
//////////////////////////////////////////
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
class MPSession
|
|
: public CarrierEventBus::Handler
|
|
{
|
|
public:
|
|
ReplicaManager& GetReplicaMgr() { return m_rm; }
|
|
void SetTransport(Carrier* transport) { m_pTransport = transport; CarrierEventBus::Handler::BusConnect(transport->GetGridMate()); }
|
|
Carrier* GetTransport() { return m_pTransport; }
|
|
void SetClient(bool isClient) { m_client = isClient; }
|
|
void AcceptConn(bool accept) { m_acceptConn = accept; }
|
|
|
|
~MPSession()
|
|
{
|
|
CarrierEventBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
char buf[1500];
|
|
for (ConnectionSet::iterator iConn = m_connections.begin(); iConn != m_connections.end(); ++iConn)
|
|
{
|
|
ConnectionID conn = *iConn;
|
|
Carrier::ReceiveResult result = m_pTransport->Receive(buf, 1500, conn, GM_REPLICA_TEST_SESSION_CHANNEL);
|
|
if (result.m_state == Carrier::ReceiveResult::RECEIVED)
|
|
{
|
|
if (strcmp(buf, "IM_A_CLIENT") == 0)
|
|
{
|
|
m_rm.AddPeer(conn, Mode_Client);
|
|
}
|
|
else if (strcmp(buf, "IM_A_PEER") == 0)
|
|
{
|
|
m_rm.AddPeer(conn, Mode_Peer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
typename T::Ptr GetChunkFromReplica(ReplicaId id)
|
|
{
|
|
ReplicaPtr replica = GetReplicaMgr().FindReplica(id);
|
|
if (!replica)
|
|
{
|
|
return nullptr;
|
|
}
|
|
return replica->FindReplicaChunk<T>();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CarrierEventBus
|
|
void OnConnectionEstablished(Carrier* carrier, ConnectionID id) override
|
|
{
|
|
if (carrier != m_pTransport)
|
|
{
|
|
return; // not for us
|
|
}
|
|
m_connections.insert(id);
|
|
if (m_client)
|
|
{
|
|
m_pTransport->Send("IM_A_CLIENT", 12, id, Carrier::SEND_RELIABLE, Carrier::PRIORITY_NORMAL, GM_REPLICA_TEST_SESSION_CHANNEL);
|
|
}
|
|
else
|
|
{
|
|
m_pTransport->Send("IM_A_PEER", 10, id, Carrier::SEND_RELIABLE, Carrier::PRIORITY_NORMAL, GM_REPLICA_TEST_SESSION_CHANNEL);
|
|
}
|
|
}
|
|
|
|
void OnDisconnect(Carrier* carrier, ConnectionID id, CarrierDisconnectReason /*reason*/) override
|
|
{
|
|
if (carrier != m_pTransport)
|
|
{
|
|
return; // not for us
|
|
}
|
|
m_rm.RemovePeer(id);
|
|
m_connections.erase(id);
|
|
}
|
|
|
|
void OnDriverError(Carrier* carrier, ConnectionID id, const DriverError& error) override
|
|
{
|
|
(void)error;
|
|
if (carrier != m_pTransport)
|
|
{
|
|
return; // not for us
|
|
}
|
|
m_pTransport->Disconnect(id);
|
|
}
|
|
|
|
void OnSecurityError(Carrier* carrier, ConnectionID id, const SecurityError& error) override
|
|
{
|
|
(void)carrier;
|
|
(void)id;
|
|
(void)error;
|
|
//Ignore security warnings in unit tests
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
ReplicaManager m_rm;
|
|
Carrier* m_pTransport;
|
|
typedef unordered_set<ConnectionID> ConnectionSet;
|
|
ConnectionSet m_connections;
|
|
bool m_client;
|
|
bool m_acceptConn;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
class MyObj
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(MyObj);
|
|
MyObj()
|
|
: m_f1(0.f)
|
|
, m_b1(false)
|
|
, m_i1(0) {}
|
|
|
|
float m_f1;
|
|
bool m_b1;
|
|
int m_i1;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
class MyCtorContext
|
|
: public CtorContextBase
|
|
{
|
|
public:
|
|
CtorDataSet<float, Float16Marshaler> m_f;
|
|
|
|
MyCtorContext()
|
|
: m_f(Float16Marshaler(0.f, 1.f))
|
|
{}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
class MigratableReplica
|
|
: public ReplicaChunk
|
|
{
|
|
public:
|
|
class Descriptor
|
|
: public ReplicaChunkDescriptor
|
|
{
|
|
public:
|
|
Descriptor()
|
|
: ReplicaChunkDescriptor(MigratableReplica::GetChunkName(), sizeof(MigratableReplica))
|
|
{
|
|
}
|
|
|
|
ReplicaChunkBase* CreateFromStream(UnmarshalContext& mc) override
|
|
{
|
|
MyCtorContext cc;
|
|
cc.Unmarshal(*mc.m_iBuf);
|
|
|
|
// Important hooks. Pre/Post construct allows us to detect all datasets.
|
|
if (mc.m_rm->GetUserContext(12345))
|
|
{
|
|
AZ_TracePrintf("GridMate", "Create with UserData:%p\n", mc.m_rm->GetUserContext(12345));
|
|
}
|
|
ReplicaChunk* chunk = aznew MigratableReplica;
|
|
return chunk;
|
|
}
|
|
|
|
void DiscardCtorStream(UnmarshalContext& mc) override
|
|
{
|
|
MyCtorContext cc;
|
|
cc.Unmarshal(*mc.m_iBuf);
|
|
}
|
|
|
|
void DeleteReplicaChunk(ReplicaChunkBase* chunkInstance) override { delete chunkInstance; }
|
|
|
|
void MarshalCtorData(ReplicaChunkBase*, WriteBuffer& wb) override
|
|
{
|
|
MyCtorContext cc;
|
|
cc.m_f.Set(0.5f);
|
|
cc.Marshal(wb);
|
|
}
|
|
};
|
|
|
|
class MigratableReplicaDebugMsgs
|
|
: public AZ::Debug::DrillerEBusTraits
|
|
{
|
|
public:
|
|
typedef AZ::EBus<MigratableReplicaDebugMsgs> EBus;
|
|
|
|
virtual void OnNewOwner(ReplicaId repId, ReplicaManager* repMgr) = 0;
|
|
};
|
|
|
|
typedef AZStd::intrusive_ptr<MigratableReplica> Ptr;
|
|
|
|
GM_CLASS_ALLOCATOR(MigratableReplica);
|
|
static const char* GetChunkName() {return "MigratableReplica"; }
|
|
|
|
MigratableReplica(MyObj* pObj = nullptr)
|
|
: MyHandler123Rpc("MyHandler123Rpc")
|
|
, m_data1("Data1")
|
|
, m_data2("Data2")
|
|
, m_data3("Data3", 3.0f, Float16Marshaler(0.0f, 10.0f))
|
|
, m_data4("Data4")
|
|
|
|
{
|
|
Bind(pObj);
|
|
}
|
|
|
|
bool IsReplicaMigratable() override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool MyHandler123(const float& f, const RpcContext& rc)
|
|
{
|
|
(void)f;
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, GetReplica()->IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
return true;
|
|
}
|
|
|
|
Rpc<RpcArg<const float&> >::BindInterface<MigratableReplica, & MigratableReplica::MyHandler123> MyHandler123Rpc;
|
|
|
|
void UpdateChunk(const ReplicaContext& rc) override
|
|
{
|
|
if (m_pLocalObj)
|
|
{
|
|
m_data1.Set(m_pLocalObj->m_f1);
|
|
m_data1Interpolated.AddSample(m_pLocalObj->m_f1, rc.m_localTime);
|
|
|
|
m_data2.Set(m_pLocalObj->m_i1);
|
|
m_data3.Set(m_pLocalObj->m_f1);
|
|
}
|
|
AZStd::bitset<25> bits = m_data4.Get();
|
|
m_data4.Set(bits.flip());
|
|
}
|
|
|
|
void UpdateFromChunk(const ReplicaContext& rc) override
|
|
{
|
|
// AZ_TracePrintf("GridMate", "Updating proxy 0x%x on peer %d coming from peer %d %s\n", GetRepId(), rc.rm->GetLocalPeerId(), rc.myPeer->GetId(), rc.myPeer->GetConnectionId() == InvalidConnectionID ? "(orphan)" : "");
|
|
if (m_pLocalObj)
|
|
{
|
|
m_data1Interpolated.AddSample(m_data1.Get(), m_data1.GetLastUpdateTime());
|
|
m_pLocalObj->m_f1 = m_data1Interpolated.GetInterpolatedValue(rc.m_localTime);
|
|
|
|
m_pLocalObj->m_i1 = m_data2.Get();
|
|
}
|
|
m_dummy = m_data3.Get();
|
|
}
|
|
|
|
void OnReplicaActivate(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
if (rc.m_rm->GetUserContext(12345))
|
|
{
|
|
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", GetReplica()->IsPrimary() ? "primary" : "proxy", rc.m_rm->GetUserContext(12345));
|
|
}
|
|
if (IsProxy())
|
|
{
|
|
Bind(aznew MyObj());
|
|
}
|
|
|
|
if (IsPrimary())
|
|
{
|
|
EBUS_EVENT(MigratableReplicaDebugMsgs::EBus, OnNewOwner, GetReplicaId(), rc.m_rm);
|
|
}
|
|
}
|
|
|
|
void OnReplicaDeactivate(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
if (m_pLocalObj)
|
|
{
|
|
delete m_pLocalObj;
|
|
m_pLocalObj = NULL;
|
|
}
|
|
}
|
|
|
|
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Migratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsPrimary() ? "primary" : "proxy", (int) rc.m_rm->GetLocalPeerId());
|
|
|
|
if (IsPrimary())
|
|
{
|
|
EBUS_EVENT(MigratableReplicaDebugMsgs::EBus, OnNewOwner, GetReplicaId(), rc.m_rm);
|
|
}
|
|
}
|
|
|
|
void Bind(MyObj* pObj)
|
|
{
|
|
m_pLocalObj = pObj;
|
|
}
|
|
private:
|
|
DataSet<float> m_data1;
|
|
LinearInterpExtrap<float> m_data1Interpolated;
|
|
|
|
DataSet<int> m_data2;
|
|
DataSet<float, Float16Marshaler> m_data3;
|
|
DataSet<AZStd::bitset<25> > m_data4;
|
|
|
|
MyObj* m_pLocalObj;
|
|
float m_dummy;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
class NonMigratableReplica
|
|
: public ReplicaChunk
|
|
{
|
|
public:
|
|
enum EBla : AZ::u8
|
|
{
|
|
e_Bla0,
|
|
e_Bla1,
|
|
};
|
|
typedef vector<int> IntVectorType;
|
|
bool m_unreliableCheck;
|
|
protected:
|
|
MyObj* m_pLocalObj;
|
|
int m_prevUnreliableValue;
|
|
|
|
bool MyHandler123(const float& f, const RpcContext& rc)
|
|
{
|
|
(void)f;
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandler123 requested at %u with %g on %s at %u.\n", rc.m_timestamp, f, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
return true;
|
|
}
|
|
bool MyHandler2(const float& f, int p2, const RpcContext& rc)
|
|
{
|
|
(void)f;
|
|
(void)p2;
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandler2 requested at %u with %g,%d on %s at %u.\n", rc.m_timestamp, f, p2, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
return true;
|
|
}
|
|
bool MyHandler3(const float& f, int p2, EBla p3, const RpcContext& rc)
|
|
{
|
|
(void)f;
|
|
(void)p2;
|
|
(void)p3;
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandler3 requested at %u with %g,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
return true;
|
|
}
|
|
bool MyHandler4(const float& f, int p2, EBla p3, const IntVectorType& p4, const RpcContext& rc)
|
|
{
|
|
(void)f;
|
|
(void)p2;
|
|
(void)p3;
|
|
(void)p4;
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandler4 requested at %u with %g,%d,%d,%d,%d on %s at %u.\n", rc.m_timestamp, f, p2, p3, p4[0], p4[1], IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
return true;
|
|
}
|
|
bool MyHandlerUnreliable(const int& i, const RpcContext& rc)
|
|
{
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "Executed MyHandlerUnreliable requested at %u with %d on %s at %u.\n", rc.m_timestamp, i, IsPrimary() ? "Primary" : "Proxy", rc.m_realTime);
|
|
AZ_TEST_ASSERT(i > m_prevUnreliableValue);
|
|
if ((i - m_prevUnreliableValue) > 1)
|
|
{
|
|
m_unreliableCheck = true;
|
|
}
|
|
m_prevUnreliableValue = i;
|
|
return true;
|
|
}
|
|
public:
|
|
GM_CLASS_ALLOCATOR(NonMigratableReplica);
|
|
typedef AZStd::intrusive_ptr<NonMigratableReplica> Ptr;
|
|
static const char* GetChunkName() { return "NonMigratableReplica"; }
|
|
|
|
Rpc<RpcArg<const float&> >::BindInterface<NonMigratableReplica, & NonMigratableReplica::MyHandler123> MyHandler123Rpc;
|
|
Rpc<RpcArg<const float&>, RpcArg<int> >::BindInterface<NonMigratableReplica, & NonMigratableReplica::MyHandler2> MyHandler2Rpc;
|
|
Rpc<RpcArg<const float&>, RpcArg<int>, RpcArg<EBla> >::BindInterface<NonMigratableReplica, & NonMigratableReplica::MyHandler3> MyHandler3Rpc;
|
|
Rpc<RpcArg<const float&>, RpcArg<int>, RpcArg<EBla>, RpcArg<const IntVectorType&> >::BindInterface<NonMigratableReplica, & NonMigratableReplica::MyHandler4> MyHandler4Rpc;
|
|
|
|
Rpc<RpcArg<const int&> >::BindInterface<NonMigratableReplica, & NonMigratableReplica::MyHandlerUnreliable, RpcUnreliable> MyHandlerUnreliableRpc;
|
|
|
|
NonMigratableReplica(MyObj* pObj = NULL)
|
|
: m_unreliableCheck(false)
|
|
, m_prevUnreliableValue(0)
|
|
, MyHandler123Rpc("MyHandler123Rpc")
|
|
, MyHandler2Rpc("MyHandler2Rpc")
|
|
, MyHandler3Rpc("MyHandler3Rpc")
|
|
, MyHandler4Rpc("MyHandler4Rpc")
|
|
, MyHandlerUnreliableRpc("MyHandlerUnreliableRpc")
|
|
, m_data1("Data1")
|
|
, m_data2("Data2")
|
|
{
|
|
Bind(pObj);
|
|
}
|
|
|
|
bool IsReplicaMigratable() override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
~NonMigratableReplica()
|
|
{
|
|
AZ_Assert(!m_pLocalObj, "Local object should be cleared");
|
|
}
|
|
|
|
void UpdateChunk(const ReplicaContext& rc) override
|
|
{
|
|
m_data1.Set(m_pLocalObj->m_f1);
|
|
m_data1Interpolated.AddSample(m_pLocalObj->m_f1, rc.m_localTime);
|
|
|
|
m_data2.Set(m_pLocalObj->m_i1);
|
|
}
|
|
|
|
void UpdateFromChunk(const ReplicaContext& rc) override
|
|
{
|
|
m_data1Interpolated.AddSample(m_data1.Get(), m_data1.GetLastUpdateTime());
|
|
m_pLocalObj->m_f1 = m_data1Interpolated.GetInterpolatedValue(rc.m_localTime);
|
|
|
|
m_pLocalObj->m_i1 = m_data2.Get();
|
|
}
|
|
|
|
void OnReplicaActivate(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
if (rc.m_rm->GetUserContext(12345))
|
|
{
|
|
AZ_TracePrintf("GridMate", "Activate %s with UserData:%p\n", IsPrimary() ? "primary" : "proxy", rc.m_rm->GetUserContext(12345));
|
|
}
|
|
if (IsProxy())
|
|
{
|
|
Bind(aznew MyObj());
|
|
}
|
|
}
|
|
|
|
void OnReplicaDeactivate(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
if (m_pLocalObj)
|
|
{
|
|
delete m_pLocalObj;
|
|
m_pLocalObj = NULL;
|
|
}
|
|
}
|
|
|
|
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
|
|
{
|
|
(void)rc;
|
|
AZ_TracePrintf("GridMate", "NonMigratable replica 0x%x became %s on Peer %d\n", (int) GetReplicaId(), IsPrimary() ? "primary" : "proxy", (int) rc.m_rm->GetLocalPeerId());
|
|
}
|
|
|
|
void Bind(MyObj* pObj)
|
|
{
|
|
m_pLocalObj = pObj;
|
|
}
|
|
|
|
protected:
|
|
DataSet<float> m_data1;
|
|
LinearInterpExtrap<float> m_data1Interpolated;
|
|
|
|
DataSet<int> m_data2;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
class MyDerivedReplica
|
|
: public NonMigratableReplica
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(MyDerivedReplica);
|
|
|
|
MyDerivedReplica()
|
|
: m_data3("Data3") { }
|
|
|
|
typedef AZStd::intrusive_ptr<MyDerivedReplica> Ptr;
|
|
static const char* GetChunkName() { return "MyDerivedReplica"; }
|
|
|
|
virtual void UpdateChunk(const ReplicaContext& rc) override
|
|
{
|
|
NonMigratableReplica::UpdateChunk(rc);
|
|
m_data3.Set(m_pLocalObj->m_b1);
|
|
}
|
|
|
|
virtual void UpdateFromChunk(const ReplicaContext& rc) override
|
|
{
|
|
NonMigratableReplica::UpdateFromChunk(rc);
|
|
m_pLocalObj->m_b1 = m_data3.Get();
|
|
}
|
|
|
|
protected:
|
|
DataSet<bool> m_data3;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class Integ_ReplicaGMTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
, public ::testing::Test
|
|
{};
|
|
|
|
TEST_F(Integ_ReplicaGMTest, ReplicaTest)
|
|
{
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MigratableReplica, MigratableReplica::Descriptor>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<NonMigratableReplica>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MyDerivedReplica>();
|
|
|
|
AZ_TracePrintf("GridMate", "\n");
|
|
enum
|
|
{
|
|
s1,
|
|
s2,
|
|
s3,
|
|
nSessions
|
|
};
|
|
const int k_delay = 100;
|
|
|
|
// Setting up simulator with outgoing packet loss
|
|
DefaultSimulator clientSimulator;
|
|
clientSimulator.SetOutgoingPacketLoss(1, 1);
|
|
|
|
MPSession sessions[nSessions];
|
|
|
|
MyObj* s1obj1 = NULL, * s1obj2 = NULL, * s2obj1 = NULL, * s3obj1 = NULL;
|
|
MigratableReplica::Ptr s1rep1, s3rep1;
|
|
NonMigratableReplica::Ptr s1rep2;
|
|
MyDerivedReplica::Ptr s2rep1;
|
|
ReplicaId s1rep1id = 0, s1rep2id = 0, s2rep1id = 0, s3rep1id = 0;
|
|
|
|
// initialize transport
|
|
int basePort = 4427;
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
desc.m_enableDisconnectDetection = false;
|
|
if (i == s2)
|
|
{
|
|
desc.m_simulator = &clientSimulator;
|
|
}
|
|
|
|
// initialize replica managers
|
|
// s2(p)<-->(p)s1(p)<-->(c)s3
|
|
sessions[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
sessions[i].AcceptConn(true);
|
|
sessions[i].SetClient(i == s3);
|
|
sessions[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1, sessions[i].GetTransport(), 0, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0));
|
|
sessions[i].GetReplicaMgr().RegisterUserContext(12345, reinterpret_cast<void*>(static_cast<size_t>(i + 1)));
|
|
}
|
|
sessions[0].GetReplicaMgr().SetLocalLagAmt(50);
|
|
|
|
// put something on s1 to get it going
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
s1rep1 = CreateAndAttachReplicaChunk<MigratableReplica>(rep);
|
|
s1rep1id = sessions[s1].GetReplicaMgr().AddPrimary(rep);
|
|
s1rep1->Bind(s1obj1 = aznew MyObj());
|
|
|
|
// connect s2 to s1
|
|
sessions[s2].GetTransport()->Connect("127.0.0.1", basePort);
|
|
|
|
// main test loop
|
|
static bool keepRunning = true;
|
|
int tick = 0;
|
|
while (keepRunning)
|
|
{
|
|
// perform some random actions on a timeline
|
|
switch (tick)
|
|
{
|
|
case 5:
|
|
{
|
|
// connect s3 to s1
|
|
sessions[s3].GetTransport()->Connect("127.0.0.1", basePort);
|
|
break;
|
|
}
|
|
case 25:
|
|
// remove s1rep1
|
|
AZ_TEST_ASSERT(s1rep1id);
|
|
s1rep1->GetReplica()->Destroy();
|
|
s1obj1 = NULL;
|
|
break;
|
|
case 35:
|
|
//AZ_TracePrintf("GridMate", "No more updates.\n");
|
|
break;
|
|
case 70:
|
|
//AZ_TracePrintf("GridMate", "Restart updates.\n");
|
|
break;
|
|
case 90:
|
|
keepRunning = false;
|
|
}
|
|
|
|
// add an object on s2
|
|
if (sessions[s2].GetReplicaMgr().IsReady())
|
|
{
|
|
if (!s2rep1id)
|
|
{
|
|
auto newReplica = Replica::CreateReplica(nullptr);
|
|
s2rep1 = CreateAndAttachReplicaChunk<MyDerivedReplica>(newReplica);
|
|
s2rep1id = sessions[s2].GetReplicaMgr().AddPrimary(newReplica);
|
|
s2rep1->Bind(s2obj1 = aznew MyObj());
|
|
}
|
|
else
|
|
{
|
|
static bool sends2rep1rpc = true;
|
|
if (sends2rep1rpc && tick >= 20)
|
|
{
|
|
s2rep1->MyHandler123Rpc(5.f);
|
|
s2rep1->MyHandler2Rpc(6.0f, 1);
|
|
s2rep1->MyHandler3Rpc(7.0f, 2, NonMigratableReplica::e_Bla0);
|
|
NonMigratableReplica::IntVectorType v;
|
|
v.push_back(10);
|
|
v.push_back(13);
|
|
s2rep1->MyHandler4Rpc(8.0f, 3, NonMigratableReplica::e_Bla1, v);
|
|
sends2rep1rpc = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add object on s1
|
|
if (sessions[s1].GetReplicaMgr().IsReady())
|
|
{
|
|
if (!s1rep2)
|
|
{
|
|
auto newReplica = Replica::CreateReplica(nullptr);
|
|
s1rep2 = CreateAndAttachReplicaChunk<NonMigratableReplica>(newReplica);
|
|
s1rep2id = sessions[s1].GetReplicaMgr().AddPrimary(newReplica);
|
|
s1rep2->Bind(s1obj2 = aznew MyObj);
|
|
}
|
|
else
|
|
{
|
|
if (s1rep2id && tick >= 40)
|
|
{
|
|
s1rep2->GetReplica()->Destroy();
|
|
s1obj2 = NULL;
|
|
s1rep2id = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add object on s3
|
|
if (sessions[s3].GetReplicaMgr().IsReady())
|
|
{
|
|
if (!s3rep1)
|
|
{
|
|
auto newReplica = Replica::CreateReplica(nullptr);
|
|
s3rep1 = CreateAndAttachReplicaChunk<MigratableReplica>(newReplica);
|
|
s3rep1id = sessions[s3].GetReplicaMgr().AddPrimary(newReplica);
|
|
s3rep1->Bind(s3obj1 = aznew MyObj());
|
|
}
|
|
else
|
|
{
|
|
if (s3rep1id && tick >= 45)
|
|
{
|
|
s3rep1->MyHandler123Rpc(-1.f);
|
|
s3rep1->GetReplica()->Destroy();
|
|
s3obj1 = NULL;
|
|
s3rep1id = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
{ // Testing unreliable rpcs: enabling network simulator with outgoing packetloss,
|
|
// calling 10 rpcs with 1..10 int argument, checking if replicas got rpcs in an order, and have missing calls
|
|
static bool requestrpc = true;
|
|
if (s3rep1id && requestrpc)
|
|
{
|
|
if (ReplicaPtr pObj = sessions[s2].GetReplicaMgr().FindReplica(s3rep1id))
|
|
{
|
|
pObj->FindReplicaChunk<MigratableReplica>()->MyHandler123Rpc(2.0f);
|
|
requestrpc = false;
|
|
}
|
|
}
|
|
|
|
static bool unreliableRequest = true;
|
|
static int numUnreliableRequests = 0;
|
|
if (sessions[s2].GetReplicaMgr().IsReady() && s2rep1 && tick > 15 && unreliableRequest)
|
|
{
|
|
// Starting packet loss
|
|
unreliableRequest = false;
|
|
}
|
|
|
|
if (!unreliableRequest && numUnreliableRequests < 10)
|
|
{
|
|
if (numUnreliableRequests == 4)
|
|
{
|
|
clientSimulator.Enable();
|
|
}
|
|
else if (numUnreliableRequests == 5)
|
|
{
|
|
clientSimulator.Disable();
|
|
}
|
|
s2rep1->MyHandlerUnreliableRpc(++numUnreliableRequests);
|
|
}
|
|
|
|
static bool checkUnreliableDelivery = true;
|
|
if (checkUnreliableDelivery && tick >= 25)
|
|
{
|
|
// Stopping packet loss
|
|
ReplicaPtr rep1 = sessions[s1].GetReplicaMgr().FindReplica(s2rep1->GetReplicaId());
|
|
ReplicaPtr rep3 = sessions[s3].GetReplicaMgr().FindReplica(s2rep1->GetReplicaId());
|
|
AZ_TEST_ASSERT(rep1);
|
|
AZ_TEST_ASSERT(rep3);
|
|
AZ_TEST_ASSERT(rep1->FindReplicaChunk<MyDerivedReplica>()->m_unreliableCheck);
|
|
AZ_TEST_ASSERT(rep3->FindReplicaChunk<MyDerivedReplica>()->m_unreliableCheck);
|
|
checkUnreliableDelivery = false;
|
|
}
|
|
}
|
|
|
|
// modify local objects
|
|
if (tick < 20 || tick > 70)
|
|
{
|
|
if (s1obj1)
|
|
{
|
|
s1obj1->m_f1 += 0.5f;
|
|
s1obj1->m_i1 += 1;
|
|
s1obj1->m_b1 = !s1obj1->m_b1;
|
|
}
|
|
if (s1obj2)
|
|
{
|
|
s1obj2->m_f1 += 1.0f;
|
|
s1obj2->m_i1 -= 1;
|
|
s1obj2->m_b1 = !s1obj2->m_b1;
|
|
}
|
|
if (s2obj1)
|
|
{
|
|
s2obj1->m_f1 += 0.1f;
|
|
s2obj1->m_i1 += 2;
|
|
s2obj1->m_b1 = !s2obj1->m_b1;
|
|
}
|
|
if (s3obj1)
|
|
{
|
|
s3obj1->m_f1 += 0.3f;
|
|
s3obj1->m_i1 += 3;
|
|
s3obj1->m_b1 = !s3obj1->m_b1;
|
|
}
|
|
}
|
|
++tick;
|
|
// tick everything
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].Update();
|
|
sessions[i].GetReplicaMgr().Unmarshal();
|
|
}
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().UpdateReplicas();
|
|
}
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().UpdateFromReplicas();
|
|
sessions[i].GetReplicaMgr().Marshal();
|
|
}
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetTransport()->Update();
|
|
}
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(k_delay));
|
|
}
|
|
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(sessions[i].GetTransport());
|
|
}
|
|
}
|
|
|
|
class Integ_ForcedReplicaMigrationTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
, public ReplicaMgrCallbackBus::Handler
|
|
, public MigratableReplica::MigratableReplicaDebugMsgs::EBus::Handler
|
|
, public ::testing::Test
|
|
{
|
|
void OnNewHost(bool isHost, ReplicaManager* pMgr) override
|
|
{
|
|
if (isHost)
|
|
{
|
|
AZ_TracePrintf("GridMate", "Peer %d has completed host migration and is now the host.\n", (int)pMgr->GetLocalPeerId());
|
|
pMgr->SetSendTimeInterval(k_hostSendRateMs);
|
|
m_newHostEventOnNewHostCount++;
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf("GridMate", "Peer %d has has received notification that host migration is complete.\n", (int)pMgr->GetLocalPeerId());
|
|
pMgr->SetSendTimeInterval(0);
|
|
m_newHostEventOnPeersCount++;
|
|
}
|
|
}
|
|
|
|
void OnNewOwner(ReplicaId repId, ReplicaManager* repMgr) override
|
|
{
|
|
AZ_TracePrintf("GridMate", "Replica 0x%08x got new owner %d on frame %d.\n", repId, (int)repMgr->GetLocalPeerId(), m_frameCount);
|
|
m_replicaOwnership[repId] = repMgr;
|
|
}
|
|
|
|
public:
|
|
Integ_ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); }
|
|
~Integ_ForcedReplicaMigrationTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); }
|
|
|
|
|
|
enum
|
|
{
|
|
p1, p2, p3, p4, p5, nPeers
|
|
};
|
|
|
|
static const int k_frameTimePerNodeMs = 10;
|
|
static const int k_numFramesToRun = 300;
|
|
static const int k_hostSendRateMs = k_frameTimePerNodeMs * nPeers * 2; // limiting host send rate x2 times
|
|
|
|
int m_frameCount;
|
|
int m_newHostEventOnNewHostCount;
|
|
int m_newHostEventOnPeersCount;
|
|
AZStd::unordered_map<ReplicaId, ReplicaManager*> m_replicaOwnership;
|
|
};
|
|
|
|
const int Integ_ForcedReplicaMigrationTest::k_frameTimePerNodeMs;
|
|
const int Integ_ForcedReplicaMigrationTest::k_numFramesToRun;
|
|
const int Integ_ForcedReplicaMigrationTest::k_hostSendRateMs;
|
|
|
|
TEST_F(Integ_ForcedReplicaMigrationTest, ForcedReplicaMigrationTest)
|
|
{
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MigratableReplica, MigratableReplica::Descriptor>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<NonMigratableReplica>();
|
|
|
|
MPSession peers[nPeers];
|
|
MigratableReplica::Ptr migrRep[nPeers];
|
|
NonMigratableReplica::Ptr nonMigrRep[nPeers];
|
|
|
|
m_newHostEventOnNewHostCount = 0;
|
|
m_newHostEventOnPeersCount = 0;
|
|
|
|
MigratableReplica::MigratableReplicaDebugMsgs::EBus::Handler::BusConnect();
|
|
|
|
// initialize full-mesh P2P session
|
|
int basePort = 4427;
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
desc.m_enableDisconnectDetection = /*false*/ true;
|
|
desc.m_threadUpdateTimeMS = k_frameTimePerNodeMs / 2;
|
|
|
|
// initialize replica managers
|
|
peers[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
peers[i].AcceptConn(true);
|
|
peers[i].SetClient(false);
|
|
peers[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1
|
|
, peers[i].GetTransport()
|
|
, 0
|
|
, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0
|
|
, i == 0 ? k_hostSendRateMs : 0));
|
|
}
|
|
|
|
AZ_TracePrintf("GridMate", "\n");
|
|
m_frameCount = 0;
|
|
while (m_frameCount < k_numFramesToRun)
|
|
{
|
|
static bool allReady = false;
|
|
// establish all connections
|
|
if (m_frameCount < nPeers)
|
|
{
|
|
for (int i = 0; i < m_frameCount; ++i)
|
|
{
|
|
peers[m_frameCount].GetTransport()->Connect("127.0.0.1", basePort + i);
|
|
}
|
|
}
|
|
|
|
if (!allReady)
|
|
{
|
|
allReady = true;
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
if (!peers[i].GetReplicaMgr().IsReady())
|
|
{
|
|
allReady = false;
|
|
}
|
|
}
|
|
if (allReady)
|
|
{
|
|
AZ_TracePrintf("GridMate", "All peers ready at frame %d\n", m_frameCount);
|
|
}
|
|
}
|
|
|
|
// perform tests
|
|
if (allReady)
|
|
{
|
|
// add replicas
|
|
static bool addReplicas = true;
|
|
if (addReplicas)
|
|
{
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
migrRep[i] = CreateAndAttachReplicaChunk<MigratableReplica>(rep, aznew MyObj());
|
|
peers[i].GetReplicaMgr().AddPrimary(rep);
|
|
AZ_TEST_ASSERT(m_replicaOwnership[migrRep[i]->GetReplicaId()] == &peers[i].GetReplicaMgr());
|
|
}
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
nonMigrRep[i] = CreateAndAttachReplicaChunk<NonMigratableReplica>(rep, aznew MyObj());
|
|
peers[i].GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
}
|
|
addReplicas = false;
|
|
AZ_TracePrintf("GridMate", "Replicas added at frame %d\n", m_frameCount);
|
|
}
|
|
|
|
// disconnect p3 and trigger peer migration
|
|
static bool dropP3 = true;
|
|
if (m_frameCount > 50 && dropP3)
|
|
{
|
|
peers[p3].GetTransport()->Disconnect(AllConnections);
|
|
dropP3 = false;
|
|
AZ_TracePrintf("GridMate", "Dropped P3 at frame %d\n", m_frameCount);
|
|
}
|
|
|
|
// Check that p3's MigratableReplica has migrated to p1 (host)
|
|
if (m_frameCount == 85)
|
|
{
|
|
AZ_TEST_ASSERT(m_replicaOwnership[migrRep[p3]->GetReplicaId()] == &peers[p1].GetReplicaMgr());
|
|
}
|
|
|
|
// disconnect p1 and trigger host loss
|
|
static bool dropP1 = true;
|
|
if (m_frameCount > 100 && dropP1)
|
|
{
|
|
peers[p1].GetTransport()->Disconnect(AllConnections);
|
|
dropP1 = false;
|
|
AZ_TracePrintf("GridMate", "Dropped P1 at frame %d\n", m_frameCount);
|
|
}
|
|
|
|
// promote p2 to host
|
|
static bool promoteP2 = true;
|
|
if (m_frameCount > 150 && promoteP2)
|
|
{
|
|
peers[p2].GetReplicaMgr().Promote();
|
|
promoteP2 = false;
|
|
AZ_TracePrintf("GridMate", "Promoted P2 at frame %d\n", m_frameCount);
|
|
}
|
|
}
|
|
|
|
// tick
|
|
int tickPeer = m_frameCount++ % nPeers;
|
|
peers[tickPeer].Update();
|
|
peers[tickPeer].GetReplicaMgr().Unmarshal();
|
|
peers[tickPeer].GetReplicaMgr().UpdateReplicas();
|
|
peers[tickPeer].GetReplicaMgr().UpdateFromReplicas();
|
|
peers[tickPeer].GetReplicaMgr().Marshal();
|
|
peers[tickPeer].GetTransport()->Update();
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(k_frameTimePerNodeMs));
|
|
}
|
|
|
|
// Check that p1's MigratableReplicas (including the one from p3) have migrated to p2 (host)
|
|
AZ_TEST_ASSERT(m_replicaOwnership[migrRep[p1]->GetReplicaId()] == &peers[p2].GetReplicaMgr());
|
|
AZ_TEST_ASSERT(m_replicaOwnership[migrRep[p3]->GetReplicaId()] == &peers[p2].GetReplicaMgr());
|
|
|
|
AZ_TEST_ASSERT(m_newHostEventOnNewHostCount == 1); // New host should have received OnNewHost event
|
|
AZ_TEST_ASSERT(m_newHostEventOnPeersCount == 2); // 2 peers remaining should have received OnNewHost event
|
|
|
|
// clean up
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
peers[i].GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(peers[i].GetTransport());
|
|
}
|
|
|
|
MigratableReplica::MigratableReplicaDebugMsgs::EBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
class Integ_ReplicaMigrationRequestTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
, public ::testing::Test
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
Host,
|
|
Peer1,
|
|
Peer2,
|
|
Client1,
|
|
Client2,
|
|
TotalNodes
|
|
};
|
|
|
|
class AlwaysMigratable
|
|
: public ReplicaChunk
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(AlwaysMigratable);
|
|
typedef AZStd::intrusive_ptr<AlwaysMigratable> Ptr;
|
|
static const char* GetChunkName() { return "AlwaysMigratable"; }
|
|
|
|
AlwaysMigratable()
|
|
: UpdateControlValue("UpdateControlValue")
|
|
, m_requests(0)
|
|
, m_accepted(0)
|
|
, m_triggerNextTransfer(false)
|
|
, m_owner("Owner")
|
|
, m_control("Control")
|
|
{
|
|
}
|
|
|
|
bool IsReplicaMigratable() override
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool AcceptChangeOwnership(PeerId requestor, const ReplicaContext& rc) override
|
|
{
|
|
AZ_TracePrintf("GridMate", "Node %d accepted transfer of AlwaysMigratable 0x%x to node %d.\n", rc.m_rm->GetLocalPeerId() - 1, GetReplicaId(), requestor - 1);
|
|
m_requests++;
|
|
m_accepted++;
|
|
|
|
if (rc.m_rm->GetLocalPeerId() - 1 == Peer2 && requestor - 1 == Host)
|
|
{
|
|
m_triggerNextTransfer = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OnReplicaActivate(const ReplicaContext& rc) override
|
|
{
|
|
if (IsPrimary())
|
|
{
|
|
m_owner.Set(rc.m_rm->GetLocalPeerId() - 1);
|
|
m_control.Set(rc.m_rm->GetLocalPeerId() - 1);
|
|
}
|
|
}
|
|
|
|
void OnReplicaChangeOwnership(const ReplicaContext& rc) override
|
|
{
|
|
if (IsPrimary())
|
|
{
|
|
AZ_TracePrintf("GridMate", "OnChangeOwnership: 0x%04x Became primary on node %d\n", GetReplicaId(), rc.m_rm->GetLocalPeerId() - 1);
|
|
m_owner.Set(rc.m_rm->GetLocalPeerId() - 1);
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf("GridMate", "OnChangeOwnership: 0x%04x Became proxy on node %d\n", GetReplicaId(), rc.m_rm->GetLocalPeerId() - 1);
|
|
if (m_triggerNextTransfer)
|
|
{
|
|
GetReplica()->RequestChangeOwnership(Client2 + 1);
|
|
m_triggerNextTransfer = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UpdateControlValueFn(const RpcContext& rc)
|
|
{
|
|
(void)rc;
|
|
m_control.Set(GetReplicaManager()->GetLocalPeerId() - 1);
|
|
return false;
|
|
}
|
|
Rpc<>::BindInterface<AlwaysMigratable, & AlwaysMigratable::UpdateControlValueFn> UpdateControlValue;
|
|
|
|
int m_requests;
|
|
int m_accepted;
|
|
bool m_triggerNextTransfer;
|
|
|
|
DataSet<int> m_owner;
|
|
DataSet<int> m_control;
|
|
};
|
|
|
|
class NeverMigratable
|
|
: public AlwaysMigratable
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(NeverMigratable);
|
|
typedef AZStd::intrusive_ptr<NeverMigratable> Ptr;
|
|
static const char* GetChunkName() { return "NeverMigratable"; }
|
|
|
|
NeverMigratable()
|
|
{
|
|
}
|
|
|
|
bool IsReplicaMigratable() override
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class SometimesMigratable
|
|
: public AlwaysMigratable
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(SometimesMigratable);
|
|
typedef AZStd::intrusive_ptr<SometimesMigratable> Ptr;
|
|
static const char* GetChunkName() { return "SometimesMigratable"; }
|
|
|
|
SometimesMigratable()
|
|
{
|
|
m_acceptMigrationRequests = false;
|
|
}
|
|
|
|
virtual bool AcceptChangeOwnership(PeerId requestor, const ReplicaContext& rc) override
|
|
{
|
|
(void)requestor;
|
|
(void)rc;
|
|
m_requests++;
|
|
if (m_acceptMigrationRequests)
|
|
{
|
|
AZ_TracePrintf("GridMate", "Node %d accepted transfer of SometimesMigratable 0x%x to node %d.\n", rc.m_rm->GetLocalPeerId() - 1, GetReplicaId(), requestor - 1);
|
|
m_accepted++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool m_acceptMigrationRequests;
|
|
};
|
|
|
|
struct Node
|
|
{
|
|
MPSession m_session;
|
|
AlwaysMigratable::Ptr m_always;
|
|
NeverMigratable::Ptr m_never;
|
|
SometimesMigratable::Ptr m_sometimes;
|
|
};
|
|
|
|
|
|
static const int k_frameTimePerNodeMs = 10;
|
|
static const int k_hostSendTimeMs = k_frameTimePerNodeMs * TotalNodes * 4; // limiting host send rate to be x4 times slower than tick
|
|
};
|
|
|
|
TEST_F(Integ_ReplicaMigrationRequestTest, ReplicaMigrationRequestTest)
|
|
{
|
|
/*
|
|
Topology:
|
|
P1---P2
|
|
\ /
|
|
\ /
|
|
H
|
|
/ \
|
|
/ \
|
|
C1 C2
|
|
|
|
Migration pattern:
|
|
AlwaysMigratable:
|
|
P1 -> P2
|
|
P2 -> Host -> C2 (both at same time, with C2 arriving second)
|
|
Host -> C1
|
|
C1 -> C2 -> Host
|
|
C2 -> P1
|
|
NeverMigratable:
|
|
P1 -> C1 (Forbidden)
|
|
C2 -> P2 (Forbidden)
|
|
SometimesMigratable:
|
|
P1 -> Host
|
|
C1 -> P1
|
|
P2 -> C2 (Forbidden)
|
|
C2 -> Host (Forbidden)
|
|
*/
|
|
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<AlwaysMigratable>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<NeverMigratable>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<SometimesMigratable>();
|
|
|
|
Node nodes[TotalNodes];
|
|
int basePort = 4427;
|
|
for (int i = 0; i < TotalNodes; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
desc.m_connectionTimeoutMS = 15000;
|
|
// initialize replica managers
|
|
nodes[i].m_session.SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
nodes[i].m_session.AcceptConn(true);
|
|
nodes[i].m_session.SetClient(i == Client1 || i == Client2);
|
|
nodes[i].m_session.GetReplicaMgr().Init(ReplicaMgrDesc(i + 1
|
|
, nodes[i].m_session.GetTransport()
|
|
, 0
|
|
, i == Host ? ReplicaMgrDesc::Role_SyncHost : 0
|
|
, i == Host ? k_hostSendTimeMs : 0));
|
|
}
|
|
|
|
// Connect all the nodes
|
|
nodes[Peer1].m_session.GetTransport()->Connect("127.0.0.1", basePort + Host);
|
|
nodes[Peer2].m_session.GetTransport()->Connect("127.0.0.1", basePort + Host);
|
|
nodes[Client1].m_session.GetTransport()->Connect("127.0.0.1", basePort + Host);
|
|
nodes[Client2].m_session.GetTransport()->Connect("127.0.0.1", basePort + Host);
|
|
nodes[Peer1].m_session.GetTransport()->Connect("127.0.0.1", basePort + Peer2);
|
|
|
|
|
|
int framesToRun = 800;
|
|
for (int iTick = 0; iTick < framesToRun; ++iTick)
|
|
{
|
|
for (int iNode = 0; iNode < TotalNodes; ++iNode)
|
|
{
|
|
if (nodes[iNode].m_session.GetReplicaMgr().IsReady())
|
|
{
|
|
if (nodes[iNode].m_always == nullptr)
|
|
{
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
nodes[iNode].m_always = CreateAndAttachReplicaChunk<AlwaysMigratable>(rep);
|
|
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
nodes[iNode].m_never = CreateAndAttachReplicaChunk<NeverMigratable>(rep);
|
|
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
nodes[iNode].m_sometimes = CreateAndAttachReplicaChunk<SometimesMigratable>(rep);
|
|
nodes[iNode].m_sometimes->m_acceptMigrationRequests = iNode == Peer1 || iNode == Client1;
|
|
nodes[iNode].m_session.GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// First round of migrations
|
|
if (iTick == 200)
|
|
{
|
|
// P1 -> P2
|
|
ReplicaPtr aP1onP2 = nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aP1onP2);
|
|
aP1onP2->RequestChangeOwnership();
|
|
|
|
// P2 -> Host -> C2 (both at same time, with C2 arriving second)
|
|
ReplicaPtr aP2onH = nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aP2onH);
|
|
aP2onH->RequestChangeOwnership();
|
|
|
|
// Host -> C1
|
|
ReplicaPtr aHonC1 = nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Host].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aHonC1);
|
|
aHonC1->RequestChangeOwnership();
|
|
|
|
// C1 -> C2 -> Host (first migration)
|
|
ReplicaPtr aC1onC2 = nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aC1onC2);
|
|
aC1onC2->RequestChangeOwnership();
|
|
|
|
// C2 -> P1
|
|
ReplicaPtr aC2onP1 = nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aC2onP1);
|
|
aC2onP1->RequestChangeOwnership();
|
|
|
|
// P1 -> C1 (Forbidden)
|
|
ReplicaPtr nP1onC1 = nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_never->GetReplicaId());
|
|
AZ_TEST_ASSERT(nP1onC1);
|
|
nP1onC1->RequestChangeOwnership();
|
|
|
|
// C2 -> P2 (Forbidden)
|
|
ReplicaPtr nC2onP2 = nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_never->GetReplicaId());
|
|
AZ_TEST_ASSERT(nC2onP2);
|
|
nC2onP2->RequestChangeOwnership();
|
|
|
|
// P1 -> Host
|
|
ReplicaPtr sP1onH = nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_sometimes->GetReplicaId());
|
|
AZ_TEST_ASSERT(sP1onH);
|
|
sP1onH->RequestChangeOwnership();
|
|
|
|
// C1 -> P1
|
|
ReplicaPtr sC1onP1 = nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_sometimes->GetReplicaId());
|
|
AZ_TEST_ASSERT(sC1onP1);
|
|
sC1onP1->RequestChangeOwnership();
|
|
|
|
// P2 -> C2 (Forbidden)
|
|
ReplicaPtr sP2onC2 = nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_sometimes->GetReplicaId());
|
|
AZ_TEST_ASSERT(sP2onC2);
|
|
sP2onC2->RequestChangeOwnership();
|
|
|
|
// C2 -> Host (Forbidden)
|
|
ReplicaPtr sC2onH = nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_sometimes->GetReplicaId());
|
|
AZ_TEST_ASSERT(sC2onH);
|
|
sC2onH->RequestChangeOwnership();
|
|
}
|
|
|
|
// Second round of migrations
|
|
if (iTick == 400)
|
|
{
|
|
// C1 -> C2 -> Host (1st migration)
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_owner.Get() == Client2);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsPrimary());
|
|
|
|
// C1 -> C2 -> Host (2nd migration)
|
|
ReplicaPtr aHonC1 = nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aHonC1);
|
|
aHonC1->RequestChangeOwnership();
|
|
}
|
|
|
|
// Send non-authoritative control RPCs
|
|
if (iTick == 600)
|
|
{
|
|
// P1 -> P2
|
|
AlwaysMigratable::Ptr aP1onH = nodes[Host].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Peer1].m_always->GetReplicaId());
|
|
aP1onH->UpdateControlValue();
|
|
// P2 -> Host -> C2 (both at same time, with C2 arriving second)
|
|
AlwaysMigratable::Ptr aP2onC1 = nodes[Client1].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Peer2].m_always->GetReplicaId());
|
|
aP2onC1->UpdateControlValue();
|
|
// Host -> C1
|
|
AlwaysMigratable::Ptr aHonP1 = nodes[Peer1].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Host].m_always->GetReplicaId());
|
|
aHonP1->UpdateControlValue();
|
|
// C1 -> C2 -> Host
|
|
AlwaysMigratable::Ptr aC1onP2 = nodes[Peer2].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Client1].m_always->GetReplicaId());
|
|
aC1onP2->UpdateControlValue();
|
|
// C2 -> P1
|
|
AlwaysMigratable::Ptr aC2onP2 = nodes[Peer2].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Client2].m_always->GetReplicaId());
|
|
aC2onP2->UpdateControlValue();
|
|
// P1 -> C1 (Forbidden)
|
|
NeverMigratable::Ptr nP1onH = nodes[Host].m_session.GetChunkFromReplica<NeverMigratable>(nodes[Peer1].m_never->GetReplicaId());
|
|
nP1onH->UpdateControlValue();
|
|
// C2 -> P2 (Forbidden)
|
|
NeverMigratable::Ptr nC2onP1 = nodes[Peer1].m_session.GetChunkFromReplica<NeverMigratable>(nodes[Client2].m_never->GetReplicaId());
|
|
nC2onP1->UpdateControlValue();
|
|
// P1 -> Host
|
|
SometimesMigratable::Ptr sP1onH = nodes[Host].m_session.GetChunkFromReplica<SometimesMigratable>(nodes[Peer1].m_sometimes->GetReplicaId());
|
|
sP1onH->UpdateControlValue();
|
|
// C1 -> P1
|
|
SometimesMigratable::Ptr sC1onC2 = nodes[Client2].m_session.GetChunkFromReplica<SometimesMigratable>(nodes[Client1].m_sometimes->GetReplicaId());
|
|
sC1onC2->UpdateControlValue();
|
|
// P2 -> C2 (Forbidden)
|
|
SometimesMigratable::Ptr sP2onC2 = nodes[Client2].m_session.GetChunkFromReplica<SometimesMigratable>(nodes[Peer2].m_sometimes->GetReplicaId());
|
|
sP2onC2->UpdateControlValue();
|
|
// C2 -> Host (Forbidden)
|
|
SometimesMigratable::Ptr sC2onP1 = nodes[Peer1].m_session.GetChunkFromReplica<SometimesMigratable>(nodes[Client2].m_sometimes->GetReplicaId());
|
|
sC2onP1->UpdateControlValue();
|
|
}
|
|
|
|
// tick
|
|
int tickNode = iTick % TotalNodes;
|
|
nodes[tickNode].m_session.Update();
|
|
nodes[tickNode].m_session.GetReplicaMgr().Unmarshal();
|
|
nodes[tickNode].m_session.GetReplicaMgr().UpdateFromReplicas();
|
|
nodes[tickNode].m_session.GetReplicaMgr().UpdateReplicas();
|
|
nodes[tickNode].m_session.GetReplicaMgr().Marshal();
|
|
nodes[tickNode].m_session.GetTransport()->Update();
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(k_frameTimePerNodeMs));
|
|
}
|
|
|
|
// P1 -> P2
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_owner.Get() == Peer2);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_always->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_always->m_control.Get() == Peer2);
|
|
|
|
// P2 -> Host -> C2 (both at same time, with C2 arriving second)
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_always->m_owner.Get() == Client2);
|
|
AlwaysMigratable::Ptr aP2onH = nodes[Host].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Peer2].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aP2onH->m_requests == 1);
|
|
AZ_TEST_ASSERT(aP2onH->m_accepted == 1);
|
|
AZ_TEST_ASSERT(aP2onH->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(aP2onH->m_owner.Get() == Client2);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_always->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_always->m_control.Get() == Client2);
|
|
|
|
// Host -> C1
|
|
AZ_TEST_ASSERT(nodes[Host].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Host].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Host].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Host].m_always->m_owner.Get() == Client1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Host].m_always->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Host].m_always->m_control.Get() == Client1);
|
|
|
|
// C1 -> C2 -> Host (2nd migration)
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_owner.Get() == Host);
|
|
AlwaysMigratable::Ptr aC1onC2 = nodes[Client2].m_session.GetChunkFromReplica<AlwaysMigratable>(nodes[Client1].m_always->GetReplicaId());
|
|
AZ_TEST_ASSERT(aC1onC2->m_requests == 1);
|
|
AZ_TEST_ASSERT(aC1onC2->m_accepted == 1);
|
|
AZ_TEST_ASSERT(aC1onC2->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(aC1onC2->m_owner.Get() == Host);
|
|
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_always->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Client1].m_always->m_control.Get() == Host);
|
|
|
|
// C2 -> P1
|
|
AZ_TEST_ASSERT(nodes[Client2].m_always->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_always->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_always->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_always->m_owner.Get() == Peer1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_always->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_always->m_control.Get() == Peer1);
|
|
|
|
// P1 -> C1 (Forbidden)
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_requests == 0);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_accepted == 0);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_never->GetReplica()->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_owner.Get() == Peer1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_never->GetReplicaId())->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_never->m_control.Get() == Peer1);
|
|
|
|
// C2 -> P2 (Forbidden)
|
|
AZ_TEST_ASSERT(nodes[Client2].m_never->m_requests == 0);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_never->m_accepted == 0);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_never->GetReplica()->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_never->m_owner.Get() == Client2);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_never->GetReplicaId())->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_never->m_control.Get() == Client2);
|
|
|
|
// P1 -> Host
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_owner.Get() == Host);
|
|
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Peer1].m_sometimes->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_sometimes->m_control.Get() == Host);
|
|
|
|
// C1 -> P1
|
|
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_accepted == 1);
|
|
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->GetReplica()->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_owner.Get() == Peer1);
|
|
AZ_TEST_ASSERT(nodes[Peer1].m_session.GetReplicaMgr().FindReplica(nodes[Client1].m_sometimes->GetReplicaId())->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Client1].m_sometimes->m_control.Get() == Peer1);
|
|
|
|
// P2 -> C2 (Forbidden)
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_accepted == 0);
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->GetReplica()->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_owner.Get() == Peer2);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_session.GetReplicaMgr().FindReplica(nodes[Peer2].m_never->GetReplicaId())->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Peer2].m_sometimes->m_control.Get() == Peer2);
|
|
|
|
// C2 -> Host (Forbidden)
|
|
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_requests == 1);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_accepted == 0);
|
|
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->GetReplica()->IsPrimary());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_owner.Get() == Client2);
|
|
AZ_TEST_ASSERT(nodes[Host].m_session.GetReplicaMgr().FindReplica(nodes[Client2].m_never->GetReplicaId())->IsProxy());
|
|
AZ_TEST_ASSERT(nodes[Client2].m_sometimes->m_control.Get() == Client2);
|
|
|
|
// clean up
|
|
for (int i = 0; i < TotalNodes; ++i)
|
|
{
|
|
nodes[i].m_always = nullptr;
|
|
nodes[i].m_never = nullptr;
|
|
nodes[i].m_sometimes = nullptr;
|
|
nodes[i].m_session.GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(nodes[i].m_session.GetTransport());
|
|
}
|
|
}
|
|
|
|
const int Integ_ReplicaMigrationRequestTest::k_frameTimePerNodeMs;
|
|
const int Integ_ReplicaMigrationRequestTest::k_hostSendTimeMs;
|
|
|
|
|
|
class Integ_PeerRejoinTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
, public ReplicaMgrCallbackBus::Handler
|
|
, public ::testing::Test
|
|
{
|
|
void OnNewHost(bool isHost, ReplicaManager* pMgr) override
|
|
{
|
|
(void)pMgr;
|
|
if (isHost)
|
|
{
|
|
AZ_TracePrintf("GridMate", "Peer %d has completed host migration and is now the host.\n", (int)pMgr->GetLocalPeerId());
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf("GridMate", "Peer %d has has received notification that host migration is complete.\n", (int)pMgr->GetLocalPeerId());
|
|
}
|
|
}
|
|
|
|
public:
|
|
Integ_PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusConnect(m_gridMate); }
|
|
~Integ_PeerRejoinTest() { ReplicaMgrCallbackBus::Handler::BusDisconnect(); }
|
|
};
|
|
|
|
TEST_F(Integ_PeerRejoinTest, PeerRejoinTest)
|
|
{
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MigratableReplica, MigratableReplica::Descriptor>();
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<NonMigratableReplica>();
|
|
|
|
int frameTime = 10;
|
|
int framesToRun = 300;
|
|
enum
|
|
{
|
|
p1, p2, nPeers
|
|
};
|
|
|
|
MPSession peers[nPeers];
|
|
MigratableReplica::Ptr migrRep[nPeers];
|
|
NonMigratableReplica::Ptr nonMigrRep[nPeers];
|
|
|
|
// initialize full-mesh P2P session
|
|
int basePort = 4427;
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
desc.m_enableDisconnectDetection = true;
|
|
desc.m_threadUpdateTimeMS = frameTime / 2;
|
|
|
|
// initialize replica managers
|
|
peers[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
peers[i].AcceptConn(true);
|
|
peers[i].SetClient(false);
|
|
peers[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1
|
|
, peers[i].GetTransport()
|
|
, 0
|
|
, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0
|
|
, frameTime / 2));
|
|
}
|
|
|
|
AZ_TracePrintf("GridMate", "\n");
|
|
int frameCount = 0;
|
|
while (frameCount < framesToRun)
|
|
{
|
|
static bool allReady = false;
|
|
// establish all connections
|
|
if (frameCount < nPeers)
|
|
{
|
|
for (int i = 0; i < frameCount; ++i)
|
|
{
|
|
peers[frameCount].GetTransport()->Connect("127.0.0.1", basePort + i);
|
|
}
|
|
}
|
|
|
|
if (!allReady)
|
|
{
|
|
allReady = true;
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
if (!peers[i].GetReplicaMgr().IsReady())
|
|
{
|
|
allReady = false;
|
|
}
|
|
}
|
|
if (allReady)
|
|
{
|
|
AZ_TracePrintf("GridMate", "All peers ready at frame %d\n", frameCount);
|
|
}
|
|
}
|
|
|
|
// perform tests
|
|
if (allReady)
|
|
{
|
|
// add replicas
|
|
static bool addReplicas = true;
|
|
if (addReplicas)
|
|
{
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
migrRep[i] = CreateAndAttachReplicaChunk<MigratableReplica>(rep, aznew MyObj());
|
|
peers[i].GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
nonMigrRep[i] = CreateAndAttachReplicaChunk<NonMigratableReplica>(rep, aznew MyObj());
|
|
peers[i].GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
}
|
|
addReplicas = false;
|
|
AZ_TracePrintf("GridMate", "Replicas added at frame %d\n", frameCount);
|
|
}
|
|
|
|
// disconnect p2 and trigger peer migration
|
|
static bool dropP2 = true;
|
|
if (frameCount > 50 && dropP2)
|
|
{
|
|
peers[p2].GetTransport()->Disconnect(AllConnections);
|
|
peers[p2].GetReplicaMgr().Shutdown();
|
|
dropP2 = false;
|
|
AZ_TracePrintf("GridMate", "Dropped P2 at frame %d\n", frameCount);
|
|
}
|
|
|
|
// reconnect p2
|
|
static bool reconP2 = true;
|
|
if (frameCount > 100 && reconP2)
|
|
{
|
|
peers[p2].GetReplicaMgr().Init(ReplicaMgrDesc(p2 + 1
|
|
, peers[p2].GetTransport()
|
|
, 0
|
|
, 0
|
|
, frameTime / 2));
|
|
peers[p1].GetTransport()->Connect("127.0.0.1", basePort + p2);
|
|
peers[p2].GetTransport()->Connect("127.0.0.1", basePort + p1);
|
|
reconP2 = false;
|
|
AZ_TracePrintf("GridMate", "Reconnected P2 at frame %d\n", frameCount);
|
|
}
|
|
|
|
// disconnect p2 again
|
|
static bool redropP2 = true;
|
|
if (frameCount > 150 && redropP2)
|
|
{
|
|
peers[p2].GetTransport()->Disconnect(AllConnections);
|
|
redropP2 = false;
|
|
AZ_TracePrintf("GridMate", "Re-Dropped P2 at frame %d\n", frameCount);
|
|
}
|
|
}
|
|
|
|
// tick
|
|
int tickPeer = frameCount++ % nPeers;
|
|
peers[tickPeer].Update();
|
|
if (peers[tickPeer].GetReplicaMgr().IsInitialized())
|
|
{
|
|
peers[tickPeer].GetReplicaMgr().Unmarshal();
|
|
peers[tickPeer].GetReplicaMgr().UpdateReplicas();
|
|
peers[tickPeer].GetReplicaMgr().UpdateFromReplicas();
|
|
peers[tickPeer].GetReplicaMgr().Marshal();
|
|
}
|
|
peers[tickPeer].GetTransport()->Update();
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(frameTime));
|
|
}
|
|
|
|
// clean up
|
|
for (int i = 0; i < nPeers; ++i)
|
|
{
|
|
peers[i].GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(peers[i].GetTransport());
|
|
}
|
|
}
|
|
|
|
class Integ_ReplicationSecurityOptionsTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
, public ::testing::Test
|
|
{
|
|
public:
|
|
enum
|
|
{
|
|
s1,
|
|
s2,
|
|
s3,
|
|
nSessions
|
|
};
|
|
|
|
class TestChunk : public ReplicaChunk
|
|
{
|
|
public:
|
|
struct ForwardSourcePeerTrait : public RpcDefaultTraits
|
|
{
|
|
static const bool s_alwaysForwardSourcePeer = true;
|
|
static const bool s_allowNonAuthoritativeRequestRelay = false;
|
|
};
|
|
|
|
struct DisableNonAuthoritativeRequestTrait : public RpcAuthoritativeTraits
|
|
{
|
|
static const bool s_alwaysForwardSourcePeer = true;
|
|
};
|
|
|
|
GM_CLASS_ALLOCATOR(TestChunk);
|
|
|
|
static const char* GetChunkName() { return "ReplicationSecurityOptionsTest::TestChunk"; }
|
|
|
|
TestChunk()
|
|
: m_nForwardSourcePeerRpcCallsFromS1("m_nForwardSourcePeerRpcCallsFromS1", 0)
|
|
, m_nForwardSourcePeerRpcCallsFromS2("m_nForwardSourcePeerRpcCallsFromS2", 0)
|
|
, m_nForwardSourcePeerRpcCallsFromS3("m_nForwardSourcePeerRpcCallsFromS3", 0)
|
|
, ForwardSourcePeerRpcFromS1("ForwardSourcePeerRpcFromS1")
|
|
, ForwardSourcePeerRpcFromS2("ForwardSourcePeerRpcFromS2")
|
|
, ForwardSourcePeerRpcFromS3("ForwardSourcePeerRpcFromS3")
|
|
, m_nAuthoritativeOnlyRpcCallsFromS1("m_nAuthoritativeOnlyRpcCallsFromS1", 0)
|
|
, m_nAuthoritativeOnlyRpcCallsFromS2("m_nAuthoritativeOnlyRpcCallsFromS2", 0)
|
|
, m_nAuthoritativeOnlyRpcCallsFromS3("m_nAuthoritativeOnlyRpcCallsFromS3", 0)
|
|
, m_nAuthoritativeOnlyProxyRpcCallsFromS1(0)
|
|
, m_nAuthoritativeOnlyProxyRpcCallsFromS2(0)
|
|
, m_nAuthoritativeOnlyProxyRpcCallsFromS3(0)
|
|
, AuthoritativeOnlyRpcFromS1("AuthoritativeOnlyRpcFromS1")
|
|
, AuthoritativeOnlyRpcFromS2("AuthoritativeOnlyRpcFromS2")
|
|
, AuthoritativeOnlyRpcFromS3("AuthoritativeOnlyRpcFromS3")
|
|
{
|
|
}
|
|
|
|
bool IsReplicaMigratable() override { return false; }
|
|
|
|
bool OnForwardSourcePeerRpcFromS1(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s1
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s1 + 1);
|
|
m_nForwardSourcePeerRpcCallsFromS1.Modify([](int& value) { ++value; return true; });
|
|
return false;
|
|
}
|
|
|
|
bool OnForwardSourcePeerRpcFromS2(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s2
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s2 + 1);
|
|
// requests to s3 should be blocked in this test
|
|
AZ_TEST_ASSERT(GetReplicaManager()->GetLocalPeerId() != s3 + 1);
|
|
m_nForwardSourcePeerRpcCallsFromS2.Modify([](int& value) { ++value; return true; });
|
|
return false;
|
|
}
|
|
|
|
bool OnForwardSourcePeerRpcFromS3(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s3
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s3 + 1);
|
|
// requests to s2 should be blocked in this test
|
|
AZ_TEST_ASSERT(GetReplicaManager()->GetLocalPeerId() != s2 + 1);
|
|
m_nForwardSourcePeerRpcCallsFromS3.Modify([](int& value) { ++value; return true; });
|
|
return false;
|
|
}
|
|
|
|
bool OnAuthoritativeOnlyRpcFromS1(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s1
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s1 + 1);
|
|
if (IsPrimary())
|
|
{
|
|
m_nAuthoritativeOnlyRpcCallsFromS1.Modify([](int& value) { ++value; return true; });
|
|
}
|
|
else
|
|
{
|
|
++m_nAuthoritativeOnlyProxyRpcCallsFromS1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OnAuthoritativeOnlyRpcFromS2(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s2
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s2 + 1);
|
|
if (IsPrimary())
|
|
{
|
|
m_nAuthoritativeOnlyRpcCallsFromS2.Modify([](int& value) { ++value; return true; });
|
|
}
|
|
else
|
|
{
|
|
++m_nAuthoritativeOnlyProxyRpcCallsFromS2;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OnAuthoritativeOnlyRpcFromS3(const RpcContext& rpcContext)
|
|
{
|
|
// make sure the requestor is set to s3
|
|
AZ_TEST_ASSERT(rpcContext.m_sourcePeer == s3 + 1);
|
|
if (IsPrimary())
|
|
{
|
|
m_nAuthoritativeOnlyRpcCallsFromS3.Modify([](int& value) { ++value; return true; });
|
|
}
|
|
else
|
|
{
|
|
++m_nAuthoritativeOnlyProxyRpcCallsFromS3;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DataSet<int> m_nForwardSourcePeerRpcCallsFromS1;
|
|
DataSet<int> m_nForwardSourcePeerRpcCallsFromS2;
|
|
DataSet<int> m_nForwardSourcePeerRpcCallsFromS3;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnForwardSourcePeerRpcFromS1, ForwardSourcePeerTrait> ForwardSourcePeerRpcFromS1;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnForwardSourcePeerRpcFromS2, ForwardSourcePeerTrait> ForwardSourcePeerRpcFromS2;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnForwardSourcePeerRpcFromS3, ForwardSourcePeerTrait> ForwardSourcePeerRpcFromS3;
|
|
|
|
DataSet<int> m_nAuthoritativeOnlyRpcCallsFromS1;
|
|
DataSet<int> m_nAuthoritativeOnlyRpcCallsFromS2;
|
|
DataSet<int> m_nAuthoritativeOnlyRpcCallsFromS3;
|
|
int m_nAuthoritativeOnlyProxyRpcCallsFromS1;
|
|
int m_nAuthoritativeOnlyProxyRpcCallsFromS2;
|
|
int m_nAuthoritativeOnlyProxyRpcCallsFromS3;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnAuthoritativeOnlyRpcFromS1, DisableNonAuthoritativeRequestTrait> AuthoritativeOnlyRpcFromS1;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnAuthoritativeOnlyRpcFromS2, DisableNonAuthoritativeRequestTrait> AuthoritativeOnlyRpcFromS2;
|
|
Rpc<>::BindInterface<TestChunk, &TestChunk::OnAuthoritativeOnlyRpcFromS3, DisableNonAuthoritativeRequestTrait> AuthoritativeOnlyRpcFromS3;
|
|
};
|
|
using TestChunkPtr = AZStd::intrusive_ptr<TestChunk> ;
|
|
};
|
|
|
|
TEST_F(Integ_ReplicationSecurityOptionsTest, ReplicationSecurityOptionsTest)
|
|
{
|
|
AZ_TracePrintf("GridMate", "\n");
|
|
|
|
// Register test chunks
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<TestChunk>();
|
|
|
|
MPSession sessions[nSessions];
|
|
ReplicaPtr primarys[nSessions];
|
|
|
|
// initialize transport
|
|
int basePort = 4427;
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
// initialize replica managers
|
|
// s2(c)<-->(p)s1(p)<-->(c)s3
|
|
sessions[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
sessions[i].AcceptConn(true);
|
|
sessions[i].SetClient(i != s1);
|
|
sessions[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1, sessions[i].GetTransport(), 0, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0));
|
|
|
|
ReplicationSecurityOptions options;
|
|
options.m_enableStrictSourceValidation = true;
|
|
sessions[i].GetReplicaMgr().SetSecurityOptions(options);
|
|
}
|
|
|
|
// connect s2 to s1
|
|
sessions[s2].GetTransport()->Connect("127.0.0.1", basePort);
|
|
|
|
// connect s3 to s1
|
|
sessions[s3].GetTransport()->Connect("127.0.0.1", basePort);
|
|
|
|
// main test loop
|
|
for (int tick = 0; tick < 1000; ++tick)
|
|
{
|
|
if (tick == 100)
|
|
{
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().IsReady());
|
|
primarys[i] = Replica::CreateReplica("ReplicationSecurityOptionsTest::TestReplica");
|
|
TestChunkPtr chunk = CreateReplicaChunk<TestChunk>();
|
|
primarys[i]->AttachReplicaChunk(chunk);
|
|
sessions[i].GetReplicaMgr().AddPrimary(primarys[i]);
|
|
}
|
|
}
|
|
|
|
if (tick == 200)
|
|
{
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[s1].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS1();
|
|
sessions[s2].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS2();
|
|
sessions[s3].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->ForwardSourcePeerRpcFromS3();
|
|
}
|
|
}
|
|
|
|
if (tick == 300)
|
|
{
|
|
// The previous test should have triggered the following assert twice:
|
|
// ReplicaChunk.cpp(449): AZ_Assert(false, "Discarding non-authoritative RPC <%s> because s_allowNonAuthoritativeRequestRelay trait is disabled!", GetDescriptor()->GetRpcName(this, rpc));
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(2);
|
|
|
|
// All chunks should have received the call from the host
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1 == 1);
|
|
|
|
// the host chunk should have received calls from both clients
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
|
|
|
|
// the chunk on s2 should receive its own call but not from s3
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 1);
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 0);
|
|
|
|
// the chunk on s3 should receive its own call but not from s2
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2 == 0);
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3 == 1);
|
|
|
|
// all datasets should have propagated properly
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
|
|
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
|
|
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nForwardSourcePeerRpcCallsFromS3.Get());
|
|
}
|
|
}
|
|
|
|
if (tick == 400)
|
|
{
|
|
AZ_TEST_START_TRACE_SUPPRESSION;
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[s1].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS1();
|
|
sessions[s2].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS2();
|
|
sessions[s3].GetReplicaMgr().FindReplica(primarys[i]->GetRepId())->FindReplicaChunk<TestChunk>()->AuthoritativeOnlyRpcFromS3();
|
|
}
|
|
}
|
|
|
|
if (tick == 500)
|
|
{
|
|
// The previous test should have triggered the following assert six times:
|
|
// ReplicaChunk.cpp(444): AZ_Assert(false, "Discarding non-authoritative RPC <%s> because s_allowNonAuthoritativeRequests trait is disabled!", GetDescriptor()->GetRpcName(this, rpc));
|
|
AZ_TEST_STOP_TRACE_SUPPRESSION(6);
|
|
|
|
// Each chunk should have received their own AuthoritativeOnlyRpc once.
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 1);
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 1);
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 1);
|
|
|
|
// Calls from other nodes should have been discarded.
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
|
|
AZ_TEST_ASSERT(primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
|
|
AZ_TEST_ASSERT(primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3 == 0);
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1 == 0);
|
|
AZ_TEST_ASSERT(primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2 == 0);
|
|
|
|
// Calls should have successfully propagated to the other 2 proxies
|
|
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
|
|
AZ_TEST_ASSERT(sessions[s1].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
|
|
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
|
|
AZ_TEST_ASSERT(sessions[s2].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS3 == 1);
|
|
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS1 == 1);
|
|
AZ_TEST_ASSERT(sessions[s3].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyProxyRpcCallsFromS2 == 1);
|
|
|
|
// all datasets should have propagated properly
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s1]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s1]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
|
|
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s2]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s2]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
|
|
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS1.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS2.Get());
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().FindReplica(primarys[s3]->GetRepId())->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get() == primarys[s3]->FindReplicaChunk<TestChunk>()->m_nAuthoritativeOnlyRpcCallsFromS3.Get());
|
|
}
|
|
}
|
|
|
|
// tick everything
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].Update();
|
|
sessions[i].GetReplicaMgr().Unmarshal();
|
|
sessions[i].GetReplicaMgr().UpdateReplicas();
|
|
sessions[i].GetReplicaMgr().UpdateFromReplicas();
|
|
sessions[i].GetReplicaMgr().Marshal();
|
|
sessions[i].GetTransport()->Update();
|
|
}
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
|
|
}
|
|
|
|
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(sessions[i].GetTransport());
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*03.25.2015* Typical test results on Core i7 3.4 GHz desktop (with polling):
|
|
Release:
|
|
Replica update time (msec): avg=2.02, min=2, max=3 (peers=40, replicas=16000, freq=0%, samples=4000)
|
|
Replica update time (msec): avg=4.83, min=2, max=12 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=4.73, min=3, max=12 (peers=40, replicas=16000, freq=100%, samples=4000)
|
|
DebugOpt:
|
|
Replica update time (msec): avg=2.53, min=2, max=5 (peers=40, replicas=16000, freq=0%, samples=4000)
|
|
Replica update time (msec): avg=4.21, min=2, max=8 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=5.59, min=3, max=14 (peers=40, replicas=16000, freq=100%, samples=4000)
|
|
|
|
|
|
Test results (task based marshaling):
|
|
Release:
|
|
Replica update time (msec): avg=0.03, min=0, max=1 (peers=40, replicas=16000, freq=0%, samples=4000)
|
|
Replica update time (msec): avg=3.94, min=1, max=11 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=5.21, min=4, max=15 (peers=40, replicas=16000, freq=100%, samples=4000)
|
|
DebugOpt:
|
|
Replica update time (msec): avg=1.00, min=1, max=2 (peers=40, replicas=16000, freq=0%, samples=4000)
|
|
Replica update time (msec): avg=4.94, min=1, max=9 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=8.05, min=6, max=15 (peers=40, replicas=16000, freq=100%, samples=4000)
|
|
*/
|
|
class Integ_ReplicaStressTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
{
|
|
public:
|
|
class StressTestReplica
|
|
: public ReplicaChunk
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(StressTestReplica);
|
|
typedef AZStd::intrusive_ptr<StressTestReplica> Ptr;
|
|
static const char* GetChunkName() { return "StressTestReplica"; }
|
|
|
|
StressTestReplica()
|
|
: m_data("Data")
|
|
{
|
|
}
|
|
|
|
bool IsReplicaMigratable() override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool m_changing;
|
|
DataSet<int> m_data;
|
|
};
|
|
|
|
static const size_t NUM_PEERS = 40;
|
|
static const size_t NUM_REPLICAS_PER_PEER = 400;
|
|
static const int FRAME_TIME = 5;
|
|
static const int BASE_PORT = 44270;
|
|
|
|
// TODO: Reduce the size or disable the test for platforms which can't allocate 2 GiB
|
|
Integ_ReplicaStressTest()
|
|
: UnitTest::GridMateMPTestFixture(2000u * 1024u * 1024u)
|
|
{}
|
|
|
|
void UpdateReplica(MPSession& session)
|
|
{
|
|
session.GetReplicaMgr().Unmarshal();
|
|
session.GetReplicaMgr().UpdateReplicas();
|
|
|
|
session.GetReplicaMgr().UpdateFromReplicas();
|
|
session.GetReplicaMgr().Marshal();
|
|
}
|
|
|
|
void Wait(MPSession* sessions, vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas, int numFrames, int frameTime)
|
|
{
|
|
(void)replicas;
|
|
while (numFrames--)
|
|
{
|
|
for (size_t i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
sessions[i].Update();
|
|
UpdateReplica(sessions[i]);
|
|
}
|
|
|
|
for (size_t i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
sessions[i].GetTransport()->Update();
|
|
}
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(frameTime));
|
|
}
|
|
}
|
|
|
|
// freq is 0..1, 0.0 - no dirty replicas per tick, 1.0 - all replicas are dirty every tick, 0.5 - 50% of replicas per tick
|
|
void TestReplicas(MPSession* sessions, vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas, int numFrames, int frameTime, double freq)
|
|
{
|
|
AZStd::chrono::system_clock::duration minUpdateTime(AZStd::chrono::system_clock::duration::max());
|
|
AZStd::chrono::system_clock::duration maxUpdateTime(AZStd::chrono::system_clock::duration::min());
|
|
AZStd::chrono::system_clock::duration sumSamples(AZStd::chrono::system_clock::duration::zero());
|
|
unsigned long long numSamples = 0;
|
|
|
|
int count = 0;
|
|
while (numFrames--)
|
|
{
|
|
MarkChanging(replicas, freq);
|
|
|
|
for (auto& r : replicas)
|
|
{
|
|
if (r.second->m_changing)
|
|
{
|
|
r.second->m_data.Set(count++);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
sessions[i].Update();
|
|
AZStd::chrono::system_clock::time_point beforeUpdateTime = AZStd::chrono::system_clock::now();
|
|
UpdateReplica(sessions[i]);
|
|
auto updateTime = AZStd::chrono::system_clock::now() - beforeUpdateTime;
|
|
minUpdateTime = AZStd::min(updateTime, minUpdateTime);
|
|
maxUpdateTime = AZStd::max(updateTime, maxUpdateTime);
|
|
sumSamples += updateTime;
|
|
++numSamples;
|
|
}
|
|
|
|
for (size_t i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
sessions[i].GetTransport()->Update();
|
|
}
|
|
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(frameTime));
|
|
}
|
|
|
|
AZ_TEST_ASSERT(numSamples > 0);
|
|
|
|
AZ_Printf("GridMate", "\n\n----------------\nReplica update time (msec): avg=%.2f, min=%.2f, max=%.2f (peers=%d, replicas=%d, freq=%d%%, samples=%llu)\n",
|
|
static_cast<double>(AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(sumSamples).count()) / static_cast<double>(numSamples),
|
|
AZStd::chrono::duration_cast<AZStd::chrono::microseconds>(minUpdateTime).count() / 1000.0,
|
|
AZStd::chrono::duration_cast<AZStd::chrono::microseconds>(maxUpdateTime).count() / 1000.0,
|
|
NUM_PEERS,
|
|
replicas.size(),
|
|
static_cast<int>(freq * 100.0),
|
|
numSamples);
|
|
}
|
|
|
|
bool ConnectPeers(MPSession* sessions, int frameTime)
|
|
{
|
|
size_t frameCount = 0;
|
|
bool allReady = false;
|
|
size_t maxFramesToReady = NUM_PEERS + 100; // this is to avoid infinite loop waiting for all peers to become ready in case some peer cannot connect
|
|
vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> > replicas;
|
|
|
|
while (!allReady && frameCount < maxFramesToReady)
|
|
{
|
|
// establish all connections
|
|
if (frameCount < NUM_PEERS)
|
|
{
|
|
for (size_t i = 0; i < frameCount; ++i)
|
|
{
|
|
sessions[frameCount].GetTransport()->Connect("127.0.0.1", BASE_PORT + static_cast<unsigned int>(i));
|
|
}
|
|
}
|
|
|
|
allReady = true;
|
|
for (size_t i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
if (!sessions[i].GetReplicaMgr().IsReady())
|
|
{
|
|
allReady = false;
|
|
break;
|
|
}
|
|
}
|
|
if (allReady)
|
|
{
|
|
AZ_Printf("GridMate", "All peers ready at frame %d\n", frameCount);
|
|
}
|
|
|
|
Wait(sessions, replicas, 1, frameTime);
|
|
++frameCount;
|
|
}
|
|
|
|
return allReady;
|
|
}
|
|
|
|
virtual void RunStressTests(MPSession* sessions, vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas)
|
|
{
|
|
// testing 3 cases & waiting for system to settle in between
|
|
TestProfiler::StartProfiling();
|
|
Wait(sessions, replicas, 50, FRAME_TIME);
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.0); // no replicas are dirty
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 1, FRAME_TIME, 1.0); // single burst dirty replicas
|
|
Wait(sessions, replicas, 2, FRAME_TIME);
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.1); // 10% of replicas are marked dirty every frame
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 100, FRAME_TIME, 1.0); // every replica is marked dirty every frame
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
TestProfiler::PrintProfilingSelf("GridMate");
|
|
|
|
TestProfiler::StopProfiling();
|
|
}
|
|
|
|
virtual void MarkChanging(vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas, double freq)
|
|
{
|
|
AZ::Sfmt& sfmt = AZ::Sfmt::GetInstance();
|
|
for (auto& r : replicas)
|
|
{
|
|
r.second->m_changing = sfmt.RandR32_2() <= freq;
|
|
}
|
|
}
|
|
|
|
void run()
|
|
{
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<StressTestReplica>();
|
|
|
|
MPSession sessions[NUM_PEERS];
|
|
vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> > replicas;
|
|
|
|
replicas.reserve(NUM_PEERS * NUM_REPLICAS_PER_PEER);
|
|
|
|
for (unsigned int i = 0; i < NUM_PEERS; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = BASE_PORT + i;
|
|
desc.m_enableDisconnectDetection = false;
|
|
|
|
// initialize replica managers
|
|
sessions[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
sessions[i].AcceptConn(true);
|
|
sessions[i].SetClient(false);
|
|
sessions[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1
|
|
, sessions[i].GetTransport()
|
|
, 0
|
|
, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0));
|
|
}
|
|
|
|
bool allReady = ConnectPeers(sessions, FRAME_TIME);
|
|
|
|
AZ_TEST_ASSERT(allReady);
|
|
|
|
for (auto& session : sessions)
|
|
{
|
|
for (size_t j = 0; j < NUM_REPLICAS_PER_PEER; ++j)
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
auto chunk = CreateAndAttachReplicaChunk<StressTestReplica>(rep);
|
|
replicas.push_back(AZStd::make_pair(rep, chunk));
|
|
session.GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
}
|
|
|
|
RunStressTests(sessions, replicas);
|
|
|
|
// clean up
|
|
for (auto& s : sessions)
|
|
{
|
|
s.GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(s.GetTransport());
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
This test performs updates to the same replicas every frame unlike stress test that picks random replicas every frame
|
|
*03.25.2015* Typical test results on Core i7 3.4 GHz desktop (with polling):
|
|
Release:
|
|
Replica update time (msec): avg=2.54, min=2, max=9 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=3.15, min=2, max=7 (peers=40, replicas=16000, freq=50%, samples=4000)
|
|
DebugOpt:
|
|
Replica update time (msec): avg=3.35, min=3, max=8 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=4.45, min=3, max=10 (peers=40, replicas=16000, freq=50%, samples=4000)
|
|
|
|
Test results (task based marshaling):
|
|
Release:
|
|
Replica update time (msec): avg=1.62, min=1, max=10 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=4.38, min=2, max=15 (peers=40, replicas=16000, freq=50%, samples=4000)
|
|
DebugOpt:
|
|
Replica update time (msec): avg=2.01, min=1, max=5 (peers=40, replicas=16000, freq=10%, samples=4000)
|
|
Replica update time (msec): avg=4.61, min=3, max=10 (peers=40, replicas=16000, freq=50%, samples=4000)
|
|
*/
|
|
class Integ_ReplicaStableStressTest
|
|
: public Integ_ReplicaStressTest
|
|
{
|
|
public:
|
|
|
|
void MarkChanging(vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas, double freq) override
|
|
{
|
|
(void)replicas;
|
|
(void)freq;
|
|
}
|
|
|
|
void RunStressTests(MPSession* sessions, vector<AZStd::pair<ReplicaPtr, StressTestReplica::Ptr> >& replicas) override
|
|
{
|
|
Integ_ReplicaStressTest::MarkChanging(replicas, 0.1); // picks 10% of replicas
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.1);
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
TestProfiler::PrintProfilingSelf("GridMate");
|
|
|
|
Integ_ReplicaStressTest::MarkChanging(replicas, 0.5); // picks 50% of replicas
|
|
Wait(sessions, replicas, 20, FRAME_TIME);
|
|
TestProfiler::StartProfiling();
|
|
TestReplicas(sessions, replicas, 100, FRAME_TIME, 0.5);
|
|
TestProfiler::PrintProfilingTotal("GridMate");
|
|
TestProfiler::PrintProfilingSelf("GridMate");
|
|
|
|
TestProfiler::StopProfiling();
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* This test verifies bandwidth limiter. The test takes ~2 minutes. It sends ~7k/s of data with a limit of 4k with the following pattern:
|
|
*
|
|
*
|
|
* time | 10s | 10s | 20s | 20s | 10s | 20s |
|
|
* +-----+-----+----------+----------+-----+----------+
|
|
* sendrate | 0k | 7k | 7k | 1.5k | 7k | 7k |
|
|
* | | | | | | |
|
|
* expected |none |brst | capped |under cap |brst | capped |
|
|
*
|
|
*/
|
|
class Integ_ReplicaBandiwdthTest
|
|
: public UnitTest::GridMateMPTestFixture
|
|
{
|
|
public:
|
|
|
|
class BandwidthTestChunk
|
|
: public ReplicaChunk
|
|
{
|
|
public:
|
|
GM_CLASS_ALLOCATOR(BandwidthTestChunk);
|
|
typedef AZStd::intrusive_ptr<BandwidthTestChunk> Ptr;
|
|
static const char* GetChunkName() { return "BandwidthTestChunk"; }
|
|
|
|
BandwidthTestChunk()
|
|
: m_value("Value")
|
|
{
|
|
Touch();
|
|
}
|
|
|
|
void Touch()
|
|
{
|
|
string randomStr;
|
|
for (unsigned i = 0; i < k_strSize; ++i)
|
|
{
|
|
randomStr += 'a' + (rand() % 26);
|
|
}
|
|
m_value.Set(randomStr);
|
|
}
|
|
|
|
bool IsReplicaMigratable() override { return false; }
|
|
|
|
static const unsigned k_strSize = 64;
|
|
DataSet<string> m_value;
|
|
};
|
|
|
|
void run()
|
|
{
|
|
ReplicaChunkDescriptorTable::Get().RegisterChunkType<BandwidthTestChunk>();
|
|
|
|
MPSession sessions[nSessions];
|
|
// initialize transport
|
|
int basePort = 4427;
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
TestCarrierDesc desc;
|
|
desc.m_port = basePort + i;
|
|
desc.m_enableDisconnectDetection = false;
|
|
sessions[i].SetClient(i != sHost);
|
|
sessions[i].SetTransport(DefaultCarrier::Create(desc, m_gridMate));
|
|
sessions[i].AcceptConn(true);
|
|
sessions[i].GetReplicaMgr().Init(ReplicaMgrDesc(i + 1, sessions[i].GetTransport(), 0, i == 0 ? ReplicaMgrDesc::Role_SyncHost : 0));
|
|
}
|
|
|
|
// adding replicas for the host
|
|
static const size_t k_numReplicas = 10;
|
|
BandwidthTestChunk::Ptr chunks[k_numReplicas];
|
|
for (size_t i = 0; i < k_numReplicas; ++i)
|
|
{
|
|
auto rep = Replica::CreateReplica(nullptr);
|
|
chunks[i] = CreateAndAttachReplicaChunk<BandwidthTestChunk>(rep);
|
|
sessions[sHost].GetReplicaMgr().AddPrimary(rep);
|
|
}
|
|
|
|
// connect to host
|
|
for (size_t i = 0; i < nSessions; ++i)
|
|
{
|
|
if (i == sHost)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
sessions[i].GetTransport()->Connect("127.0.0.1", basePort);
|
|
}
|
|
|
|
static const int k_delayMS = 100; // tick time
|
|
|
|
bool isDone = false;
|
|
size_t numReplicasToChange = 0;
|
|
|
|
/*
|
|
10 replicas x ~70 bytes per replica x 10 frames per second =~ 7000 bytes per second
|
|
Will try to cutoff it at 4k per sec
|
|
*/
|
|
static const unsigned k_sendRateLimit = 4000;
|
|
|
|
unsigned tickNo = 0;
|
|
AZ_Printf("GridMate", "Created %d sessions.\n", nSessions);
|
|
while (!isDone)
|
|
{
|
|
for (size_t i = 0; i < numReplicasToChange; ++i)
|
|
{
|
|
chunks[i]->Touch();
|
|
}
|
|
|
|
for (auto connId : sessions[sHost].m_connections)
|
|
{
|
|
if (connId == InvalidConnectionID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TrafficControl::Statistics lastSecEffective;
|
|
sessions[sHost].GetTransport()->QueryStatistics(connId, nullptr, nullptr, &lastSecEffective);
|
|
m_measurements[connId].m_sendData.push_back(lastSecEffective.m_dataSend);
|
|
|
|
if (!(tickNo % 30)) // printout every 3 seconds
|
|
{
|
|
AZ_Printf("GridMate", " - effective sendRate=%.2f KB\n", lastSecEffective.m_dataSend / 1000.f);
|
|
}
|
|
//AZ_TracePrintf("GridMate", "%d\n", lastSecEffective.m_dataSend);
|
|
}
|
|
|
|
|
|
// =======================================================
|
|
// Actual tests
|
|
// =======================================================
|
|
if (tickNo == 100) // things should've settle at this point, session is established, initial replicas are sent
|
|
{
|
|
for (size_t i = 0; i < AZ_ARRAY_SIZE(sessions); ++i)
|
|
{
|
|
AZ_TEST_ASSERT(sessions[i].GetReplicaMgr().IsReady());
|
|
}
|
|
|
|
numReplicasToChange = k_numReplicas;
|
|
ResetMeasurements();
|
|
AZ_Printf("GridMate", "Starting replica data send. Unlimited.\n");
|
|
}
|
|
else if (tickNo == 200)
|
|
{
|
|
// test if initial unlimited burst was allowed
|
|
for (const auto& m : m_measurements)
|
|
{
|
|
AZ_TEST_ASSERT(m.second.Max() > k_sendRateLimit * 1.5f);
|
|
}
|
|
ResetMeasurements();
|
|
sessions[sHost].GetReplicaMgr().SetSendLimit(k_sendRateLimit);
|
|
AZ_Printf("GridMate", "Limited by SendLimit=%d Bps.\n", sessions[sHost].GetReplicaMgr().GetSendLimit());
|
|
}
|
|
else if (tickNo == 400)
|
|
{
|
|
// checking if bandwidth was rate limited
|
|
for (const auto& m : m_measurements)
|
|
{
|
|
if (!(m.second.Mean() < k_sendRateLimit * 1.1f))
|
|
{
|
|
AZ_Printf("GridMate", "rate mean: %f limit %d\n", m.second.Mean(), sessions[sHost].GetReplicaMgr().GetSendLimit())
|
|
}
|
|
AZ_TEST_ASSERT(m.second.Mean() < k_sendRateLimit * 1.1f); // allowing 10% margin of error
|
|
}
|
|
ResetMeasurements();
|
|
numReplicasToChange = k_numReplicas / 4; // reducing outgoing traffic 1/4
|
|
AZ_Printf("GridMate", "Reduced send rate...\n");
|
|
}
|
|
else if (tickNo == 600)
|
|
{
|
|
// checking if traffic below the limit
|
|
for (const auto& m : m_measurements)
|
|
{
|
|
AZ_TEST_ASSERT(m.second.Mean() < k_sendRateLimit * 0.7f);
|
|
}
|
|
ResetMeasurements();
|
|
sessions[sHost].GetReplicaMgr().SetSendLimit(0); // returning to unlimited send rate
|
|
numReplicasToChange = k_numReplicas;
|
|
AZ_Printf("GridMate", "Full send rate...\n");
|
|
}
|
|
else if (tickNo == 700)
|
|
{
|
|
// test if burst allowed
|
|
for (const auto& m : m_measurements)
|
|
{
|
|
AZ_TEST_ASSERT(m.second.Max() > k_sendRateLimit * 1.5f);
|
|
}
|
|
ResetMeasurements();
|
|
sessions[sHost].GetReplicaMgr().SetSendLimit(k_sendRateLimit);
|
|
AZ_Printf("GridMate", "Limited by SendLimit=%d Bps.\n", sessions[sHost].GetReplicaMgr().GetSendLimit());
|
|
}
|
|
else if (tickNo == 900)
|
|
{
|
|
// checking if bandwidth was limited
|
|
for (const auto& m : m_measurements)
|
|
{
|
|
AZ_TEST_ASSERT(m.second.Mean() < k_sendRateLimit * 1.1f); // allowing 10% jerking around limit
|
|
}
|
|
|
|
isDone = true;
|
|
}
|
|
|
|
// =======================================================
|
|
// Tick everything
|
|
// =======================================================
|
|
for (size_t i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].Update();
|
|
sessions[i].GetReplicaMgr().Unmarshal();
|
|
}
|
|
|
|
for (size_t i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().UpdateReplicas();
|
|
}
|
|
|
|
for (size_t i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().UpdateFromReplicas();
|
|
sessions[i].GetReplicaMgr().Marshal();
|
|
}
|
|
|
|
for (size_t i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetTransport()->Update();
|
|
}
|
|
|
|
++tickNo;
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(k_delayMS));
|
|
}
|
|
|
|
|
|
for (int i = 0; i < nSessions; ++i)
|
|
{
|
|
sessions[i].GetReplicaMgr().Shutdown();
|
|
DefaultCarrier::Destroy(sessions[i].GetTransport());
|
|
}
|
|
}
|
|
|
|
void ResetMeasurements()
|
|
{
|
|
for (auto& m : m_measurements)
|
|
{
|
|
m.second.Reset();
|
|
}
|
|
}
|
|
|
|
struct Measurement
|
|
{
|
|
unsigned int Mean() const
|
|
{
|
|
AZ_Assert(!m_sendData.empty(), "No data!");
|
|
unsigned int sum = 0;
|
|
for (auto val : m_sendData)
|
|
{
|
|
sum += val;
|
|
}
|
|
|
|
return sum / static_cast<unsigned int>(m_sendData.size());
|
|
}
|
|
|
|
unsigned int Max() const
|
|
{
|
|
unsigned int curMax = 0;
|
|
for (auto val : m_sendData)
|
|
{
|
|
curMax = AZStd::GetMax(curMax, val);
|
|
}
|
|
|
|
return curMax;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
m_sendData.clear();
|
|
}
|
|
|
|
vector<unsigned int> m_sendData; // data sent per second
|
|
};
|
|
|
|
enum
|
|
{
|
|
sHost, sClient1, nSessions
|
|
};
|
|
unordered_map<ConnectionID, Measurement> m_measurements;
|
|
};
|
|
|
|
} // namespace UnitTest
|
|
|
|
GM_TEST_SUITE(ReplicaSuite)
|
|
GM_TEST(InterpolatorTest)
|
|
|
|
#if !defined(AZ_DEBUG_BUILD) // these tests are a little slow for debug
|
|
GM_TEST(Integ_ReplicaBandiwdthTest)
|
|
GM_TEST(Integ_ReplicaStressTest)
|
|
GM_TEST(Integ_ReplicaStableStressTest)
|
|
#endif
|
|
|
|
GM_TEST_SUITE_END()
|