Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,451 @@
/*
* 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 "AzToolsFrameworkTestHelpers.h"
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <QAction>
using namespace AzToolsFramework;
namespace UnitTest
{
bool TestWidget::eventFilter(QObject* watched, QEvent* event)
{
AZ_UNUSED(watched);
switch (event->type())
{
case QEvent::ShortcutOverride:
{
const auto& cachedActions = actions();
// perform cast after type check (now safe to do so)
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
// do our best to build a key sequence
// note: this will not handle more complex multi-key shortcuts at this time
const auto keySequence = QKeySequence(keyEvent->modifiers() + keyEvent->key());
// lookup the action that corresponds to this key event
const auto actionIt = AZStd::find_if(
cachedActions.begin(), cachedActions.end(), [keySequence](QAction* action)
{
return action->shortcut() == keySequence;
});
// if we get a match, generate the shortcut for this key combination
if (actionIt != cachedActions.end())
{
// trigger shortcut
QShortcutEvent shortcutEvent(keySequence, 0);
QApplication::sendEvent(*actionIt, &shortcutEvent);
keyEvent->accept();
return true;
}
return false;
}
default:
return false;
}
}
void TestEditorActions::Connect()
{
using AzToolsFramework::GetEntityContextId;
using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus;
AzToolsFramework::EditorActionRequestBus::Handler::BusConnect();
EditorComponentModeNotificationBus::Handler::BusConnect(GetEntityContextId());
m_defaultWidget.setFocus();
}
void TestEditorActions::Disconnect()
{
using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus;
EditorComponentModeNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorActionRequestBus::Handler::BusDisconnect();
}
void TestEditorActions::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentTypes)
{
m_componentModeWidget.setFocus();
}
void TestEditorActions::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentTypes)
{
m_defaultWidget.setFocus();
}
void TestEditorActions::AddActionViaBus(int id, QAction* action)
{
AZ_Assert(action, "Attempting to add a null action");
if (action)
{
action->setData(id);
action->setShortcutContext(Qt::ApplicationShortcut);
m_defaultWidget.addAction(action);
}
}
void TestEditorActions::RemoveActionViaBus(QAction* action)
{
AZ_Assert(action, "Attempting to remove a null action");
if (action)
{
m_defaultWidget.removeAction(action);
}
}
static void SetDefaultActionsEnabled(
const QList<QAction*>& actions, const bool enabled)
{
AZStd::for_each(
actions.begin(), actions.end(),
[enabled](QAction* action)
{
if (!action->property("Reserved").toBool())
{
action->setEnabled(enabled);
}
});
}
void TestEditorActions::EnableDefaultActions()
{
SetDefaultActionsEnabled(m_defaultWidget.actions(), true);
}
void TestEditorActions::DisableDefaultActions()
{
SetDefaultActionsEnabled(m_defaultWidget.actions(), false);
}
EditorEntityComponentChangeDetector::EditorEntityComponentChangeDetector(const AZ::EntityId entityId)
{
PropertyEditorEntityChangeNotificationBus::Handler::BusConnect(entityId);
EditorTransformChangeNotificationBus::Handler::BusConnect();
ToolsApplicationNotificationBus::Handler::BusConnect();
}
EditorEntityComponentChangeDetector::~EditorEntityComponentChangeDetector()
{
ToolsApplicationNotificationBus::Handler::BusDisconnect();
EditorTransformChangeNotificationBus::Handler::BusDisconnect();
PropertyEditorEntityChangeNotificationBus::Handler::BusDisconnect();
}
void EditorEntityComponentChangeDetector::OnEntityComponentPropertyChanged(const AZ::ComponentId componentId)
{
m_componentIds.push_back(componentId);
}
void EditorEntityComponentChangeDetector::OnEntityTransformChanged(
const AzToolsFramework::EntityIdList& entityIds)
{
for (const AZ::EntityId& entityId : entityIds)
{
if (const auto* entity = GetEntityById(entityId))
{
if (AZ::Component * transformComponent = entity->FindComponent<Components::TransformComponent>())
{
OnEntityComponentPropertyChanged(transformComponent->GetId());
}
}
}
}
void EditorEntityComponentChangeDetector::InvalidatePropertyDisplay(
AzToolsFramework::PropertyModificationRefreshLevel /*level*/)
{
m_propertyDisplayInvalidated = true;
}
ToolsApplicationMessageHandler::ToolsApplicationMessageHandler()
{
m_gridMateMessageHandler = AZStd::make_unique<ErrorHandler>("GridMate");
m_enginePathMessageHandler = AZStd::make_unique<ErrorHandler>("Engine Path");
m_skippingDriveMessageHandler = AZStd::make_unique<ErrorHandler>("Skipping drive");
m_storageDriveMessageHandler = AZStd::make_unique<ErrorHandler>("Storage drive");
m_jsonComponentErrorHandler = AZStd::make_unique<ErrorHandler>("JsonSystem");
}
void MockPerforceCommand::ExecuteCommand()
{
m_rawOutput.Clear();
m_applicationFound = true;
if (m_commandArgs == "set")
{
m_rawOutput.outputResult =
R"(P4PORT=ssl:unittest.amazon.com:1666 (set))" "\r\n"
"\r\n";
}
else if (m_commandArgs == "info")
{
m_rawOutput.outputResult =
R"(... userName unittest)" "\r\n"
R"(... clientName unittest)" "\r\n"
R"(... clientRoot c:\depot)" "\r\n"
R"(... clientLock none)" "\r\n"
R"(... clientCwd c:\depot\dev\Engine\Fonts)" "\r\n"
R"(... clientHost unittest)" "\r\n"
R"(... clientCase insensitive)" "\r\n"
R"(... peerAddress 127.0.0.1:64565)" "\r\n"
R"(... clientAddress 127.0.0.1)" "\r\n"
R"(... serverName unittest)" "\r\n"
R"(... lbr.replication readonly)" "\r\n"
R"(... monitor enabled)" "\r\n"
R"(... security enabled)" "\r\n"
R"(... externalAuth enabled)" "\r\n"
R"(... serverAddress unittest.amazon.com:1666)" "\r\n"
R"(... serverRoot /data/repos/p4root/root)" "\r\n"
R"(... serverDate 2020/01/01 10:00:00 -0500 PST)" "\r\n"
R"(... tzoffset -28800)" "\r\n"
R"(... serverUptime 1234:12:34)" "\r\n"
R"(... serverVersion P4D/LINUX33X86_64/2020.4/1234567 (2020/06/06))" "\r\n"
R"(... serverEncryption encrypted)" "\r\n"
R"(... serverCertExpires Dec 24 04:10:00 3030 GMT)" "\r\n"
R"(... ServerID unittest)" "\r\n"
R"(... serverServices edge-server)" "\r\n"
R"(... authServer ssl:127.0.0.1:1666)" "\r\n"
R"(... changeServer ssl:127.0.0.1:1666)" "\r\n"
R"(... serverLicense ssl:127.0.0.1:1666)" "\r\n"
R"(... caseHandling insensitive)" "\r\n"
R"(... replica ssl:127.0.0.1:1666)" "\r\n"
"\r\n";
}
else if (m_commandArgs.starts_with("changes"))
{
m_rawOutput.outputResult =
R"(... change 12345)" "\r\n"
R"(... time 1234565432)" "\r\n"
R"(... user unittest)" "\r\n"
R"(... client unittest_workspace)" "\r\n"
R"(... status pending)" "\r\n"
R"(... changeType public)" "\r\n"
R"(... desc *Lumberyard Auto)" "\r\n"
"\r\n";
}
else if (m_commandArgs.starts_with("fstat"))
{
m_rawOutput.outputResult = m_fstatResponse;
m_rawOutput.errorResult = m_fstatErrorResponse;
if (!m_persistFstatResponse)
{
m_fstatResponse = m_fstatErrorResponse = "";
}
}
else if (m_commandArgs.starts_with("add"))
{
if (m_addCallback)
{
m_addCallback(m_commandArgs);
}
}
else if (m_commandArgs.starts_with("edit"))
{
if (m_editCallback)
{
m_editCallback(m_commandArgs);
}
}
else if (m_commandArgs.starts_with("move"))
{
if (m_moveCallback)
{
m_moveCallback(m_commandArgs);
}
}
else if (m_commandArgs.starts_with("delete"))
{
if (m_deleteCallback)
{
m_deleteCallback(m_commandArgs);
}
}
}
void MockPerforceCommand::ExecuteRawCommand()
{
ExecuteCommand();
}
void WaitForSourceControl(AZStd::binary_semaphore& waitSignal)
{
constexpr int WaitTimeMS = 2;
constexpr int TimeLimitMS = 5000;
int retryLimit = TimeLimitMS / WaitTimeMS;
do
{
AZ::TickBus::ExecuteQueuedEvents();
--retryLimit;
}
while (!waitSignal.try_acquire_for(AZStd::chrono::milliseconds(WaitTimeMS)) && retryLimit >= 0);
ASSERT_GE(retryLimit, 0);
}
SourceControlTest::SourceControlTest()
{
BusConnect();
}
SourceControlTest::~SourceControlTest()
{
BusDisconnect();
}
void SourceControlTest::ConnectivityStateChanged(const AzToolsFramework::SourceControlState state)
{
m_connected = (state == AzToolsFramework::SourceControlState::Active);
m_connectSignal.release();
}
void SourceControlTest::EnableSourceControl()
{
AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(
&AzToolsFramework::SourceControlConnectionRequestBus::Events::EnableSourceControl, true);
WaitForSourceControl(m_connectSignal);
ASSERT_TRUE(m_connected);
}
AZ::EntityId CreateDefaultEditorEntity(const char* name, AZ::Entity** outEntity /*= nullptr*/)
{
AZ::EntityId entityId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
entityId, &AzToolsFramework::EditorEntityContextRequestBus::Events::CreateNewEditorEntity, name);
AZ::Entity* entity = GetEntityById(entityId);
entity->Deactivate();
// add required components for the Editor entity
entity->CreateComponent<Components::TransformComponent>();
entity->CreateComponent<Components::EditorLockComponent>();
entity->CreateComponent<Components::EditorVisibilityComponent>();
// This is necessary to prevent a warning in the undo system.
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity,
entity->GetId());
entity->Activate();
if (outEntity)
{
*outEntity = entity;
}
return entity->GetId();
}
AZ::EntityId CreateEditorLayerEntity(const char* name, AZ::Entity** outEntity /*= nullptr*/)
{
AZ::Entity* entity = nullptr;
CreateDefaultEditorEntity(name, &entity);
auto layer = aznew Layers::EditorLayerComponent();
AZStd::vector<AZ::Component*> newComponents{ layer };
EntityCompositionRequests::AddExistingComponentsOutcome componentAddResult;
EntityCompositionRequestBus::BroadcastResult(
componentAddResult, &EntityCompositionRequests::AddExistingComponentsToEntityById,
entity->GetId(), newComponents);
if (outEntity)
{
*outEntity = entity;
}
return entity->GetId();
}
AZ::Data::AssetId SaveAsSlice(
AZStd::vector<AZ::Entity*> entities, AzToolsFramework::ToolsApplication* toolsApplication,
SliceAssets& inoutSliceAssets)
{
AZ::Entity* sliceEntity = aznew AZ::Entity();
AZ::SliceComponent* sliceComponent = aznew AZ::SliceComponent();
sliceComponent->SetSerializeContext(toolsApplication->GetSerializeContext());
for (const auto& entity : entities)
{
sliceComponent->AddEntity(entity);
}
// don't activate `sliceEntity`, whose purpose is to be attached by `sliceComponent`
sliceEntity->AddComponent(sliceComponent);
AZ::Data::AssetId assetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0);
AZ::Data::Asset<AZ::SliceAsset> sliceAssetHolder =
AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
sliceAssetHolder.GetAs<AZ::SliceAsset>()->SetData(sliceEntity, sliceComponent);
// hold on to sliceAssetHolder so it's not ref-counted away
inoutSliceAssets.emplace(assetId, sliceAssetHolder);
return assetId;
}
AZ::SliceComponent::EntityList InstantiateSlice(
AZ::Data::AssetId sliceAssetId, const SliceAssets& sliceAssets)
{
auto foundItr = sliceAssets.find(sliceAssetId);
AZ_Assert(foundItr != sliceAssets.end(), "sliceAssetId not in sliceAssets");
AZ::SliceComponent* rootSlice;
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
rootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
AZ::SliceComponent::SliceInstanceAddress sliceInstAddress = rootSlice->AddSlice(foundItr->second);
rootSlice->Instantiate();
const AZ::SliceComponent::InstantiatedContainer* instanceContainer =
sliceInstAddress.GetInstance()->GetInstantiated();
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequestBus::Events::HandleEntitiesAdded,
instanceContainer->m_entities);
return instanceContainer->m_entities;
}
void DestroySlices(SliceAssets& sliceAssets)
{
AZ::SliceComponent* rootSlice = nullptr;
AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
rootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
for (const auto& sliceAssetPair : sliceAssets)
{
rootSlice->RemoveSlice(sliceAssetPair.second);
}
sliceAssets.clear();
}
} // namespace UnitTest
@@ -0,0 +1,319 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Slice/SliceAsset.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzTest/AzTest.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AZTestShared/Utils/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
#include <AzToolsFramework/SourceControl/PerforceConnection.h>
#include <ostream>
AZ_PUSH_DISABLE_WARNING(4127, "-Wunknown-warning-option") // warning suppressed: constant used in conditional expression
#include <QtTest/QtTest>
AZ_POP_DISABLE_WARNING
namespace AZ
{
class Entity;
class EntityId;
} // namespace AZ
namespace UnitTest
{
/// Test widget to store QActions generated by EditorTransformComponentSelection.
class TestWidget
: public QWidget
{
public:
TestWidget()
: QWidget()
{
// ensure TestWidget can intercept and filter any incoming events itself
installEventFilter(this);
}
bool eventFilter(QObject* watched, QEvent* event) override;
};
/// Stores actions registered for either normal mode (regular viewport) editing and
/// component mode editing.
class TestEditorActions
: private AzToolsFramework::EditorActionRequestBus::Handler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
{
// EditorActionRequestBus ...
void AddActionViaBus(int id, QAction* action) override;
void RemoveActionViaBus(QAction* action) override;
void EnableDefaultActions() override;
void DisableDefaultActions() override;
void AttachOverride(QWidget* /*object*/) override {}
void DetachOverride() override {}
// EditorComponentModeNotificationBus ...
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override;
public:
void Connect();
void Disconnect();
TestWidget m_componentModeWidget; /// Widget to hold component mode actions.
TestWidget m_defaultWidget; /// Widget to hold normal viewport/editor actions.
};
//! Used to intercept various messages which get printed to the console during the startup of a tools application
//! but are not relevant for testing.
class ToolsApplicationMessageHandler
{
public:
ToolsApplicationMessageHandler();
private:
AZStd::unique_ptr<ErrorHandler> m_gridMateMessageHandler;
AZStd::unique_ptr<ErrorHandler> m_enginePathMessageHandler;
AZStd::unique_ptr<ErrorHandler> m_skippingDriveMessageHandler;
AZStd::unique_ptr<ErrorHandler> m_storageDriveMessageHandler;
AZStd::unique_ptr<ErrorHandler> m_jsonComponentErrorHandler;
};
/// Base fixture for ToolsApplication editor tests.
class ToolsApplicationFixture
: public AllocatorsTestFixture
, private AzToolsFramework::NewViewportInteractionModelEnabledRequestBus::Handler
{
public:
void SetUp() override final
{
using AzToolsFramework::GetEntityContextId;
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
AllocatorsTestFixture::SetUp();
if (!GetApplication())
{
// Create & Start a new ToolsApplication if there's no existing one
m_app = AZStd::make_unique<AzToolsFramework::ToolsApplication>();
m_app->Start(AzFramework::Application::Descriptor());
}
// without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
AzToolsFramework::NewViewportInteractionModelEnabledRequestBus::Handler::BusConnect();
m_editorActions.Connect();
const auto viewportHandlerBuilder =
[this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache)
{
// create the default viewport (handles ComponentMode)
AZStd::unique_ptr<AzToolsFramework::EditorDefaultSelection> defaultSelection =
AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache);
// override the phantom widget so we can use out custom test widget
defaultSelection->SetOverridePhantomWidget(&m_editorActions.m_componentModeWidget);
return defaultSelection;
};
// setup default editor interaction model with the phantom widget overridden
EditorInteractionSystemViewportSelectionRequestBus::Event(
GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
viewportHandlerBuilder);
SetUpEditorFixtureImpl();
}
void TearDown() override final
{
using AzToolsFramework::GetEntityContextId;
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
// Reset back to Default Handler to prevent having a handler with dangling "this" pointer
EditorInteractionSystemViewportSelectionRequestBus::Event(
GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetDefaultHandler);
TearDownEditorFixtureImpl();
m_editorActions.Disconnect();
AzToolsFramework::NewViewportInteractionModelEnabledRequestBus::Handler::BusDisconnect();
// Stop & delete the Application created by this fixture, hence not using GetApplication() here
if (m_app)
{
m_app->Stop();
m_app.reset();
}
AllocatorsTestFixture::TearDown();
}
virtual void SetUpEditorFixtureImpl() {}
virtual void TearDownEditorFixtureImpl() {}
AzToolsFramework::ToolsApplication* GetApplication()
{
if (m_app)
{
return m_app.get();
}
// Fallback to ToolsApplication created externally
AZ::ComponentApplication* app = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(app, &AZ::ComponentApplicationBus::Events::GetApplication);
// It's a valid case if GetApplication returns null here, it means there's no externally created Application exists.
auto* toolsApp = azdynamic_cast<AzToolsFramework::ToolsApplication*>(app);
AZ_Assert(!app || toolsApp, "ComponentApplication must be the ToolsApplication here. Unsuccessful dynamic_cast.");
return toolsApp;
}
protected:
TestEditorActions m_editorActions;
ToolsApplicationMessageHandler m_messageHandler; // used to suppress trace messages in test output
private:
AZStd::unique_ptr<AzToolsFramework::ToolsApplication> m_app;
// NewViewportInteractionModelEnabledRequestBus ...
bool IsNewViewportInteractionModelEnabled() override
{
// default to the new viewport interaction model bus being enabled so the
// manipulator manager is correctly instantiated in EditorDefaultSelection
return true;
}
};
class EditorEntityComponentChangeDetector
: private AzToolsFramework::PropertyEditorEntityChangeNotificationBus::Handler
, private AzToolsFramework::EditorTransformChangeNotificationBus::Handler
, private AzToolsFramework::ToolsApplicationNotificationBus::Handler
{
public:
explicit EditorEntityComponentChangeDetector(const AZ::EntityId entityId);
~EditorEntityComponentChangeDetector();
bool ChangeDetected() const { return !m_componentIds.empty(); }
bool PropertyDisplayInvalidated() const { return m_propertyDisplayInvalidated; }
AZStd::vector<AZ::ComponentId> m_componentIds;
private:
// PropertyEditorEntityChangeNotificationBus ...
void OnEntityComponentPropertyChanged(AZ::ComponentId componentId) override;
// EditorTransformChangeNotificationBus ...
void OnEntityTransformChanged(const AzToolsFramework::EntityIdList& entityIds) override;
// ToolsApplicationNotificationBus ...
void InvalidatePropertyDisplay(AzToolsFramework::PropertyModificationRefreshLevel level) override;
bool m_propertyDisplayInvalidated = false;
};
// Moves the 'Editor' (ToolsApplication) into Component Mode for the given Component type.
// @note Entities must be selected before using EnterComponentMode to ensure
// Component Mode is entered correctly.
template<typename ComponentT>
void EnterComponentMode()
{
using AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus;
ComponentModeSystemRequestBus::Broadcast(
&ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType,
AZ::AzTypeInfo<ComponentT>::Uuid());
}
struct MockPerforceCommand
: AzToolsFramework::PerforceCommand
{
void ExecuteCommand() override;
void ExecuteRawCommand() override;
bool m_persistFstatResponse = false;
AZStd::string m_fstatResponse;
AZStd::string m_fstatErrorResponse;
AZStd::function<void(AZStd::string)> m_addCallback;
AZStd::function<void(AZStd::string)> m_editCallback;
AZStd::function<void(AZStd::string)> m_moveCallback;
AZStd::function<void(AZStd::string)> m_deleteCallback;
};
struct MockPerforceConnection
: AzToolsFramework::PerforceConnection
{
explicit MockPerforceConnection(MockPerforceCommand& command) : PerforceConnection(command)
{
}
};
void WaitForSourceControl(AZStd::binary_semaphore& waitSignal);
struct SourceControlTest
: AzToolsFramework::SourceControlNotificationBus::Handler
{
SourceControlTest();
~SourceControlTest();
void ConnectivityStateChanged(const AzToolsFramework::SourceControlState state) override;
void EnableSourceControl();
bool m_connected = false;
AZStd::binary_semaphore m_connectSignal;
MockPerforceCommand m_command;
};
/// Create an Entity as it would appear in the Editor.
/// Optional second parameter of Entity pointer if required.
AZ::EntityId CreateDefaultEditorEntity(const char* name, AZ::Entity** outEntity = nullptr);
/// Create a Layer Entity as it would appear in the Editor.
/// Optional second parameter of Entity pointer if required.
AZ::EntityId CreateEditorLayerEntity(const char* name, AZ::Entity** outEntity = nullptr);
using SliceAssets = AZStd::unordered_map<AZ::Data::AssetId, AZ::Data::Asset<AZ::SliceAsset>>;
/// This function transfers the ownership of all the entity pointers - do not delete or use them afterwards.
AZ::Data::AssetId SaveAsSlice(
AZStd::vector<AZ::Entity*> entities, AzToolsFramework::ToolsApplication* toolsApplication,
SliceAssets& inoutSliceAssets);
/// Instantiate the entities from the saved slice asset.
AZ::SliceComponent::EntityList InstantiateSlice(
AZ::Data::AssetId sliceAssetId, const SliceAssets& sliceAssets);
/// Destroy all the created slice assets.
void DestroySlices(SliceAssets& sliceAssets);
} // namespace UnitTest