Files
UnrealEngineUWP/Engine/Source/Editor/GameProjectGeneration/Private/Tests/GameProjectAutomationTests.cpp
bryan sefcik b93a6cf7ed Pass 1 on editor include fixes:
Removed redundant private include paths from build.cs files.
Fixed include paths to be relative to the private or public folders.
Hid or removed includes that reached into other private module folders.
Updated PublicInclude paths when necessary.

#jira
#preflight 631e283bec5b0c765fc0ffdb

[CL 21960084 by bryan sefcik in ue5-main branch]
2022-09-11 18:33:06 -04:00

372 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "HAL/FileManager.h"
#include "Misc/Paths.h"
#include "Misc/AutomationTest.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Class.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Builders/CubeBuilder.h"
#include "GameFramework/PlayerStart.h"
#include "Editor.h"
#include "LevelEditorViewport.h"
#include "FileHelpers.h"
#include "ProjectDescriptor.h"
#include "IContentSource.h"
#include "GameProjectUtils.h"
#include "SProjectDialog.h"
#include "DesktopPlatformModule.h"
#include "Tests/AutomationTestSettings.h"
#include "Tests/AutomationEditorCommon.h"
#include "TemplateCategory.h"
#include "TemplateItem.h"
#if WITH_DEV_AUTOMATION_TESTS
DEFINE_LOG_CATEGORY_STATIC(LogGameProjectGenerationTests, Log, All);
namespace GameProjectAutomationUtils
{
/**
* Generates the desired project file name
*/
static FString GetDesiredProjectFilename()
{
UAutomationTestSettings const* AutomationTestSettings = GetDefault<UAutomationTestSettings>();
check(AutomationTestSettings);
FString ProjectName;
const FString ProjectNameOverride = AutomationTestSettings->BuildPromotionTest.NewProjectSettings.NewProjectNameOverride;
if (ProjectNameOverride.Len())
{
ProjectName = ProjectNameOverride;
}
else
{
ProjectName = TEXT("NewTestProject");
}
FString ProjectPath;
const FString ProjectPathOverride = AutomationTestSettings->BuildPromotionTest.NewProjectSettings.NewProjectFolderOverride.Path;
if (ProjectPathOverride.Len())
{
ProjectPath = ProjectPathOverride;
}
else
{
ProjectPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*FAutomationTestFramework::Get().GetUserAutomationDirectory());
}
const FString Filename = ProjectName + TEXT(".") + FProjectDescriptor::GetExtension();
FString ProjectFilename = FPaths::Combine(*ProjectPath, *ProjectName, *Filename);
FPaths::MakePlatformFilename(ProjectFilename);
return ProjectFilename;
}
/*
* Create a project from a template with a given criteria
* @oaram InTemplates List of available project templates
* @param InTargetedHardware Target hardware (EHardwareClass)
* @param InGraphicPreset Graphics preset (EGraphicsPreset)
* @param InCategory Target category (EContentSourceCategory)
* @param bInCopyStarterContent Should the starter content be copied also
* @param OutMatchedProjects Total projects matching criteria
* @param OutCreatedProjects Total projects succesfully created
*/
static void CreateProjectSet(TMap<FName, TArray<TSharedPtr<FTemplateItem>> >& InTemplates, EHardwareClass InTargetedHardware, EGraphicsPreset InGraphicPreset, FName InCategory, bool bInCopyStarterContent, int32 &OutCreatedProjects, int32 &OutMatchedProjects)
{
// If this is empty, it will use the same name for each project, otherwise it will create a project based on target platform and source template
FString TestRootFolder;// = "ProjectTests";
// This has the code remove the projects once created
bool bRemoveCreatedProjects = true;
OutCreatedProjects = 0;
OutMatchedProjects = 0;
// Iterate all templates and try to create those that match the required criteria
for (auto& EachTemplate : InTemplates)
{
// Check this is the correct category
if (EachTemplate.Key == InCategory)
{
// Now iterate each template in the category
for (TSharedPtr<FTemplateItem> OneTemplate : EachTemplate.Value)
{
TSharedPtr<FTemplateItem> Item = OneTemplate;
// loop over the code project file and the blueprint file
FString ProjectFile = Item->BlueprintProjectFile;
for (int i = 0; i < 2; ++i)
{
if (ProjectFile.IsEmpty())
continue;
FString DesiredProjectFilename;
if (TestRootFolder.IsEmpty() == true)
{
// Same name for all
DesiredProjectFilename = GameProjectAutomationUtils::GetDesiredProjectFilename();
}
else
{
// Unique names
FString Hardware;
if (InTargetedHardware == EHardwareClass::Desktop)
{
Hardware = TEXT("Dsk");
}
else
{
Hardware = TEXT("Mob");
}
FString ProjectName = FPaths::GetCleanFilename(ProjectFile).Replace(TEXT("TP_"), TEXT(""));
FString ProjectDirName = FPaths::GetBaseFilename(ProjectFile).Replace(TEXT("TP_"), TEXT(""));
FString BasePath = FPaths::RootDir();
DesiredProjectFilename = FString::Printf(TEXT("%s/%s/%s%s/%s%s"), *BasePath, *TestRootFolder, *Hardware, *ProjectDirName, *Hardware, *ProjectName);
}
// If the project already exists, delete it just in case things were left in a bad state.
if (IFileManager::Get().DirectoryExists(*FPaths::GetPath(DesiredProjectFilename)))
{
IFileManager::Get().DeleteDirectory(*FPaths::GetPath(DesiredProjectFilename), /*RequireExists=*/false, /*Tree=*/true);
}
// Setup creation parameters
FText FailReason, FailLog;
FProjectInformation ProjectInfo;
ProjectInfo.ProjectFilename = DesiredProjectFilename;
ProjectInfo.bShouldGenerateCode = ProjectFile == Item->CodeProjectFile;
ProjectInfo.bCopyStarterContent = bInCopyStarterContent;
ProjectInfo.TemplateFile = ProjectFile;
ProjectInfo.TargetedHardware = InTargetedHardware;
ProjectInfo.DefaultGraphicsPerformance = InGraphicPreset;
TArray<FString> CreatedFiles;
OutMatchedProjects++;
// Finally try to create the project
if (!GameProjectUtils::CreateProject(ProjectInfo, FailReason, FailLog, &CreatedFiles))
{
// Failed, report the reason
UE_LOG(LogGameProjectGenerationTests, Error, TEXT("Failed to create %s project %s based on %s. Reason: %s\nProject Creation Failure Log:\n%s"), *InCategory.ToString(), *DesiredProjectFilename, *Item->Name.ToString(), *FailReason.ToString(), *FailLog.ToString());
}
else
{
// Created ok
OutCreatedProjects++;
// Now remove the files we just created (if required)
if (bRemoveCreatedProjects == true) //-V547
{
FString RootFolder = FPaths::GetPath(DesiredProjectFilename);
GameProjectUtils::DeleteCreatedFiles(RootFolder, CreatedFiles);
}
}
}
ProjectFile = Item->CodeProjectFile;
}
}
}
return;
}
}
/**
* Automation test to clean up old test project files
*/
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FBuildPromotionNewProjectCleanupTest, "System.Promotion.Project Promotion Pass.Step 1 Blank Project Creation.Cleanup Potential Project Location", /*EAutomationTestFlags::Disabled |*/ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter );
bool FBuildPromotionNewProjectCleanupTest::RunTest(const FString& Parameters)
{
FString DesiredProjectFilename = GameProjectAutomationUtils::GetDesiredProjectFilename();
if (FPaths::FileExists(DesiredProjectFilename))
{
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Found an old project file at %s"), *DesiredProjectFilename);
if (FPaths::IsProjectFilePathSet())
{
FString CurrentProjectPath = FPaths::GetProjectFilePath();
if (CurrentProjectPath == DesiredProjectFilename)
{
UE_LOG(LogGameProjectGenerationTests, Warning, TEXT("Can not clean up the target project location because it is the current active project."));
}
else
{
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Removing files from old project path: %s"), *FPaths::GetPath(DesiredProjectFilename));
bool bEnsureExists = false;
bool bDeleteEntireTree = true;
IFileManager::Get().DeleteDirectory(*FPaths::GetPath(DesiredProjectFilename), bEnsureExists, bDeleteEntireTree);
}
}
}
else
{
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Target project location is clear"));
}
return true;
}
/**
* Automation test to create a new project
*/
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FBuildPromotionNewProjectCreateTest, "System.Promotion.Project Promotion Pass.Step 1 Blank Project Creation.Create Project", /*EAutomationTestFlags::Disabled |*/ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter | EAutomationTestFlags::RequiresUser);
bool FBuildPromotionNewProjectCreateTest::RunTest(const FString& Parameters)
{
FString DesiredProjectFilename = GameProjectAutomationUtils::GetDesiredProjectFilename();
if (FPaths::FileExists(DesiredProjectFilename))
{
UE_LOG(LogGameProjectGenerationTests, Warning, TEXT("A project already exists at the target location: %s"), *DesiredProjectFilename);
const FString OldProjectFolder = FPaths::GetPath(DesiredProjectFilename);
const FString OldProjectName = FPaths::GetBaseFilename(DesiredProjectFilename);
const FString RootFolder = FPaths::GetPath(OldProjectFolder);
//Add a number to the end
for (uint32 i = 2;; ++i)
{
const FString PossibleProjectName = FString::Printf(TEXT("%s%i"), *OldProjectName, i);
const FString PossibleProjectFilename = FString::Printf(TEXT("%s/%s/%s.%s"), *RootFolder, *PossibleProjectName, *PossibleProjectName, *FProjectDescriptor::GetExtension());
if (!FPaths::FileExists(PossibleProjectFilename))
{
DesiredProjectFilename = PossibleProjectFilename;
UE_LOG(LogGameProjectGenerationTests, Warning, TEXT("Changing the target project name to: %s"), *FPaths::GetBaseFilename(DesiredProjectFilename));
break;
}
}
}
FText FailReason, FailLog;
FProjectInformation ProjectInfo;
ProjectInfo.ProjectFilename = DesiredProjectFilename;
ProjectInfo.bCopyStarterContent = true;
if (GameProjectUtils::CreateProject(ProjectInfo, FailReason, FailLog))
{
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Generated a new project: %s"), *DesiredProjectFilename);
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Test successful!"));
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("\nPlease switch to the new project and continue to Step 2."));
}
else
{
UE_LOG(LogGameProjectGenerationTests, Error, TEXT("Could not generate new project: %s - %s"), *FailReason.ToString(), *FailLog.ToString());
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Test failed!"));
}
return true;
}
/**
* Automation test to create a simple level and save it
*/
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FBuildPromotionNewProjectMapTest, "System.Promotion.Project Promotion Pass.Step 2 Basic Level Creation.Create Basic Level", /*EAutomationTestFlags::Disabled |*/ EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter);
bool FBuildPromotionNewProjectMapTest::RunTest(const FString& Parameters)
{
//New level
UWorld* CurrentWorld = FAutomationEditorCommonUtils::CreateNewMap();
if (!CurrentWorld)
{
UE_LOG(LogGameProjectGenerationTests, Error, TEXT("Failed to create an empty level"));
return false;
}
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Adding Level Geometry"));
//Add some bsp and a player start
GEditor->Exec(CurrentWorld, TEXT("BRUSH Scale 1 1 1"));
for(FLevelEditorViewportClient* ViewportClient : GEditor->GetLevelViewportClients())
{
if (!ViewportClient->IsOrtho())
{
ViewportClient->SetViewLocation(FVector(176, 2625, 2075));
ViewportClient->SetViewRotation(FRotator(319, 269, 1));
}
}
ULevel* CurrentLevel = CurrentWorld->GetCurrentLevel();
//Cube Additive Brush
UCubeBuilder* CubeAdditiveBrushBuilder = Cast<UCubeBuilder>(GEditor->FindBrushBuilder(UCubeBuilder::StaticClass()));
CubeAdditiveBrushBuilder->X = 4096.0f;
CubeAdditiveBrushBuilder->Y = 4096.0f;
CubeAdditiveBrushBuilder->Z = 128.0f;
CubeAdditiveBrushBuilder->Build(CurrentWorld);
GEditor->Exec(CurrentWorld, TEXT("BRUSH MOVETO X=0 Y=0 Z=0"));
GEditor->Exec(CurrentWorld, TEXT("BRUSH ADD"));
//Add a playerstart
const FTransform Transform(FRotator(-16384, 0, 0), FVector(0.f, 1750.f, 166.f));
AActor* PlayerStart = GEditor->AddActor(CurrentWorld->GetCurrentLevel(), APlayerStart::StaticClass(), Transform);
if (PlayerStart)
{
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Added a player start"));
}
else
{
UE_LOG(LogGameProjectGenerationTests, Error, TEXT("Failed to add a player start"));
}
// Save the map
FString PathToSave = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir() + TEXT("Maps/NewProjectTest.umap"));
FEditorFileUtils::SaveLevel(CurrentLevel, PathToSave);
UE_LOG(LogGameProjectGenerationTests, Display, TEXT("Saved map"));
return true;
}
/*
* Template project creation test
*/
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FCreateBPTemplateProjectAutomationTests, "System.Promotion.Project Promotion Pass.Step 3 NewProjectCreationTests.CreateBlueprintProjects", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter /*| EAutomationTestFlags::Disabled*/)
/**
* Uses the new project wizard to locate all templates available for new blueprint project creation and verifies creation succeeds.
*
* @param Parameters - Unused for this test
* @return TRUE if the test was successful, FALSE otherwise
*/
bool FCreateBPTemplateProjectAutomationTests::RunTest(const FString& Parameters)
{
TMap<FName, TArray<TSharedPtr<FTemplateItem>> > Templates = SProjectDialog::FindTemplateProjects();
int32 OutMatchedProjectsDesk = 0;
int32 OutCreatedProjectsDesk = 0;
GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Desktop, EGraphicsPreset::Maximum, "Game", false, OutMatchedProjectsDesk, OutCreatedProjectsDesk);
int32 OutMatchedProjectsMob = 0;
int32 OutCreatedProjectsMob = 0;
GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Mobile, EGraphicsPreset::Maximum, "Visualization", false, OutMatchedProjectsMob, OutCreatedProjectsMob);
return ( OutMatchedProjectsDesk == OutCreatedProjectsDesk ) && ( OutMatchedProjectsMob == OutCreatedProjectsMob );
}
/*
* Template project creation test
*/
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FCreateCPPTemplateProjectAutomationTests, "System.Promotion.Project Promotion Pass.Step 3 NewProjectCreationTests.CreateCodeProjects", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter /*| EAutomationTestFlags::Disabled*/)
/**
* Uses the new project wizard to locate all templates available for new code project creation and verifies creation succeeds.
*
* @param Parameters - Unused for this test
* @return TRUE if the test was successful, FALSE otherwise
*/
bool FCreateCPPTemplateProjectAutomationTests::RunTest(const FString& Parameters)
{
TMap<FName, TArray<TSharedPtr<FTemplateItem>> > Templates = SProjectDialog::FindTemplateProjects();
int32 OutMatchedProjectsDesk = 0;
int32 OutCreatedProjectsDesk = 0;
GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Desktop, EGraphicsPreset::Maximum, "Game", false, OutMatchedProjectsDesk, OutCreatedProjectsDesk);
int32 OutMatchedProjectsMob = 0;
int32 OutCreatedProjectsMob = 0;
GameProjectAutomationUtils::CreateProjectSet(Templates, EHardwareClass::Mobile, EGraphicsPreset::Maximum, "Game", false, OutMatchedProjectsMob, OutCreatedProjectsMob);
return (OutMatchedProjectsDesk == OutCreatedProjectsDesk) && (OutMatchedProjectsMob == OutCreatedProjectsMob);
}
#endif //WITH_DEV_AUTOMATION_TESTS