in Ambit/Source/Ambit/Mode/ConfigImportExport.spec.cpp [52:1190]
void ConfigImportExportSpec::Define()
{
Describe("OnExportSdf()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [](const FString& FilePath, const FString& OutString) -> bool
{
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
});
It("Should Return FReply::Handled", [this]()
{
const FReply Reply = Exporter->OnExportSdf();
TestTrue("Reply should be true on return", Reply.IsEventHandled());
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = "";
});
});
Describe("DequeueOrDefaultNextSdfConfigToProcess()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
});
Describe("When DequeueOrDefaultNextSdfConfigToProcess() Defaults", [this]()
{
BeforeEach([this]()
{
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->TimeOfDay = 0.1;
AmbitMode->UISettings->PedestrianDensity = 0.2;
AmbitMode->UISettings->VehicleDensity = 0.3;
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
});
It("Should Give On Screen Values (Name Default)", [this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ScenarioName = "";
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
Exporter->ProcessSdfForExport(FakeArray, false);
TestTrue("Json contains Mode's Time of Day", JsonContent.Contains("\"TimeOfDay\": 0.1"));
TestTrue("Json contains Mode's Pedestrian Density", JsonContent.Contains("\"PedestrianDensity\": 0.2"));
TestTrue("Json contains Mode's Vehicle Density", JsonContent.Contains("\"TrafficDensity\": 0.3"));
TestTrue("Json contains Default Scenario Name",
JsonContent.Contains("\"ScenarioName\": \"AmbitScenario\""));
});
It("Should Give On Screen Values (Name Specified)", [this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ScenarioName = "FakeName";
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
Exporter->ProcessSdfForExport(FakeArray, false);
TestTrue("Json contains input scenario came", JsonContent.Contains("\"ScenarioName\": \"FakeName\""));
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("SerializeSpawnerConfigs()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& Content) -> bool
{
return true;
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
Exporter->SetMockPutObjectS3(MockWriteToS3);
});
Describe("When Spawners Exists", [this]()
{
BeforeEach([this]()
{
// ProcessSdfForExport Current Manually looks for Spawners, so we need to use a their instances for testing
// Replace with AMockableSpawner after unifying this process
World->SpawnActor<ASpawnOnSurface>();
World->SpawnActor<ASpawnOnPath>();
World->SpawnActor<ASpawnInVolume>();
World->SpawnActor<ASpawnWithHoudini>();
});
It("Should Contain Spawners in Output", [this]()
{
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
Exporter->ProcessSdfForExport(FakeArray, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* Spawners;
const bool FoundField = ConvertedJson->TryGetObjectField(
JsonConstants::KAllSpawnersConfigsKey, Spawners);
TestTrue("Spawners should be contained in the output", FoundField);
if (FoundField)
{
TestTrue("Surface Spawner should exist in SDF",
Spawners->Get()->HasField(JsonConstants::KSpawnerSurfaceKey));
TestTrue("Path Spawner should exist in SDF",
Spawners->Get()->HasField(JsonConstants::KSpawnerPathKey));
TestTrue("Volume Spawner should exist in SDF",
Spawners->Get()->HasField(JsonConstants::KSpawnerVolumeKey));
TestTrue("Houdini Spawner should exist in SDF",
Spawners->Get()->HasField(JsonConstants::KSpawnerSurfaceHoudiniKey));
}
});
It("Should Condense the Same Spawner Type Down to One Array", [this]()
{
World->SpawnActor<ASpawnOnSurface>();
World->SpawnActor<ASpawnOnSurface>();
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
Exporter->ProcessSdfForExport(FakeArray, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TArray<TSharedPtr<FJsonValue>>* OutputArray = {};
const TSharedPtr<FJsonObject> Spawners = ConvertedJson->GetObjectField(
JsonConstants::KAllSpawnersConfigsKey);
if (Spawners)
{
Spawners->TryGetArrayField(JsonConstants::KSpawnerSurfaceKey, OutputArray);
}
TestEqual("Surface Spawner be an array of 3", OutputArray->Num(), 3);
});
});
Describe("When Spawners Do Not Exist", [this]()
{
It("Should Have the AllSpawnersConfigs Object Inserted", [this]()
{
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
Exporter->ProcessSdfForExport(FakeArray, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* Spawners;
const bool FoundField = ConvertedJson->TryGetObjectField(
JsonConstants::KAllSpawnersConfigsKey, Spawners);
TestTrue("Spawner Config object should exist", FoundField);
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("ProcessSdfForExport()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
});
Describe("Serialize SpawnedObjectsConfig", [this]()
{
BeforeEach([this]()
{
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
});
Describe("When Spawned Objects Exists", [this]()
{
It("Should Contain Spawned Objects in Output (Single)", [this]()
{
const FString Key = "Testing";
const FString ValueString = "{\"Test\": 123}";
const TSharedPtr<FJsonObject> ValueJson = FJsonHelpers::DeserializeJson(ValueString);
TMap<FString, TSharedPtr<FJsonObject>> TestSpawnedObjects;
TestSpawnedObjects.Add(Key, ValueJson);
Exporter->ProcessSdfForExport(TestSpawnedObjects, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* SpawnedItem;
const bool FoundField = ConvertedJson->TryGetObjectField("Testing", SpawnedItem);
TestTrue("Spawned item should be contained in the output", FoundField);
});
It("Should Contain Spawned Objects in Output (Multiple)", [this]()
{
const FString Key1 = "Testing";
const FString ValueString1 = "{\"Test\": 123}";
const TSharedPtr<FJsonObject> ValueJson1 = FJsonHelpers::DeserializeJson(ValueString1);
const FString Key2 = "FakeItem";
const FString ValueString2 = "{\"Fake\": false}";
const TSharedPtr<FJsonObject> ValueJson2 = FJsonHelpers::DeserializeJson(ValueString1);
TMap<FString, TSharedPtr<FJsonObject>> TestSpawnedObjects;
TestSpawnedObjects.Add(Key1, ValueJson1);
TestSpawnedObjects.Add(Key2, ValueJson2);
Exporter->ProcessSdfForExport(TestSpawnedObjects, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* SpawnedItem1;
const bool FoundField1 = ConvertedJson->TryGetObjectField("Testing", SpawnedItem1);
const TSharedPtr<FJsonObject>* SpawnedItem2;
const bool FoundField2 = ConvertedJson->TryGetObjectField("Testing", SpawnedItem2);
TestTrue("Spawned items should be contained in the output", FoundField1);
TestTrue("Spawned items should be contained in the output", FoundField2);
});
It("Should Contain Spawned Object Details", [this]()
{
const FString Key = "Testing";
const FString ValueString = "\"Test\": 123";
const TSharedPtr<FJsonObject> ValueJson = FJsonHelpers::DeserializeJson(ValueString);
TMap<FString, TSharedPtr<FJsonObject>> TestSpawnedObjects;
TestSpawnedObjects.Add(Key, ValueJson);
Exporter->ProcessSdfForExport(TestSpawnedObjects, false);
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* SpawnedItem;
const bool FoundField = ConvertedJson->TryGetObjectField("Testing", SpawnedItem);
if (FoundField)
{
int OutputItem;
const bool FoundSubField = SpawnedItem->Get()->TryGetNumberField("Test", OutputItem);
TestTrue("Key is valid", FoundSubField);
TestEqual("Value matches expected value", OutputItem, 123);
}
});
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("WriteJsonFile()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
Exporter->SetMockWriteFile(MockWrite);
auto MockListBuckets = []() -> TSet<FString>
{
TSet<FString> setBuckets;
setBuckets.Add("BucketName");
return setBuckets;
};
Exporter->SetMockS3ListBuckets(MockListBuckets);
auto MockCreateBucket = [](const FString& Region, const FString& BucketName) -> void
{
};
Exporter->SetMockS3CreateBucket(MockCreateBucket);
});
Describe("When Writing to Disk", [this]()
{
Describe("When a File Path is not Specified from Popup", [this]()
{
It("Should not Write to Disk", [this]()
{
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "";
};
Exporter->SetMockGetPathFromPopup(MockPopup);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, false);
TestFalse("No write should occur", bWroteSuccess);
});
});
});
Describe("When Writing to Amazon S3", [this]()
{
Describe("When a Bucket is Not Valid", [this]()
{
BeforeEach([this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->AwsRegion = "";
AmbitMode->UISettings->S3BucketName = "";
AmbitMode->UISettings->BatchName = "";
});
It("Should Return False and Not Write", [this]()
{
bool bHitS3 = false;
auto MockWriteToS3 = [&bHitS3](const FString& Region, const FString& BucketName,
const FString& ObjectName, const FString& Content) -> bool
{
bHitS3 = true;
return true;
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
auto MockCreateBucket = [](const FString& Region, const FString& BucketName) -> void
{
throw std::invalid_argument("The bucket name or region is empty");
};
Exporter->SetMockS3CreateBucket(MockCreateBucket);
// Must be set before we call the function.
AddExpectedError("The bucket name or region is empty", EAutomationExpectedErrorFlags::Contains, 1);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
TestFalse("Amazon S3 should not be written to", bHitS3);
TestFalse("Process should return failure", bWroteSuccess);
});
});
Describe("When a Bucket is Valid", [this]()
{
BeforeEach([this]()
{
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->AwsRegion = Aws::Region::US_EAST_1;
AmbitMode->UISettings->S3BucketName = "test--config-import-export--process-sdf-for-export";
AmbitMode->UISettings->BatchName = "AmbitTest" + FDateTime::UtcNow().ToString() +
"ProcessSdfForExport";
});
Describe("When an Error Occurs During Put", [this]()
{
It("Should Catch and Log Error (Invalid Argument)", [this]()
{
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName,
const FString& ObjectName, const FString& Content) -> bool
{
throw std::invalid_argument("Test Fail");
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
// Must be set before we call the function.
AddExpectedError("WriteJsonFile failed with invalid argument error: Test Fail",
EAutomationExpectedErrorFlags::Exact, 1);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
TestFalse("Process should return failure", bWroteSuccess);
});
It("Should Catch and Log Error (Runtime Argument)", [this]()
{
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName,
const FString& ObjectName, const FString& Content) -> bool
{
throw std::runtime_error("Test Fail");
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
// Must be set before we call the function.
AddExpectedError("WriteJsonFile failed with runtime error: Test Fail",
EAutomationExpectedErrorFlags::Exact, 1);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
TestFalse("Process should return failure", bWroteSuccess);
});
It("Should Throw Other Error To Caller", [this]()
{
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName,
const FString& ObjectName, const FString& Content) -> bool
{
throw std::domain_error("Test Fail");
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
bool bHitError = false;
try
{
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
}
catch (std::domain_error test)
{
bHitError = true;
}
TestTrue("Error should throw to caller", bHitError);
});
});
It("Should Use Default Configuration Name When No Name is Set", [this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ConfigurationName = "";
FString SpecifiedBscConfigName;
auto MockWriteToS3 = [&SpecifiedBscConfigName](const FString& Region, const FString& BucketName,
const FString& ObjectName,
const FString& Content) -> bool
{
SpecifiedBscConfigName = ObjectName;
return true;
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
TestTrue("Should write successfully", bWroteSuccess);
TestTrue("Name should contain default name",
SpecifiedBscConfigName.Contains("AmbitScenarioConfiguration"));
});
It("Should Use Specified Configuration Name When Name is Set", [this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ConfigurationName = "ConfigImportExportUnitTest";
FString SpecifiedBscConfigName;
auto MockWriteToS3 = [&SpecifiedBscConfigName](const FString& Region, const FString& BucketName,
const FString& ObjectName,
const FString& Content) -> bool
{
SpecifiedBscConfigName = ObjectName;
return true;
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
TestTrue("Should write successfully", bWroteSuccess);
TestTrue("Name should contain default name",
SpecifiedBscConfigName.Contains("ConfigImportExportUnitTest"));
});
It("Should Create A Path for SDF Configuration", [this]()
{
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ConfigurationName = "ConfigImportExportUnitTest";
AmbitMode->UISettings->ScenarioName = "ConfigTest";
FString ActualPathName;
auto MockWriteToS3 = [&ActualPathName](const FString& Region, const FString& BucketName,
const FString& ObjectName, const FString& Content) -> bool
{
ActualPathName = ObjectName;
return true;
};
Exporter->SetMockPutObjectS3(MockWriteToS3);
const TMap<FString, TSharedPtr<FJsonObject>> FakeArray;
const bool bWroteSuccess = Exporter->ProcessSdfForExport(FakeArray, true);
const FString ExpectedOutputName = FPaths::Combine(
"GeneratedScenarios-" + AmbitMode->UISettings->ConfigurationName,
AmbitMode->UISettings->ScenarioName + ".sdf.json");
TestTrue("Should write successfully", bWroteSuccess);
TestEqual("File Name should be expected path", ActualPathName, ExpectedOutputName);
});
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("PrepareAllSpawnersObjectConfigs()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& Content) -> bool
{
return true;
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
Exporter->SetMockPutObjectS3(MockWriteToS3);
});
Describe("When there are no items", [this]()
{
It("Should call ProcessSdfForExport() Immediately", [this]()
{
Exporter->OnExportSdf();
TestFalse("Config should be not empty", JsonContent.IsEmpty());
});
});
Describe("When there are items", [this]()
{
BeforeEach([this]()
{
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& Content) -> bool
{
return true;
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
Exporter->SetMockPutObjectS3(MockWriteToS3);
});
LatentBeforeEach([this](FDoneDelegate const& Done)
{
// Generate items
auto* Spawner = World->SpawnActor<AMockableSpawner>();
auto HasSpawnedActors = []() -> bool
{
return true;
};
Spawner->LambdaHasActorToSpawn = HasSpawnedActors;
auto SpawnedConfig = [Spawner]()
{
USpawnedObjectConfig* Config = NewObject<USpawnedObjectConfig>();
TArray<FTransform> TransformArray;
const FTransform CurrentTransform = Spawner->GetTransform();
TransformArray.Add(CurrentTransform);
Config->SpawnedObjects.Add("Test", TransformArray);
auto FinalConfig = TScriptInterface<IConfigJsonSerializer>(Config);
Spawner->EmitConfigCompleted(FinalConfig, true);
};
Spawner->LambdaGenerateSpawnedObjectConfiguration = SpawnedConfig;
Exporter->SetSdfProcessDone(Done);
Exporter->OnExportSdf();
});
It("Should Call ProcessSdfForExport() Asynchronously With Actors", [this]()
{
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TSharedPtr<FJsonObject>* AmbitSpawnerSection;
const bool FoundField = ConvertedJson->TryGetObjectField(
JsonConstants::KAmbitSpawnerKey, AmbitSpawnerSection);
TestTrue("Spawned items should be contained in the output", FoundField);
const TArray<TSharedPtr<FJsonValue>>* OutputArray = {};
if (FoundField)
{
const bool FoundItems = AmbitSpawnerSection->Get()->TryGetArrayField(
JsonConstants::KAmbitSpawnerObjectsKey, OutputArray);
}
TestEqual("Spawned Object count should be consistent", OutputArray->Num(), 1);
TestTrue("Contains Expected Field", JsonContent.Contains("\"ActorToSpawn\": \"Test\","));
});
It("Should Contain the Spawned Actor Information", [this]()
{
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
TestTrue("Contains Expected Field", JsonContent.Contains("\"ActorToSpawn\": \"Test\","));
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("SpawnedObjectConfigCompleted_Handler()", [this]()
{
BeforeEach([this]()
{
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockWrite = [this](const FString& FilePath, const FString& OutString) -> bool
{
JsonContent = *OutString;
return true;
};
auto MockPopup = [](const FString& FileExtension, const FString& DefaultPath,
const FString& Filename) -> FString
{
return "Test";
};
auto MockWriteToS3 = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& Content) -> bool
{
return true;
};
Exporter->SetMockGetPathFromPopup(MockPopup);
Exporter->SetMockWriteFile(MockWrite);
Exporter->SetMockPutObjectS3(MockWriteToS3);
});
Describe("On Spawner Failure To Generate", [this]()
{
It("Should Generate Error", [this]()
{
// Generate items
auto* Spawner = World->SpawnActor<AMockableSpawner>();
auto HasSpawnedActors = []() -> bool
{
return true;
};
Spawner->LambdaHasActorToSpawn = HasSpawnedActors;
auto SpawnedConfig = [Spawner]()
{
USpawnedObjectConfig* Config = NewObject<USpawnedObjectConfig>();
auto FinalConfig = TScriptInterface<IConfigJsonSerializer>(Config);
Spawner->EmitConfigCompleted(FinalConfig, false);
};
Spawner->LambdaGenerateSpawnedObjectConfiguration = SpawnedConfig;
UAmbitExporterDelegateWatcher* ConfigurationDelegateWatcher = NewObject<
UAmbitExporterDelegateWatcher>();
ConfigurationDelegateWatcher->SpawnerCount = 1;
ConfigurationDelegateWatcher->bSendToS3 = false;
ConfigurationDelegateWatcher->Parent = Exporter;
Spawner->GetOnSpawnedObjectConfigCompletedDelegate().BindUObject(
ConfigurationDelegateWatcher, &UAmbitExporterDelegateWatcher::SpawnedObjectConfigCompleted_Handler);
// Assert
AddExpectedError("One of the SDF configurations have failed to generate properly",
EAutomationExpectedErrorFlags::Exact, 1);
Spawner->GenerateSpawnedObjectConfiguration();
});
});
Describe("On Spawner Success", [this]()
{
Describe("With Single Spawner", [this]()
{
LatentBeforeEach([this](FDoneDelegate const& Done)
{
Exporter->SetSdfProcessDone(Done);
// Generate items
auto* Spawner = World->SpawnActor<AMockableSpawner>();
auto HasSpawnedActors = []() -> bool
{
return true;
};
Spawner->LambdaHasActorToSpawn = HasSpawnedActors;
auto SpawnedConfig = [Spawner]()
{
USpawnedObjectConfig* Config = NewObject<USpawnedObjectConfig>();
TArray<FTransform> TransformArray;
const FTransform CurrentTransform = Spawner->GetTransform();
TransformArray.Add(CurrentTransform);
Config->SpawnedObjects.Add("Test", TransformArray);
auto FinalConfig = TScriptInterface<IConfigJsonSerializer>(Config);
Spawner->EmitConfigCompleted(FinalConfig, true);
};
Spawner->LambdaGenerateSpawnedObjectConfiguration = SpawnedConfig;
UAmbitExporterDelegateWatcher* ConfigurationDelegateWatcher = NewObject<
UAmbitExporterDelegateWatcher>();
ConfigurationDelegateWatcher->SpawnerCount = 1;
ConfigurationDelegateWatcher->bSendToS3 = false;
ConfigurationDelegateWatcher->Parent = Exporter;
Spawner->GetOnSpawnedObjectConfigCompletedDelegate().BindUObject(
ConfigurationDelegateWatcher,
&UAmbitExporterDelegateWatcher::SpawnedObjectConfigCompleted_Handler);
Spawner->GenerateSpawnedObjectConfiguration();
});
It("Should Generate Config from ProcessSdfForExport (Single)", [this]()
{
TestTrue("JSON Contains expected test field", JsonContent.Contains("\"ActorToSpawn\": \"Test\","));
});
});
Describe("With Multiple Spawners", [this]()
{
LatentBeforeEach([this](FDoneDelegate const& Done)
{
Exporter->SetSdfProcessDone(Done);
// Generate items
auto* Spawner = World->SpawnActor<AMockableSpawner>();
auto HasSpawnedActors = []() -> bool
{
return true;
};
Spawner->LambdaHasActorToSpawn = HasSpawnedActors;
auto SpawnedConfig = [Spawner]()
{
USpawnedObjectConfig* Config = NewObject<USpawnedObjectConfig>();
TArray<FTransform> TransformArray;
const FTransform CurrentTransform = Spawner->GetTransform();
TransformArray.Add(CurrentTransform);
Config->SpawnedObjects.Add("Test", TransformArray);
auto FinalConfig = TScriptInterface<IConfigJsonSerializer>(Config);
Spawner->EmitConfigCompleted(FinalConfig, true);
};
Spawner->LambdaGenerateSpawnedObjectConfiguration = SpawnedConfig;
auto* Spawner2 = World->SpawnActor<AMockableSpawner>();
Spawner2->LambdaHasActorToSpawn = HasSpawnedActors;
auto SpawnedConfig2 = [Spawner2]()
{
USpawnedObjectConfig* Config = NewObject<USpawnedObjectConfig>();
TArray<FTransform> TransformArray;
const FTransform CurrentTransform = Spawner2->GetTransform();
TransformArray.Add(CurrentTransform);
Config->SpawnedObjects.Add("Test2", TransformArray);
auto FinalConfig = TScriptInterface<IConfigJsonSerializer>(Config);
Spawner2->EmitConfigCompleted(FinalConfig, true);
};
Spawner2->LambdaGenerateSpawnedObjectConfiguration = SpawnedConfig2;
UAmbitExporterDelegateWatcher* ConfigurationDelegateWatcher = NewObject<
UAmbitExporterDelegateWatcher>();
ConfigurationDelegateWatcher->SpawnerCount = 2;
ConfigurationDelegateWatcher->bSendToS3 = false;
ConfigurationDelegateWatcher->Parent = Exporter;
Spawner->GetOnSpawnedObjectConfigCompletedDelegate().BindUObject(
ConfigurationDelegateWatcher,
&UAmbitExporterDelegateWatcher::SpawnedObjectConfigCompleted_Handler);
Spawner2->GetOnSpawnedObjectConfigCompletedDelegate().BindUObject(
ConfigurationDelegateWatcher,
&UAmbitExporterDelegateWatcher::SpawnedObjectConfigCompleted_Handler);
Spawner->GenerateSpawnedObjectConfiguration();
Spawner2->GenerateSpawnedObjectConfiguration();
});
It("Should Generate Config from ProcessSdfForExport", [this]()
{
TestTrue("JSON Contains expected test field (first item)",
JsonContent.Contains("\"ActorToSpawn\": \"Test\","));
TestTrue("JSON Contains expected test field (second item)",
JsonContent.Contains("\"ActorToSpawn\": \"Test2\","));
});
It("Should Condense Arrays of Spawned Objects", [this]()
{
const TSharedPtr<FJsonObject> ConvertedJson = FJsonHelpers::DeserializeJson(JsonContent);
const TArray<TSharedPtr<FJsonValue>>* OutputArray = {};
const TSharedPtr<FJsonObject> AmbitSpawnerObject = ConvertedJson->GetObjectField(
JsonConstants::KAmbitSpawnerKey);
if (AmbitSpawnerObject)
{
AmbitSpawnerObject->TryGetArrayField(JsonConstants::KAmbitSpawnerObjectsKey, OutputArray);
}
TestEqual("Spawned Objects should be merged into one field", OutputArray->Num(), 2);
});
});
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
JsonContent = nullptr;
});
});
Describe("OnExportMap()", [this]()
{
BeforeEach([this]()
{
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
auto MockListBuckets = []() -> TSet<FString>
{
TSet<FString> setBuckets;
setBuckets.Add("BucketName");
return setBuckets;
};
Exporter->SetMockS3ListBuckets(MockListBuckets);
auto MockCreateBucket = [](const FString& Region, const FString& BucketName) -> void
{
};
Exporter->SetMockS3CreateBucket(MockCreateBucket);
auto MockS3FileUpload = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& FilePath) -> bool
{
return true;
};
Exporter->SetMockS3FileUpload(MockS3FileUpload);
});
It("Should Return FReply::Handled", [this]()
{
const FReply Reply = Exporter->OnExportMap();
TestTrue("Reply should be true on return", Reply.IsEventHandled());
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
});
});
Describe("OnExportGltf()", [this]()
{
BeforeEach([this]()
{
// Generate World
World = FAutomationEditorCommonUtils::CreateNewMap();
// Create Mode Settings
GLevelEditorModeTools().ActivateMode(FAmbitMode::EM_AmbitModeId);
// Generate Exporter
Exporter = NewObject<UMockableConfigImportExport>();
const FAmbitMode* AmbitMode = FAmbitMode::GetEditorMode();
AmbitMode->UISettings->ExportPlatforms.SetWindows(true);
auto MockListBuckets = []() -> TSet<FString>
{
TSet<FString> setBuckets;
setBuckets.Add("BucketName");
return setBuckets;
};
Exporter->SetMockS3ListBuckets(MockListBuckets);
auto MockCreateBucket = [](const FString& Region, const FString& BucketName) -> void
{
};
Exporter->SetMockS3CreateBucket(MockCreateBucket);
auto MockS3FileUpload = [](const FString& Region, const FString& BucketName, const FString& ObjectName,
const FString& FilePath) -> bool
{
return true;
};
Exporter->SetMockS3FileUpload(MockS3FileUpload);
});
It("Should fail if no mesh to export", [this]()
{
AddExpectedError("Cannot find a static mesh to export.", EAutomationExpectedErrorFlags::Exact, 1);
Exporter->OnExportGltf();
});
It("Should fail if exporter is not found", [this]()
{
auto MockGltfExport = [](UWorld* World, const FString& FilePath)
{
return UGltfExport::GltfExportReturnCode::ExporterNotFound;
};
Exporter->SetMockGltfExport(MockGltfExport);
// Add a box to the scene so there is a static mesh to export.
const FString SpawnedActorPath = "/Ambit/Test/Props/BP_Box01.BP_Box01_C";
const FSoftClassPath ClassPath(SpawnedActorPath);
const TSubclassOf<AActor> ActorToSpawn = ClassPath.TryLoadClass<UObject>();
World->SpawnActor(ActorToSpawn.Get());
AddExpectedError("glTF Exporter plugin is not installed.", EAutomationExpectedErrorFlags::Contains, 1);
Exporter->OnExportGltf();
});
It("Should fail if there is an error when writing to file", [this]()
{
auto MockGltfExport = [](UWorld* World, const FString& FilePath)
{
return UGltfExport::GltfExportReturnCode::WriteToFileError;
};
Exporter->SetMockGltfExport(MockGltfExport);
// Add a box to the scene so there is a static mesh to export.
const FString SpawnedActorPath = "/Ambit/Test/Props/BP_Box01.BP_Box01_C";
const FSoftClassPath ClassPath(SpawnedActorPath);
const TSubclassOf<AActor> ActorToSpawn = ClassPath.TryLoadClass<UObject>();
World->SpawnActor(ActorToSpawn.Get());
AddExpectedError("Error writing to file", EAutomationExpectedErrorFlags::Contains, 1);
Exporter->OnExportGltf();
});
It("Should fail if export fails", [this]()
{
auto MockGltfExport = [](UWorld* World, const FString& FilePath)
{
return UGltfExport::GltfExportReturnCode::Failed;
};
Exporter->SetMockGltfExport(MockGltfExport);
// Add a box to the scene so there is a static mesh to export.
const FString SpawnedActorPath = "/Ambit/Test/Props/BP_Box01.BP_Box01_C";
const FSoftClassPath ClassPath(SpawnedActorPath);
const TSubclassOf<AActor> ActorToSpawn = ClassPath.TryLoadClass<UObject>();
World->SpawnActor(ActorToSpawn.Get());
AddExpectedError("Error completing export to", EAutomationExpectedErrorFlags::Contains, 1);
Exporter->OnExportGltf();
});
It("Should succeed after static mesh is present", [this]()
{
auto MockGltfExport = [](UWorld* World, const FString& FilePath)
{
return UGltfExport::GltfExportReturnCode::Success;
};
Exporter->SetMockGltfExport(MockGltfExport);
// Add a box to the scene so there is a static mesh to export.
const FString SpawnedActorPath = "/Ambit/Test/Props/BP_Box01.BP_Box01_C";
const FSoftClassPath ClassPath(SpawnedActorPath);
const TSubclassOf<AActor> ActorToSpawn = ClassPath.TryLoadClass<UObject>();
World->SpawnActor(ActorToSpawn.Get());
const FReply Reply = Exporter->OnExportGltf();
TestTrue("Reply should be true on return", Reply.IsEventHandled());
});
AfterEach([this]()
{
GLevelEditorModeTools().DeactivateMode(FAmbitMode::EM_AmbitModeId);
Exporter = nullptr;
});
});
}