Switched GradientSignal to use GemTestEnvironment. (#6886)

This allows actual Shape components to be used instead of MockShapes, which is important for the benchmarks to get accurate results as Mocks are extremely expensive.  It also removes a lot of unnecessary mock handling and test setup code.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
Mike Balfour
2022-01-13 14:44:59 -06:00
committed by GitHub
parent a99bf8ff1a
commit 62e7483b0e
9 changed files with 193 additions and 220 deletions
@@ -29,21 +29,34 @@ namespace UnitTest
{
GradientSignalTest::SetUp();
// Set up job manager with two threads so that we can run and test the preview job logic.
AZ::JobManagerDesc desc;
AZ::JobManagerThreadDesc threadDesc;
desc.m_workerThreads.push_back(threadDesc);
desc.m_workerThreads.push_back(threadDesc);
m_jobManager = aznew AZ::JobManager(desc);
m_jobContext = aznew AZ::JobContext(*m_jobManager);
AZ::JobContext::SetGlobalContext(m_jobContext);
auto globalContext = AZ::JobContext::GetGlobalContext();
if (globalContext)
{
AZ_Assert(
globalContext->GetJobManager().GetNumWorkerThreads() >= 2,
"Job Manager previously started by test environment with too few threads for this test.");
}
else
{
// Set up job manager with two threads so that we can run and test the preview job logic.
AZ::JobManagerDesc desc;
AZ::JobManagerThreadDesc threadDesc;
desc.m_workerThreads.push_back(threadDesc);
desc.m_workerThreads.push_back(threadDesc);
m_jobManager = aznew AZ::JobManager(desc);
m_jobContext = aznew AZ::JobContext(*m_jobManager);
AZ::JobContext::SetGlobalContext(m_jobContext);
}
}
void TearDown() override
{
AZ::JobContext::SetGlobalContext(nullptr);
delete m_jobContext;
delete m_jobManager;
if (m_jobContext)
{
AZ::JobContext::SetGlobalContext(nullptr);
delete m_jobContext;
delete m_jobManager;
}
GradientSignalTest::TearDown();
}
@@ -180,4 +193,5 @@ namespace UnitTest
}
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
// This uses a custom test hook so that we can load LmbrCentral and use Shape components in our unit tests.
AZ_UNIT_TEST_HOOK(new UnitTest::GradientSignalTestEnvironment);
@@ -14,10 +14,13 @@
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/Math/Vector2.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzFramework/Components/TransformComponent.h>
#include <GradientSignal/Components/ImageGradientComponent.h>
#include <GradientSignal/Components/GradientTransformComponent.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
namespace UnitTest
{
struct GradientSignalImageTestsFixture
@@ -89,23 +92,21 @@ namespace UnitTest
test.m_imageSize, test.m_imageSize, static_cast<AZ::u32>(test.m_pixel.GetX()), static_cast<AZ::u32>(test.m_pixel.GetY()));
config.m_tilingX = test.m_tiling;
config.m_tilingY = test.m_tiling;
CreateComponent<GradientSignal::ImageGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ImageGradientComponent>(config);
// Create the Gradient Transform Component.
GradientSignal::GradientTransformConfig gradientTransformConfig;
gradientTransformConfig.m_wrappingType = test.m_wrappingType;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
// Create a mock Shape component that describes the bounds that we're using to map our ImageGradient into world space.
CreateComponent<MockShapeComponent>(entity.get());
MockShapeComponentHandler mockShapeHandler(entity->GetId());
mockShapeHandler.m_GetLocalBounds = AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds);
LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f));
auto boxComponent = entity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId);
boxComponent->SetConfiguration(boxConfig);
// Create a mock Transform component that locates our ImageGradient in the center of our desired mock Shape.
MockTransformHandler mockTransformHandler;
mockTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
mockTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
mockTransformHandler.BusConnect(entity->GetId());
// Create a transform that locates our gradient in the center of our desired mock Shape.
auto transform = entity->CreateComponent<AzFramework::TransformComponent>();
transform->SetLocalTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)));
transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)));
// All components are created, so activate the entity
ActivateEntity(entity.get());
@@ -365,7 +366,7 @@ namespace UnitTest
mockShapeTransformHandler.BusConnect(mockShape->GetId());
// Create the mock shape that maps our 3x3 image to a 3x3 sample space in the world.
CreateComponent<MockShapeComponent>(mockShape.get());
mockShape->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeComponentHandler(mockShape->GetId());
// Create a 2x2 box shape (shapes are inclusive, so that's 3x3 sampling space), so that each pixel in the image directly maps to 1 meter in the box.
mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0f), AZ::Vector3(2.0f));
@@ -379,7 +380,7 @@ namespace UnitTest
// Create an ImageGradient with a 3x3 asset with the center pixel set.
GradientSignal::ImageGradientConfig gradientConfig;
gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1);
CreateComponent<GradientSignal::ImageGradientComponent>(entity.get(), gradientConfig);
entity->CreateComponent<GradientSignal::ImageGradientComponent>(gradientConfig);
// Create the test GradientTransform
GradientSignal::GradientTransformConfig config;
@@ -400,7 +401,7 @@ namespace UnitTest
config.m_overrideRotate = false;
config.m_overrideScale = false;
config.m_is3d = false;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::GradientTransformComponent>(config);
// Set up the transform on the gradient entity.
MockTransformHandler mockTransformHandler;
@@ -409,7 +410,7 @@ namespace UnitTest
mockTransformHandler.BusConnect(entity->GetId());
// Put a default shape on our gradient entity. This is only used for previews, so it doesn't matter what it gets set to.
CreateComponent<MockShapeComponent>(entity.get());
entity->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeHandler(entity->GetId());
ActivateEntity(entity.get());
@@ -55,7 +55,7 @@ namespace UnitTest
config.m_layers.push_back(layer);
auto entity = CreateEntity();
CreateComponent<GradientSignal::MixedGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::MixedGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -88,7 +88,7 @@ namespace UnitTest
config.m_smoothStep.m_falloffStrength = falloffStrength;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceSlopeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceSlopeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -391,7 +391,7 @@ namespace UnitTest
GradientSignal::ReferenceGradientConfig referenceGradientConfig1;
referenceGradientConfig1.m_gradientSampler.m_ownerEntityId = referenceGradientEntity1->GetId();
referenceGradientConfig1.m_gradientSampler.m_gradientId = constantGradientEntity->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity1.get(), referenceGradientConfig1);
referenceGradientEntity1->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig1);
ActivateEntity(referenceGradientEntity1.get());
EXPECT_TRUE(referenceGradientConfig1.m_gradientSampler.ValidateGradientEntityId());
@@ -400,7 +400,7 @@ namespace UnitTest
GradientSignal::ReferenceGradientConfig referenceGradientConfig2;
referenceGradientConfig2.m_gradientSampler.m_ownerEntityId = referenceGradientEntity2->GetId();
referenceGradientConfig2.m_gradientSampler.m_gradientId = referenceGradientEntity1->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity2.get(), referenceGradientConfig2);
referenceGradientEntity2->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig2);
ActivateEntity(referenceGradientEntity2.get());
EXPECT_TRUE(referenceGradientConfig2.m_gradientSampler.ValidateGradientEntityId());
@@ -409,7 +409,7 @@ namespace UnitTest
GradientSignal::ReferenceGradientConfig referenceGradientConfig3;
referenceGradientConfig3.m_gradientSampler.m_ownerEntityId = referenceGradientEntity3->GetId();
referenceGradientConfig3.m_gradientSampler.m_gradientId = referenceGradientEntity3->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity3.get(), referenceGradientConfig3);
referenceGradientEntity3->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig3);
ActivateEntity(referenceGradientEntity3.get());
EXPECT_FALSE(referenceGradientConfig3.m_gradientSampler.ValidateGradientEntityId());
EXPECT_EQ(referenceGradientConfig3.m_gradientSampler.m_gradientId, AZ::EntityId());
@@ -422,19 +422,19 @@ namespace UnitTest
GradientSignal::ReferenceGradientConfig referenceGradientConfig4;
referenceGradientConfig4.m_gradientSampler.m_ownerEntityId = referenceGradientEntity4->GetId();
referenceGradientConfig4.m_gradientSampler.m_gradientId = referenceGradientEntity5->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity4.get(), referenceGradientConfig4);
referenceGradientEntity4->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig4);
ActivateEntity(referenceGradientEntity4.get());
GradientSignal::ReferenceGradientConfig referenceGradientConfig5;
referenceGradientConfig5.m_gradientSampler.m_ownerEntityId = referenceGradientEntity5->GetId();
referenceGradientConfig5.m_gradientSampler.m_gradientId = referenceGradientEntity6->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity5.get(), referenceGradientConfig5);
referenceGradientEntity5->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig5);
ActivateEntity(referenceGradientEntity5.get());
GradientSignal::ReferenceGradientConfig referenceGradientConfig6;
referenceGradientConfig6.m_gradientSampler.m_ownerEntityId = referenceGradientEntity6->GetId();
referenceGradientConfig6.m_gradientSampler.m_gradientId = referenceGradientEntity4->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientEntity6.get(), referenceGradientConfig6);
referenceGradientEntity6->CreateComponent<GradientSignal::ReferenceGradientComponent>(referenceGradientConfig6);
ActivateEntity(referenceGradientEntity6.get());
EXPECT_FALSE(referenceGradientConfig6.m_gradientSampler.ValidateGradientEntityId());
@@ -456,7 +456,7 @@ namespace UnitTest
// Create an AABB from -1 to 1, so points at coorindates 0 and 1 fall on it, but any points at coordinate 2 won't.
auto entityShape = CreateEntity();
CreateComponent<MockShapeComponent>(entityShape.get());
entityShape->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(1.0f));
@@ -466,7 +466,7 @@ namespace UnitTest
config.m_falloffType = GradientSignal::FalloffType::Outer;
auto entity = CreateEntity();
CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -481,7 +481,7 @@ namespace UnitTest
// Create our test shape from -1 to 0, so we have a corner directly on (0, 0).
auto entityShape = CreateEntity();
CreateComponent<MockShapeComponent>(entityShape.get());
entityShape->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-1.0f), AZ::Vector3(0.0f));
@@ -512,7 +512,7 @@ namespace UnitTest
}
auto entity = CreateEntity();
CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -533,7 +533,7 @@ namespace UnitTest
// We're pinning a shape, so the bounding box of (0, 0, 0) - (10, 10, 10) will be the one that applies.
auto entityShape = CreateEntity();
CreateComponent<MockShapeComponent>(entityShape.get());
entityShape->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeComponentHandler(entityShape->GetId());
mockShapeComponentHandler.m_GetEncompassingAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3::CreateZero(), AZ::Vector3(10.0f));
@@ -551,7 +551,7 @@ namespace UnitTest
config.m_altitudeMax = 24.0f;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -584,7 +584,7 @@ namespace UnitTest
config.m_altitudeMax = 10.0f;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -612,7 +612,7 @@ namespace UnitTest
config.m_altitudeMax = 15.0f;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -649,7 +649,7 @@ namespace UnitTest
config.m_altitudeMax = 15.0f;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -684,7 +684,7 @@ namespace UnitTest
config.m_surfaceTagList.push_back(AZ_CRC("test_mask", 0x7a16e9ff));
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -711,7 +711,7 @@ namespace UnitTest
config.m_surfaceTagList.push_back(AZ_CRC("test_mask", 0x7a16e9ff));
auto entity = CreateEntity();
CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -31,7 +31,7 @@ namespace UnitTest
config.m_value = expectedOutput;
auto entity = CreateEntity();
CreateComponent<GradientSignal::ConstantGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ConstantGradientComponent>(config);
ActivateEntity(entity.get());
GradientSignal::GradientSampler gradientSampler;
@@ -75,7 +75,7 @@ namespace UnitTest
config.m_gradientSampler.m_gradientId = entityMock->GetId();
auto entity = CreateEntity();
CreateComponent<GradientSignal::DitherGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::DitherGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -109,7 +109,7 @@ namespace UnitTest
config.m_gradientSampler.m_gradientId = entityMock->GetId();
auto entity = CreateEntity();
CreateComponent<GradientSignal::DitherGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::DitherGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -147,7 +147,7 @@ namespace UnitTest
config.m_gradientSampler.m_gradientId = entityMock->GetId();
auto entity = CreateEntity();
CreateComponent<GradientSignal::DitherGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::DitherGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -185,7 +185,7 @@ namespace UnitTest
config.m_gradientSampler.m_gradientId = entityMock->GetId();
auto entity = CreateEntity();
CreateComponent<GradientSignal::DitherGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::DitherGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -44,11 +44,9 @@ namespace UnitTest
// This lets our component register with surfaceData successfully.
MockSurfaceDataSystem mockSurfaceDataSystem;
// Create a mock shape entity in case we want to use it.
// Create a mock shape entity in case our gradient test uses shape constraints.
// The mock shape is a cube that goes from -0.5 to 0.5 in space.
auto mockShapeEntity = CreateEntity();
CreateComponent<MockShapeComponent>(mockShapeEntity.get());
MockShapeComponentHandler mockShapeHandler(mockShapeEntity->GetId());
auto mockShapeEntity = CreateTestEntity(0.5f);
ActivateEntity(mockShapeEntity.get());
// For ease of testing, use a constant gradient as our input gradient.
@@ -76,8 +74,8 @@ namespace UnitTest
// Create the test entity with the GradientSurfaceData component and the required gradient dependency
auto entity = CreateEntity();
CreateComponent<GradientSignal::ConstantGradientComponent>(entity.get(), constantGradientConfig);
CreateComponent<GradientSignal::GradientSurfaceDataComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ConstantGradientComponent>(constantGradientConfig);
entity->CreateComponent<GradientSignal::GradientSurfaceDataComponent>(config);
ActivateEntity(entity.get());
// Get our registered modifier handle (and verify that it's valid)
@@ -27,14 +27,12 @@ namespace UnitTest
void TestLevelsGradientComponent(int dataSize, const AZStd::vector<float>& inputData, const AZStd::vector<float>& expectedOutput,
float inputMin, float inputMid, float inputMax, float outputMin, float outputMax)
{
auto entityMock = CreateEntity();
auto entityMock = CreateTestEntity(1.0f);
const AZ::EntityId id = entityMock->GetId();
UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entityMock.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entityMock.get());
MockShapeComponentHandler mockShapeHandler(entityMock->GetId());
entityMock->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entityMock.get());
@@ -47,7 +45,7 @@ namespace UnitTest
config.m_outputMax = outputMax;
auto entity = CreateEntity();
CreateComponent<GradientSignal::LevelsGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::LevelsGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -56,14 +54,12 @@ namespace UnitTest
void TestPosterizeGradientComponent(int dataSize, const AZStd::vector<float>& inputData, const AZStd::vector<float>& expectedOutput,
GradientSignal::PosterizeGradientConfig::ModeType posterizeMode, int bands)
{
auto entityMock = CreateEntity();
auto entityMock = CreateTestEntity(0.5f);
const AZ::EntityId id = entityMock->GetId();
UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entityMock.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entityMock.get());
MockShapeComponentHandler mockShapeHandler(entityMock->GetId());
entityMock->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entityMock.get());
@@ -73,7 +69,7 @@ namespace UnitTest
config.m_bands = bands;
auto entity = CreateEntity();
CreateComponent<GradientSignal::PosterizeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::PosterizeGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -82,14 +78,12 @@ namespace UnitTest
void TestSmoothStepGradientComponent(int dataSize, const AZStd::vector<float>& inputData, const AZStd::vector<float>& expectedOutput,
float midpoint, float range, float softness)
{
auto entityMock = CreateEntity();
auto entityMock = CreateTestEntity(0.5f);
const AZ::EntityId id = entityMock->GetId();
UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entityMock.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entityMock.get());
MockShapeComponentHandler mockShapeHandler(entityMock->GetId());
entityMock->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entityMock.get());
@@ -100,7 +94,7 @@ namespace UnitTest
config.m_smoothStep.m_falloffStrength = softness;
auto entity = CreateEntity();
CreateComponent<GradientSignal::SmoothStepGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SmoothStepGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -108,14 +102,12 @@ namespace UnitTest
void TestThresholdGradientComponent(int dataSize, const AZStd::vector<float>& inputData, const AZStd::vector<float>& expectedOutput, float threshold)
{
auto entityMock = CreateEntity();
auto entityMock = CreateTestEntity(0.5f);
const AZ::EntityId id = entityMock->GetId();
UnitTest::MockGradientArrayRequestsBus mockGradientRequestsBus(id, inputData, dataSize);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entityMock.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entityMock.get());
MockShapeComponentHandler mockShapeHandler(entityMock->GetId());
entityMock->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entityMock.get());
@@ -124,7 +116,7 @@ namespace UnitTest
config.m_threshold = threshold;
auto entity = CreateEntity();
CreateComponent<GradientSignal::ThresholdGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ThresholdGradientComponent>(config);
ActivateEntity(entity.get());
TestFixedDataSampler(expectedOutput, dataSize, entity->GetId());
@@ -167,11 +159,11 @@ namespace UnitTest
AZStd::vector<float> expectedOutput = { AZ_TRAIT_UNIT_TEST_PERLINE_GRADIANT_GOLDEN_VALUES_7878 };
auto entity = CreateEntity();
CreateComponent<GradientSignal::PerlinGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::PerlinGradientComponent>(config);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entity.get());
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
entity->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeHandler(entity->GetId());
ActivateEntity(entity.get());
@@ -197,11 +189,11 @@ namespace UnitTest
config.m_randomSeed = 5656;
auto entity = CreateEntity();
CreateComponent<GradientSignal::RandomGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::RandomGradientComponent>(config);
GradientSignal::GradientTransformConfig gradientTransformConfig;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
CreateComponent<MockShapeComponent>(entity.get());
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
entity->CreateComponent<MockShapeComponent>();
MockShapeComponentHandler mockShapeHandler(entity->GetId());
ActivateEntity(entity.get());
@@ -546,4 +538,5 @@ namespace UnitTest
}
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
// This uses custom test / benchmark hooks so that we can load LmbrCentral and use Shape components in our unit tests and benchmarks.
AZ_UNIT_TEST_HOOK(new UnitTest::GradientSignalTestEnvironment, UnitTest::GradientSignalBenchmarkEnvironment);
@@ -10,7 +10,9 @@
#include <Tests/GradientSignalTestFixtures.h>
#include <AzFramework/Components/TransformComponent.h>
#include <GradientSignal/Components/GradientSurfaceDataComponent.h>
#include <GradientSignal/Components/GradientTransformComponent.h>
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
// Base gradient components
#include <GradientSignal/Components/ConstantGradientComponent.h>
@@ -36,65 +38,48 @@
namespace UnitTest
{
void GradientSignalTestEnvironment::AddGemsAndComponents()
{
AddDynamicModulePaths({ "LmbrCentral" });
AddComponentDescriptors({
AzFramework::TransformComponent::CreateDescriptor(),
GradientSignal::ConstantGradientComponent::CreateDescriptor(),
GradientSignal::DitherGradientComponent::CreateDescriptor(),
GradientSignal::GradientSurfaceDataComponent::CreateDescriptor(),
GradientSignal::GradientTransformComponent::CreateDescriptor(),
GradientSignal::ImageGradientComponent::CreateDescriptor(),
GradientSignal::InvertGradientComponent::CreateDescriptor(),
GradientSignal::LevelsGradientComponent::CreateDescriptor(),
GradientSignal::MixedGradientComponent::CreateDescriptor(),
GradientSignal::PerlinGradientComponent::CreateDescriptor(),
GradientSignal::PosterizeGradientComponent::CreateDescriptor(),
GradientSignal::RandomGradientComponent::CreateDescriptor(),
GradientSignal::ReferenceGradientComponent::CreateDescriptor(),
GradientSignal::ShapeAreaFalloffGradientComponent::CreateDescriptor(),
GradientSignal::SmoothStepGradientComponent::CreateDescriptor(),
GradientSignal::SurfaceAltitudeGradientComponent::CreateDescriptor(),
GradientSignal::SurfaceMaskGradientComponent::CreateDescriptor(),
GradientSignal::SurfaceSlopeGradientComponent::CreateDescriptor(),
GradientSignal::ThresholdGradientComponent::CreateDescriptor(),
MockShapeComponent::CreateDescriptor(),
});
}
void GradientSignalBaseFixture::SetupCoreSystems()
{
m_app = AZStd::make_unique<AZ::ComponentApplication>();
ASSERT_TRUE(m_app != nullptr);
AZ::ComponentApplication::Descriptor componentAppDesc;
m_systemEntity = m_app->Create(componentAppDesc);
ASSERT_TRUE(m_systemEntity != nullptr);
m_app->AddEntity(m_systemEntity);
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create();
AZ::Data::AssetManager::Descriptor desc;
AZ::Data::AssetManager::Create(desc);
m_mockHandler = new ImageAssetMockAssetHandler();
m_mockHandler = new UnitTest::ImageAssetMockAssetHandler();
AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid<GradientSignal::ImageAsset>());
m_mockShapeHandlers = new AZStd::vector<AZStd::unique_ptr<testing::NiceMock<UnitTest::MockShapeComponentRequests>>>();
}
void GradientSignalBaseFixture::TearDownCoreSystems()
{
// Clear any mock shape handlers that we've created for our test entities.
delete m_mockShapeHandlers;
AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler);
delete m_mockHandler; // delete after removing from the asset manager
AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
AZ::Data::AssetManager::Destroy();
AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy();
m_app->Destroy();
m_app.reset();
m_systemEntity = nullptr;
}
AZStd::unique_ptr<testing::NiceMock<UnitTest::MockShapeComponentRequests>> GradientSignalBaseFixture::CreateMockShape(
const AZ::Aabb& spawnerBox, const AZ::EntityId& shapeEntityId)
{
AZStd::unique_ptr<testing::NiceMock<UnitTest::MockShapeComponentRequests>> mockShape =
AZStd::make_unique<testing::NiceMock<UnitTest::MockShapeComponentRequests>>(shapeEntityId);
ON_CALL(*mockShape, GetEncompassingAabb).WillByDefault(testing::Return(spawnerBox));
ON_CALL(*mockShape, GetTransformAndLocalBounds)
.WillByDefault(
[spawnerBox](AZ::Transform& transform, AZ::Aabb& bounds)
{
transform = AZ::Transform::CreateTranslation(spawnerBox.GetCenter());
bounds = spawnerBox.GetTranslated(-spawnerBox.GetCenter());
});
ON_CALL(*mockShape, IsPointInside)
.WillByDefault(
[spawnerBox](const AZ::Vector3& point) -> bool
{
return spawnerBox.Contains(point);
});
return mockShape;
}
AZStd::unique_ptr<MockSurfaceDataSystem> GradientSignalBaseFixture::CreateMockSurfaceDataSystem(const AZ::Aabb& spawnerBox)
@@ -130,16 +115,12 @@ namespace UnitTest
// Create the base entity
AZStd::unique_ptr<AZ::Entity> testEntity = CreateEntity();
// Create a mock Shape component that describes the bounds that we're using to map our gradient into world space.
CreateComponent<MockShapeComponent>(testEntity.get());
// Create and keep a reference to a mock shape handler that will respond to shape requests for the mock shape.
auto mockShapeHandler =
CreateMockShape(AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds), testEntity->GetId());
m_mockShapeHandlers->push_back(AZStd::move(mockShapeHandler));
LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(shapeHalfBounds * 2.0f));
auto boxComponent = testEntity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId);
boxComponent->SetConfiguration(boxConfig);
// Create a transform that locates our gradient in the center of our desired mock Shape.
auto transform = CreateComponent<AzFramework::TransformComponent>(testEntity.get());
auto transform = testEntity->CreateComponent<AzFramework::TransformComponent>();
transform->SetLocalTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)));
transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds)));
@@ -152,7 +133,7 @@ namespace UnitTest
auto entity = CreateTestEntity(shapeHalfBounds);
GradientSignal::ConstantGradientConfig config;
config.m_value = 0.75f;
CreateComponent<GradientSignal::ConstantGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ConstantGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -168,12 +149,12 @@ namespace UnitTest
config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed);
config.m_tilingX = 1.0f;
config.m_tilingY = 1.0f;
CreateComponent<GradientSignal::ImageGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ImageGradientComponent>(config);
// Create a Gradient Transform Component with arbitrary parameters.
GradientSignal::GradientTransformConfig gradientTransformConfig;
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entity.get());
return entity;
@@ -188,12 +169,12 @@ namespace UnitTest
config.m_frequency = 1.1f;
config.m_octave = 4;
config.m_randomSeed = 12345;
CreateComponent<GradientSignal::PerlinGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::PerlinGradientComponent>(config);
// Create a Gradient Transform Component with arbitrary parameters.
GradientSignal::GradientTransformConfig gradientTransformConfig;
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entity.get());
return entity;
@@ -205,12 +186,12 @@ namespace UnitTest
auto entity = CreateTestEntity(shapeHalfBounds);
GradientSignal::RandomGradientConfig config;
config.m_randomSeed = 12345;
CreateComponent<GradientSignal::RandomGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::RandomGradientComponent>(config);
// Create a Gradient Transform Component with arbitrary parameters.
GradientSignal::GradientTransformConfig gradientTransformConfig;
gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
CreateComponent<GradientSignal::GradientTransformComponent>(entity.get(), gradientTransformConfig);
entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
ActivateEntity(entity.get());
return entity;
@@ -224,7 +205,7 @@ namespace UnitTest
config.m_shapeEntityId = entity->GetId();
config.m_falloffWidth = 16.0f;
config.m_falloffType = GradientSignal::FalloffType::InnerOuter;
CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ShapeAreaFalloffGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -238,10 +219,11 @@ namespace UnitTest
GradientSignal::DitherGradientConfig config;
config.m_gradientSampler.m_gradientId = inputGradientId;
config.m_useSystemPointsPerUnit = false;
config.m_pointsPerUnit = 1.0f;
// Use a number other than 1.0f for pointsPerUnit to ensure the dither math is getting exercised properly.
config.m_pointsPerUnit = 0.25f;
config.m_patternOffset = AZ::Vector3::CreateZero();
config.m_patternType = GradientSignal::DitherGradientConfig::BayerPatternType::PATTERN_SIZE_4x4;
CreateComponent<GradientSignal::DitherGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::DitherGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -254,7 +236,7 @@ namespace UnitTest
auto entity = CreateTestEntity(shapeHalfBounds);
GradientSignal::InvertGradientConfig config;
config.m_gradientSampler.m_gradientId = inputGradientId;
CreateComponent<GradientSignal::InvertGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::InvertGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -272,7 +254,7 @@ namespace UnitTest
config.m_inputMax = 0.9f;
config.m_outputMin = 0.0f;
config.m_outputMax = 1.0f;
CreateComponent<GradientSignal::LevelsGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::LevelsGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -298,7 +280,7 @@ namespace UnitTest
layer.m_gradientSampler.m_opacity = 0.75f;
config.m_layers.push_back(layer);
CreateComponent<GradientSignal::MixedGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::MixedGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -313,7 +295,7 @@ namespace UnitTest
config.m_gradientSampler.m_gradientId = inputGradientId;
config.m_mode = GradientSignal::PosterizeGradientConfig::ModeType::Ps;
config.m_bands = 5;
CreateComponent<GradientSignal::PosterizeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::PosterizeGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -327,7 +309,7 @@ namespace UnitTest
GradientSignal::ReferenceGradientConfig config;
config.m_gradientSampler.m_gradientId = inputGradientId;
config.m_gradientSampler.m_ownerEntityId = entity->GetId();
CreateComponent<GradientSignal::ReferenceGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ReferenceGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -343,7 +325,7 @@ namespace UnitTest
config.m_smoothStep.m_falloffMidpoint = 0.75f;
config.m_smoothStep.m_falloffRange = 0.125f;
config.m_smoothStep.m_falloffStrength = 0.25f;
CreateComponent<GradientSignal::SmoothStepGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SmoothStepGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -357,7 +339,7 @@ namespace UnitTest
GradientSignal::ThresholdGradientConfig config;
config.m_gradientSampler.m_gradientId = inputGradientId;
config.m_threshold = 0.75f;
CreateComponent<GradientSignal::ThresholdGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::ThresholdGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -370,7 +352,7 @@ namespace UnitTest
GradientSignal::SurfaceAltitudeGradientConfig config;
config.m_altitudeMin = -5.0f;
config.m_altitudeMax = 15.0f;
CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -382,7 +364,7 @@ namespace UnitTest
auto entity = CreateTestEntity(shapeHalfBounds);
GradientSignal::SurfaceMaskGradientConfig config;
config.m_surfaceTagList.push_back(AZ_CRC_CE("test_mask"));
CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -399,7 +381,7 @@ namespace UnitTest
config.m_smoothStep.m_falloffMidpoint = 0.75f;
config.m_smoothStep.m_falloffRange = 0.125f;
config.m_smoothStep.m_falloffStrength = 0.25f;
CreateComponent<GradientSignal::SurfaceSlopeGradientComponent>(entity.get(), config);
entity->CreateComponent<GradientSignal::SurfaceSlopeGradientComponent>(config);
ActivateEntity(entity.get());
return entity;
@@ -9,9 +9,39 @@
#include <Tests/GradientSignalTestMocks.h>
#include <LmbrCentral/Shape/MockShapes.h>
#include <AzTest/GemTestEnvironment.h>
namespace UnitTest
{
// The GradientSignal unit tests need to use the GemTestEnvironment to load the LmbrCentral Gem so that Shape components can be used
// in the unit tests and benchmarks.
class GradientSignalTestEnvironment
: public AZ::Test::GemTestEnvironment
{
public:
void AddGemsAndComponents() override;
};
#ifdef HAVE_BENCHMARK
//! The Benchmark environment is used for one time setup and tear down of shared resources
class GradientSignalBenchmarkEnvironment
: public AZ::Test::BenchmarkEnvironmentBase
, public GradientSignalTestEnvironment
{
protected:
void SetUpBenchmark() override
{
SetupEnvironment();
}
void TearDownBenchmark() override
{
TeardownEnvironment();
}
};
#endif
// Base test fixture used for GradientSignal unit tests and benchmark tests
class GradientSignalBaseFixture
{
@@ -30,24 +60,6 @@ namespace UnitTest
entity->Activate();
}
template<typename Component, typename Configuration>
Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
{
m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>(config);
}
template<typename Component>
Component* CreateComponent(AZ::Entity* entity)
{
m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
return entity->CreateComponent<Component>();
}
// Create a mock shape that will respond to the shape bus with proper responses for the given input box.
AZStd::unique_ptr<testing::NiceMock<UnitTest::MockShapeComponentRequests>> CreateMockShape(
const AZ::Aabb& spawnerBox, const AZ::EntityId& shapeEntityId);
// Create a mock SurfaceDataSystem that will respond to requests for surface points with mock responses for points inside
// the given input box.
AZStd::unique_ptr<MockSurfaceDataSystem> CreateMockSurfaceDataSystem(const AZ::Aabb& spawnerBox);
@@ -77,27 +89,22 @@ namespace UnitTest
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceMaskGradient(float shapeHalfBounds);
AZStd::unique_ptr<AZ::Entity> BuildTestSurfaceSlopeGradient(float shapeHalfBounds);
AZStd::unique_ptr<AZ::ComponentApplication> m_app;
AZ::Entity* m_systemEntity = nullptr;
ImageAssetMockAssetHandler* m_mockHandler = nullptr;
AZStd::vector<AZStd::unique_ptr<testing::NiceMock<UnitTest::MockShapeComponentRequests>>>* m_mockShapeHandlers = nullptr;
UnitTest::ImageAssetMockAssetHandler* m_mockHandler = nullptr;
};
struct GradientSignalTest
: public GradientSignalBaseFixture
, public UnitTest::AllocatorsTestFixture
, public ::testing::Test
{
protected:
void SetUp() override
{
UnitTest::AllocatorsTestFixture::SetUp();
SetupCoreSystems();
}
void TearDown() override
{
TearDownCoreSystems();
UnitTest::AllocatorsTestFixture::TearDown();
}
void TestFixedDataSampler(const AZStd::vector<float>& expectedOutput, int size, AZ::EntityId gradientEntityId);
@@ -106,41 +113,36 @@ namespace UnitTest
#ifdef HAVE_BENCHMARK
class GradientSignalBenchmarkFixture
: public GradientSignalBaseFixture
, public UnitTest::AllocatorsBenchmarkFixture
, public UnitTest::TraceBusRedirector
, public ::benchmark::Fixture
{
public:
void internalSetUp(const benchmark::State& state)
void internalSetUp()
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
UnitTest::AllocatorsBenchmarkFixture::SetUp(state);
SetupCoreSystems();
}
void internalTearDown(const benchmark::State& state)
void internalTearDown()
{
TearDownCoreSystems();
UnitTest::AllocatorsBenchmarkFixture::TearDown(state);
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
protected:
void SetUp(const benchmark::State& state) override
void SetUp([[maybe_unused]] const benchmark::State& state) override
{
internalSetUp(state);
internalSetUp();
}
void SetUp(benchmark::State& state) override
void SetUp([[maybe_unused]] benchmark::State& state) override
{
internalSetUp(state);
internalSetUp();
}
void TearDown(const benchmark::State& state) override
void TearDown([[maybe_unused]] const benchmark::State& state) override
{
internalTearDown(state);
internalTearDown();
}
void TearDown(benchmark::State& state) override
void TearDown([[maybe_unused]] benchmark::State& state) override
{
internalTearDown(state);
internalTearDown();
}
};
#endif
@@ -11,6 +11,7 @@
#include <AzCore/Component/ComponentApplication.h>
#include <GradientSignal/GradientImageConversion.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <limits>
@@ -64,26 +65,8 @@ namespace
}
}
class ImageAssetTest
: public ::testing::Test
class ImageAssetTest : public ::testing::Test
{
protected:
AZ::ComponentApplication m_app;
AZ::Entity* m_systemEntity = nullptr;
void SetUp() override
{
AZ::ComponentApplication::Descriptor appDesc;
appDesc.m_memoryBlocksByteSize = 128 * 1024 * 1024;
m_systemEntity = m_app.Create(appDesc);
m_app.AddEntity(m_systemEntity);
}
void TearDown() override
{
m_app.Destroy();
m_systemEntity = nullptr;
}
};
#if AZ_TRAIT_DISABLE_FAILED_GRADIENT_SIGNAL_TESTS