// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "DisplayClusterConfigurationTypes_Viewport.h" #if WITH_DEV_AUTOMATION_TESTS #include "Kismet2/BlueprintEditorUtils.h" #include "PropertyEditorModule.h" #include "PropertyHandle.h" class UDisplayClusterBlueprint; class UDisplayClusterConfigurationCluster; class UDisplayClusterConfigurationClusterNode; /** * Utility functions used for display cluster tests. **/ namespace DisplayClusterTestUtils { /** * Create a display cluster asset using a new UDisplayClusterConfiguratorFactory. * If the asset is invalid or not a UDisplayClusterBlueprint, the package will be immediately cleaned up. */ UDisplayClusterBlueprint* CreateDisplayClusterAsset(); /** * Add a cluster node to a cluster asset. This will automatically create the template node and discard it when * finished. */ UDisplayClusterConfigurationClusterNode* AddClusterNodeToCluster(UBlueprint* Blueprint, UDisplayClusterConfigurationCluster* RootCluster, FString Name = "", bool bCallPostEditChange = true); /** * Add a viewport to a cluster asset. This will automatically create the template viewport and discard it when * finished. */ UDisplayClusterConfigurationViewport* AddViewportToClusterNode(UBlueprint* Blueprint, UDisplayClusterConfigurationClusterNode* Node, FString Name = "", bool bCallPostEditChange = true); /** * Create a world and set it as the current world. */ UWorld* CreateWorld(); /** * Clean up a package that was created during a test. */ void CleanUpPackage(UPackage* Package); /** * Clean up an asset that was created during a test. */ void CleanUpAsset(UObject* Asset); /** * Clean up an asset that was created during a test as well as its containing package (if the package exists). */ void CleanUpAssetAndPackage(UObject* Asset); /** * Clean up a world that was created during a test. */ void CleanUpWorld(UWorld* World); /** * Get a property view based on a list of nested field names. * * @param Owner The root owner of the field. * @param FieldNames A list of field names, where the first name is the top-most field, and subsequent names are nested fields (e.g. a field within a struct). * @param bAllowAdd If true and one of the fields encountered is a container, an element will be added. If false, this will return false in the same situation. * @param OutPropertyView The property will be stored here. * @param OutPropertyHandle The property handle will be stored here. * * @return Whether the property view and handle were successfully found. */ bool GetPropertyViewAndHandleFromFieldNames(UObject* Owner, const TArray& FieldNames, bool bAllowAdd, TSharedPtr& OutPropertyView, TSharedPtr& OutPropertyHandle); /** * Set the value of a property handle. */ template FPropertyAccess::Result SetPropertyHandleValue(TSharedPtr Handle, const T& Value); /** * Get the value of a property handle. */ template FPropertyAccess::Result GetPropertyHandleValue(TSharedPtr Handle, T& OutValue); /** * Set a property (or nested property) of an object using a PropertyView and notify its root blueprint of any * changes as if the change happened through the Blueprint editor. * * @param Owner The root owner of the field. * @param Blueprint The Blueprint asset to notify after changing the value (optional). * @param FieldNames A list of field names, where the first name is the top-most field, and subsequent names are nested fields (e.g. a field within a struct). * @param Value The value to set the field to. * * @return Whether the field was successfully found and set. */ template bool SetBlueprintPropertyValue(UObject* Owner, UBlueprint* Blueprint, const TArray& FieldNames, const T& Value); /** * Get the value of a property (or nested property) of an object using a PropertyView. * * @param Owner The root owner of the field. * @param FieldNames A list of field names, where the first name is the top-most field, and subsequent names are nested fields (e.g. a field within a struct). * @param OutValue The value of the property will be stored here. * * @return Whether the value was successfully retrieved. */ template bool GetBlueprintPropertyValue(UObject* Owner, const TArray& FieldNames, T& OutValue); } template FPropertyAccess::Result DisplayClusterTestUtils::SetPropertyHandleValue(TSharedPtr Handle, const T& Value) { return Handle->SetValue(Value); } template FPropertyAccess::Result DisplayClusterTestUtils::GetPropertyHandleValue(TSharedPtr Handle, T& OutValue) { return Handle->GetValue(OutValue); } // Get/set specializations for colors since they have to be set/retrieved as strings rather than directly template <> inline FPropertyAccess::Result DisplayClusterTestUtils::SetPropertyHandleValue(TSharedPtr Handle, const FLinearColor& Value) { return Handle->SetValueFromFormattedString(Value.ToString()); } template <> inline FPropertyAccess::Result DisplayClusterTestUtils::GetPropertyHandleValue(TSharedPtr Handle, FLinearColor& Value) { FString StringData; const FPropertyAccess::Result Result = Handle->GetValueAsFormattedString(StringData); if (Result == FPropertyAccess::Success) { Value.InitFromString(StringData); } else { Value = FLinearColor(); } return Result; } template bool DisplayClusterTestUtils::SetBlueprintPropertyValue(UObject* Owner, UBlueprint* Blueprint, const TArray& FieldNames, const T& OutValue) { TSharedPtr PropertyView; TSharedPtr PropertyHandle; if (!GetPropertyViewAndHandleFromFieldNames(Owner, FieldNames, true, PropertyView, PropertyHandle)) { return false; } const FPropertyAccess::Result Result = SetPropertyHandleValue(PropertyHandle, OutValue); if (Result != FPropertyAccess::Success) { return false; } if (Blueprint) { // Trigger Blueprint updates as if we were in an editor. This will re-run construction scripts FBlueprintEditorUtils::PostEditChangeBlueprintActors(Blueprint); } return true; } template bool DisplayClusterTestUtils::GetBlueprintPropertyValue(UObject* Owner, const TArray& FieldNames, T& OutValue) { TSharedPtr PropertyView; TSharedPtr PropertyHandle; if (!GetPropertyViewAndHandleFromFieldNames(Owner, FieldNames, false, PropertyView, PropertyHandle)) { return false; } return GetPropertyHandleValue(PropertyHandle, OutValue) == FPropertyAccess::Success; } #endif