2023-03-29 15:02:12 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2023-05-08 15:16:20 -04:00
# include "Components/MapTestSpawner.h"
2023-03-29 15:02:12 -04:00
2024-04-09 16:36:02 -04:00
# if WITH_AUTOMATION_TESTS
2023-11-17 14:30:58 -05:00
# include "Commands/TestCommands.h"
2023-03-29 15:02:12 -04:00
# include "Tests/AutomationCommon.h"
2024-08-05 12:50:36 -04:00
# include "GameDelegates.h"
2023-03-29 15:02:12 -04:00
# include "GameFramework/PlayerController.h"
# include "Engine/Engine.h"
2024-04-09 16:36:02 -04:00
# include "Misc/PackageName.h"
2023-03-29 20:01:48 -04:00
# include "Misc/Paths.h"
2024-04-09 16:36:02 -04:00
# include "UObject/Package.h"
2024-08-05 12:50:36 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogMapTest , Log , All ) ;
2024-04-09 16:36:02 -04:00
# if WITH_EDITOR
2023-11-14 09:29:27 -05:00
# include "Editor.h"
2024-01-23 20:20:04 -05:00
# include "Editor/UnrealEdEngine.h"
2023-11-14 09:29:27 -05:00
# include "HAL/FileManager.h"
2023-11-27 16:19:25 -05:00
# include "LevelEditor.h"
2023-11-14 09:29:27 -05:00
# include "LevelEditorSubsystem.h"
2024-05-28 12:15:48 -04:00
# include "Modules/ModuleManager.h"
2023-11-14 09:29:27 -05:00
# include "Tests/AutomationEditorCommon.h"
2024-01-23 20:20:04 -05:00
# include "UnrealEdGlobals.h"
2023-11-14 09:29:27 -05:00
namespace {
static const FString TempMapDirectory = FPaths : : Combine ( FPaths : : ProjectContentDir ( ) , TEXT ( " CQTestMapTemp " ) ) ;
/**
* Generates a unique random 8 character map name .
*/
FString GenerateUniqueMapName ( )
{
FString UniqueMapName = FGuid : : NewGuid ( ) . ToString ( ) ;
UniqueMapName . LeftInline ( 8 ) ;
return UniqueMapName ;
}
/**
* Cleans up all created resources .
*/
void CleanupTempResources ( )
{
2024-08-12 14:22:23 -04:00
const bool bDirectoryMustExist = true ;
const bool bRemoveRecursively = true ;
const bool bWasDeleted = IFileManager : : Get ( ) . DeleteDirectory ( * TempMapDirectory , bDirectoryMustExist , bRemoveRecursively ) ;
2023-11-14 09:29:27 -05:00
check ( bWasDeleted ) ;
}
} //anonymous
2024-04-09 16:36:02 -04:00
# endif // WITH_EDITOR
2023-11-27 16:19:25 -05:00
2024-08-05 12:50:36 -04:00
FMapTestSpawner : : ~ FMapTestSpawner ( )
{
// Only explicitly removing the 'EndPlayMapHandle' handle as either the `OnEndPlayMap` gets triggered and the Game/PIE Worlds are no longer valid
// Or we are ending the test and the `FSpawnHelper` will handle cleaning up of the GameWorld
if ( EndPlayMapHandle . IsValid ( ) )
{
FGameDelegates : : Get ( ) . GetEndPlayMapDelegate ( ) . Remove ( EndPlayMapHandle ) ;
EndPlayMapHandle . Reset ( ) ;
}
}
2023-11-14 09:29:27 -05:00
TUniquePtr < FMapTestSpawner > FMapTestSpawner : : CreateFromTempLevel ( FTestCommandBuilder & InCommandBuilder )
{
2024-04-09 16:36:02 -04:00
# if WITH_EDITOR
2024-01-23 20:20:04 -05:00
if ( IsValid ( GUnrealEd - > PlayWorld ) )
{
UE_LOG ( LogMapTest , Verbose , TEXT ( " Active PIE session '%s' needs to be shutdown before a creation of a new level can occur. " ) , * GUnrealEd - > PlayWorld - > GetMapName ( ) ) ;
GUnrealEd - > EndPlayMap ( ) ;
}
2023-11-14 09:29:27 -05:00
FString MapName = GenerateUniqueMapName ( ) ;
FString MapPath = FPaths : : Combine ( TempMapDirectory , MapName ) ;
FString NewLevelPackage = FPackageName : : FilenameToLongPackageName ( MapPath ) ;
ULevelEditorSubsystem * LevelEditorSubsystem = GEditor - > GetEditorSubsystem < ULevelEditorSubsystem > ( ) ;
bool bWasTempLevelCreated = LevelEditorSubsystem - > NewLevel ( NewLevelPackage ) ;
check ( bWasTempLevelCreated ) ;
TUniquePtr < FMapTestSpawner > Spawner = MakeUnique < FMapTestSpawner > ( TempMapDirectory , MapName ) ;
2024-08-05 12:50:36 -04:00
InCommandBuilder . OnTearDown ( [ ] ( ) {
2023-11-14 09:29:27 -05:00
// Create a new map to free up the reference to the map used during testing before cleaning up all temporary resources
FAutomationEditorCommonUtils : : CreateNewMap ( ) ;
CleanupTempResources ( ) ;
} ) ;
return MoveTemp ( Spawner ) ;
2024-04-09 16:36:02 -04:00
# else
checkf ( false , TEXT ( " CreateFromTempLevel can't create a new level if WITH_EDITOR=false " ) ) ;
return nullptr ;
# endif // WITH_EDITOR
2023-11-14 09:29:27 -05:00
}
2023-03-29 15:02:12 -04:00
void FMapTestSpawner : : AddWaitUntilLoadedCommand ( FAutomationTestBase * TestRunner )
{
check ( PieWorld = = nullptr ) ;
2024-01-23 20:20:04 -05:00
FString PackagePath ;
const FString Path = FPaths : : Combine ( MapDirectory , MapName ) ;
bool bPackageExists = FPackageName : : DoesPackageExist ( Path , & PackagePath ) ;
2024-04-09 16:36:02 -04:00
checkf ( bPackageExists , TEXT ( " Could not get package from path '%s' " ) , * Path ) ;
2024-01-23 20:20:04 -05:00
2024-04-09 16:36:02 -04:00
// We need to retrieve the LongPackageName from the PackagePath to be able to load the map for both Editor and Target builds
FString LongPackageName , PackageConversionError ;
bool bFilenameConverted = FPackageName : : TryConvertFilenameToLongPackageName ( PackagePath , LongPackageName , & PackageConversionError ) ;
checkf ( bFilenameConverted , TEXT ( " Could not get LongPackageName. Error: '%s' " ) , * PackageConversionError ) ;
bool bOpened = AutomationOpenMap ( LongPackageName , true ) ;
2023-03-29 15:02:12 -04:00
check ( bOpened ) ;
2024-08-05 12:50:36 -04:00
ADD_LATENT_AUTOMATION_COMMAND ( FWaitUntil ( * TestRunner , [ this ] ( ) - > bool {
for ( const FWorldContext & Context : GEngine - > GetWorldContexts ( ) )
2023-03-29 15:02:12 -04:00
{
2024-04-09 16:36:02 -04:00
UWorld * World = Context . World ( ) ;
if ( ! IsValid ( World ) )
{
continue ;
}
// We only want to set our PieWorld if the loaded World name matches our expected World name
FString WorldMapName = FPackageName : : GetShortName ( World - > GetMapName ( ) ) ;
2024-06-19 11:43:46 -04:00
WorldMapName = UWorld : : RemovePIEPrefix ( WorldMapName ) ;
2024-04-09 16:36:02 -04:00
if ( ( ( Context . WorldType = = EWorldType : : PIE ) | | ( Context . WorldType = = EWorldType : : Game ) ) & & ( WorldMapName . Equals ( MapName ) ) )
2023-03-29 15:02:12 -04:00
{
PieWorld = Context . World ( ) ;
2024-08-05 12:50:36 -04:00
EndPlayMapHandle = FGameDelegates : : Get ( ) . GetEndPlayMapDelegate ( ) . AddRaw ( this , & FMapTestSpawner : : OnEndPlayMap ) ;
2023-03-29 15:02:12 -04:00
return true ;
}
}
return false ;
} ) ) ;
}
UWorld * FMapTestSpawner : : CreateWorld ( )
{
checkf ( PieWorld , TEXT ( " Must call AddWaitUntilLoadedCommand in BEFORE_TEST " ) ) ;
return PieWorld ;
}
APawn * FMapTestSpawner : : FindFirstPlayerPawn ( )
{
2024-04-09 16:36:02 -04:00
APlayerController * PlayerController = GetWorld ( ) . GetFirstPlayerController ( ) ;
// There's a chance that we may not have a PlayerController spawned in the world
if ( ! IsValid ( PlayerController ) )
{
return nullptr ;
}
return PlayerController - > GetPawn ( ) ;
2023-03-29 15:02:12 -04:00
}
2023-11-17 14:30:58 -05:00
2024-08-05 12:50:36 -04:00
void FMapTestSpawner : : OnEndPlayMap ( )
{
2024-08-12 14:22:23 -04:00
if ( ! IsValid ( GEngine - > GetCurrentPlayWorld ( ) ) )
2024-08-05 12:50:36 -04:00
{
UE_LOG ( LogMapTest , Verbose , TEXT ( " Play session has ended. " ) ) ;
GameWorld = nullptr ;
PieWorld = nullptr ;
FGameDelegates : : Get ( ) . GetEndPlayMapDelegate ( ) . Remove ( EndPlayMapHandle ) ;
EndPlayMapHandle . Reset ( ) ;
}
}
2024-04-09 16:36:02 -04:00
# endif // WITH_AUTOMATION_TESTS