`// File: RunPerfTest.cs ----
using Gauntlet;
using UnrealBuildBase;
namespace MyGame {
public class RunPerfTest : RunUnreal {
public override string DefaultTestName => “MyPerfTestNode”;
public override ExitCode Execute(){
Globals.Params = new Gauntlet.Params(this.params);
UnrealTestOptions testOptions = new UnrealTestOptions();
AutoParam.ApplyDefaults(testOptions);
testOptions.Project = “MyGameProject”;
testOptions.Namespace = “MyGame.Automation”;
AutoParam.ApplyParams(testOptions, Globals.Params.AllArguments);
return RunTests(testOptions);
}
}
}
// File: MyPerfTestNode.cs ----
using Gauntlet;
using UE;
namespace MyGame.Automation {
public class MyPerfTestNode : UE.TargetAutomation {
public MyPerfTestNode(UnrealTestContext inContext) : base(inContext) {}
public override AutomationTestConfig GetConfiguration(){
AutomationTestConfig config = base.GetConfiguration();
UnrealTestRole clientRole = config.RequireRole(UnrealTargetRole.Client);
// so I don’t have to race the game client to attach the debugger
clientRole.CommandLine += " -WaitForDebugger";
// What is meant to go here? Is it the GauntletTestController, or just some test string:
// eg: Project.MyGameProject.Functional Tests.ThisIsAFunctionalTestIWrote
config.RunTest = “MyGameProject.MyGauntletTestController”;
// config.RunTest = “MyGameProject.MyUnitTests”;
return config;
}
}
}
// File: MyGauntletTestController.h ----
include “Gauntlet/Public/GauntletTestController.h”
include "MyGauntletTestController.generated.h
UCLASS()
class MYGAME_API UMyGauntletTestController : public UGauntletTestController
{
GENERATED_BODY()
public:
virtual void OnInit() override;
virtual void OnTick(float deltaTime_s) override;
};
// File: MyGauntletTestController.cpp ----
include “MyGauntletTestController.h”
// just imagine all of these functions calls are implemented and working
void MyGauntletTestController::OnInit()
{
Super::OnInit();
GetRemainingPositionsFromTestNode();
}
void MyGauntletTestController::OnTick(float deltaTime_s)
{
Super::OnTick(deltaTime_s);
if(state == ETestState::GetNextPosition)
{
currentPosition = GetNextPosition();
}
else if (state == ETestState::ProfilingFinishedForCurrentPosition)
{
Framework::EmitMessage(“ProfiledPosition”, currentPosition);
}
}`
Essentially, my goal is to:
-
Have a simple command which will launch my automated perf testing (For a single player, offline game)
- Not pictured: during a perf test, I’ll move the APlayerCharacter to different positions, and run the CSVProfiler
- I want to have MyPerfTestNode.cs read a file of those positions, and then pass them along. (Should the Gauntlet controller be doing this?)
- Not pictured: during a perf test, I’ll move the APlayerCharacter to different positions, and run the CSVProfiler
-
If the game crashes, I want some way of retesting the positions I haven’t profiled yet---not of re-running the entire job.
- Can the Gauntlet controller emit an event back out to MyPerfTestNode? (so it can keep track of the work that’s been performed)
- Can MyPerfTestNode reboot the game client if it goes down?
- Also, how would I do that? Does the test node itself need a restart?
- Previously, I’ve used a flag to restart on a crash, but it restarts the entire job
-
Am I thinking about this the right way?
- So far, I can do: RunUAT.bat RunPerfTest, and it gets MyPerfTestNode and sets it up
- I haven’t been able to launch into a Gauntlet Controller though.
- When using a Gauntlet Controller: What is the “test”? Just the gauntlet controller’s name?
Thank you for your patience with this question. I realize it’s really several questions at once. I’m still trying to get a feel for where different responsibilities are meant to live within these systems.
If any of y’all have thoughts or advice, I would appreciate hearing it. Apologies if I’ve missed anything rudimentary. The code above hasn’t actually been run before, it’s just a generalization of my real code. Thanks!