claude-code-arcane 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +104 -55
- package/agents/career/career-strategist.md +88 -0
- package/agents/career/cv-reviewer.md +81 -0
- package/agents/career/hiring-manager.md +68 -0
- package/agents/career/mock-interviewer.md +76 -0
- package/agents/freelance/client-screener.md +84 -0
- package/agents/freelance/discovery-call.md +65 -0
- package/agents/freelance/pipeline-strategist.md +99 -0
- package/agents/freelance/proposal-reviewer.md +82 -0
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +57 -13
- package/docs/RELEASE-SETUP.md +1 -1
- package/docs/SKILLS-CATALOG.md +67 -4
- package/docs/USER-GUIDE.md +77 -72
- package/docs/agent-hierarchy.md +21 -1
- package/docs/presentations/build_arcane_deck.py +2 -2
- package/docs/presentations/build_arcane_deck_12.py +2 -2
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +2 -2
- package/profiles/freelance.yaml +43 -0
- package/profiles/job-hunt.yaml +5 -2
- package/profiles/unreal-dev.yaml +97 -0
- package/rules/drafter-reviewer.md +16 -3
- package/rules/freelance-guardrails.md +53 -0
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/career-registry/SKILL.md +95 -0
- package/skills/career-registry/scripts/career_registry.py +488 -0
- package/skills/client-screen/SKILL.md +83 -0
- package/skills/client-screen/references/red-flags.md +95 -0
- package/skills/cover-letter/SKILL.md +4 -3
- package/skills/cv-tailor/SKILL.md +11 -2
- package/skills/freelance-deliver/SKILL.md +90 -0
- package/skills/freelance-hunt/SKILL.md +126 -0
- package/skills/freelance-hunt/references/templates/Connects.md +39 -0
- package/skills/freelance-hunt/references/templates/Contrato.md +60 -0
- package/skills/freelance-hunt/references/templates/Propuesta.md +66 -0
- package/skills/freelance-kickoff/SKILL.md +94 -0
- package/skills/freelance-pipeline/SKILL.md +109 -0
- package/skills/freelance-pipeline/scripts/import_history.py +425 -0
- package/skills/freelance-profile/SKILL.md +93 -0
- package/skills/freelance-proposal/SKILL.md +112 -0
- package/skills/freelance-scan/SKILL.md +196 -0
- package/skills/freelance-scan/references/dedup.md +35 -0
- package/skills/freelance-scan/references/platforms.md +167 -0
- package/skills/freelance-scan/scripts/freelance_search.py +1077 -0
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/interview-prep/SKILL.md +11 -3
- package/skills/job-aplicar/SKILL.md +5 -1
- package/skills/job-hunt/SKILL.md +17 -0
- package/skills/job-outcome/SKILL.md +15 -1
- package/skills/job-upskill/SKILL.md +10 -2
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Automation Test Patterns
|
|
2
|
+
|
|
3
|
+
Reference for common Unreal Engine automation test setups, latent command patterns, and scenario recipes. All patterns use the real API from `Misc/AutomationTest.h`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Test Module Setup
|
|
8
|
+
|
|
9
|
+
### Build Rules
|
|
10
|
+
|
|
11
|
+
```csharp
|
|
12
|
+
// MyGameTests.Build.cs
|
|
13
|
+
public class MyGameTests : ModuleRules
|
|
14
|
+
{
|
|
15
|
+
public MyGameTests(ReadOnlyTargetRules Target) : base(Target)
|
|
16
|
+
{
|
|
17
|
+
PrivateDependencyModuleNames.AddRange(new string[]
|
|
18
|
+
{
|
|
19
|
+
"Core",
|
|
20
|
+
"CoreUObject",
|
|
21
|
+
"Engine",
|
|
22
|
+
"AutomationController",
|
|
23
|
+
"MyGame", // the module being tested
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Include editor-only dependencies when running in Editor context
|
|
27
|
+
if (Target.bBuildEditor)
|
|
28
|
+
{
|
|
29
|
+
PrivateDependencyModuleNames.Add("UnrealEd");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Target File Inclusion
|
|
36
|
+
|
|
37
|
+
```csharp
|
|
38
|
+
// MyGameEditor.Target.cs (editor target — tests run here)
|
|
39
|
+
ExtraModuleNames.Add("MyGameTests");
|
|
40
|
+
|
|
41
|
+
// MyGame.Target.cs (game target — tests excluded unless bWithAutomationTests)
|
|
42
|
+
if (bWithAutomationTests)
|
|
43
|
+
{
|
|
44
|
+
ExtraModuleNames.Add("MyGameTests");
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Naming Convention
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
MyGame.Category.SubCategory.TestName
|
|
52
|
+
|
|
53
|
+
Examples:
|
|
54
|
+
MyGame.Inventory.AddItem
|
|
55
|
+
MyGame.Inventory.RemoveItem
|
|
56
|
+
MyGame.Inventory.Overflow
|
|
57
|
+
MyGame.Pathfinding.SimpleGrid
|
|
58
|
+
MyGame.Pathfinding.ObstacleDense
|
|
59
|
+
MyGame.Assets.PrimaryWeaponLoad
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The dot-separated path creates a tree in the Session Frontend Automation tab.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Pattern: Pure Logic Test (No World)
|
|
67
|
+
|
|
68
|
+
For testing stateless utility functions and pure C++ classes.
|
|
69
|
+
|
|
70
|
+
```cpp
|
|
71
|
+
#include "Misc/AutomationTest.h"
|
|
72
|
+
#include "MyMath.h" // the unit under test
|
|
73
|
+
|
|
74
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
75
|
+
FMyMathLerpTest,
|
|
76
|
+
"MyGame.Math.LerpClamped",
|
|
77
|
+
EAutomationTestFlags::EditorContext |
|
|
78
|
+
EAutomationTestFlags::SmokeFilter) // SmokeFilter = fast, runs every check-in
|
|
79
|
+
|
|
80
|
+
bool FMyMathLerpTest::RunTest(const FString& Parameters)
|
|
81
|
+
{
|
|
82
|
+
TestEqual(TEXT("Lerp at 0"), MyMath::LerpClamped(0.f, 10.f, 0.f), 0.f, 0.001f);
|
|
83
|
+
TestEqual(TEXT("Lerp at 1"), MyMath::LerpClamped(0.f, 10.f, 1.f), 10.f, 0.001f);
|
|
84
|
+
TestEqual(TEXT("Lerp at 0.5"), MyMath::LerpClamped(0.f, 10.f, 0.5f), 5.f, 0.001f);
|
|
85
|
+
// Clamped: alpha outside [0,1] saturates
|
|
86
|
+
TestEqual(TEXT("Lerp above 1"), MyMath::LerpClamped(0.f, 10.f, 2.f), 10.f, 0.001f);
|
|
87
|
+
TestEqual(TEXT("Lerp below 0"), MyMath::LerpClamped(0.f, 10.f, -1.f), 0.f, 0.001f);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Pattern: UObject Test (Requires GC)
|
|
95
|
+
|
|
96
|
+
For testing `UObject`-derived classes. `NewObject<>` requires a valid outer.
|
|
97
|
+
|
|
98
|
+
```cpp
|
|
99
|
+
#include "Misc/AutomationTest.h"
|
|
100
|
+
#include "Engine/Engine.h"
|
|
101
|
+
#include "Inventory/InventoryComponent.h"
|
|
102
|
+
|
|
103
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
104
|
+
FInventoryComponentTest,
|
|
105
|
+
"MyGame.Inventory.AddRemove",
|
|
106
|
+
EAutomationTestFlags::EditorContext |
|
|
107
|
+
EAutomationTestFlags::ProductFilter)
|
|
108
|
+
|
|
109
|
+
bool FInventoryComponentTest::RunTest(const FString& Parameters)
|
|
110
|
+
{
|
|
111
|
+
// Use GetTransientPackage() as outer for test UObjects
|
|
112
|
+
UInventoryComponent* Inv = NewObject<UInventoryComponent>(GetTransientPackage());
|
|
113
|
+
if (!TestNotNull(TEXT("Inventory created"), Inv)) { return false; }
|
|
114
|
+
|
|
115
|
+
Inv->AddItem(FName("Sword"), 3);
|
|
116
|
+
TestEqual(TEXT("Count after add"), Inv->GetCount(FName("Sword")), 3);
|
|
117
|
+
|
|
118
|
+
Inv->RemoveItem(FName("Sword"), 1);
|
|
119
|
+
TestEqual(TEXT("Count after remove"), Inv->GetCount(FName("Sword")), 2);
|
|
120
|
+
|
|
121
|
+
Inv->RemoveItem(FName("Sword"), 99); // over-remove
|
|
122
|
+
TestEqual(TEXT("Count clamped at 0"), Inv->GetCount(FName("Sword")), 0);
|
|
123
|
+
|
|
124
|
+
// TestValid works on TWeakObjectPtr and IsValid()-capable types
|
|
125
|
+
TWeakObjectPtr<UInventoryComponent> WeakInv(Inv);
|
|
126
|
+
TestValid(TEXT("Weak ref valid"), WeakInv);
|
|
127
|
+
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Pattern: Parameterized Test Over Asset Paths
|
|
135
|
+
|
|
136
|
+
```cpp
|
|
137
|
+
IMPLEMENT_COMPLEX_AUTOMATION_TEST(
|
|
138
|
+
FDataAssetValidation,
|
|
139
|
+
"MyGame.Assets.DataAssetValidation",
|
|
140
|
+
EAutomationTestFlags::EditorContext |
|
|
141
|
+
EAutomationTestFlags::ProductFilter)
|
|
142
|
+
|
|
143
|
+
void FDataAssetValidation::GetTests(TArray<FString>& OutBeautifiedNames,
|
|
144
|
+
TArray<FString>& OutTestCommands) const
|
|
145
|
+
{
|
|
146
|
+
// Enumerate assets at editor time
|
|
147
|
+
FAssetRegistryModule& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
148
|
+
TArray<FAssetData> Assets;
|
|
149
|
+
AR.Get().GetAssetsByClass(UMyItemData::StaticClass()->GetClassPathName(), Assets);
|
|
150
|
+
|
|
151
|
+
for (const FAssetData& Asset : Assets)
|
|
152
|
+
{
|
|
153
|
+
OutBeautifiedNames.Add(Asset.AssetName.ToString());
|
|
154
|
+
OutTestCommands.Add(Asset.GetObjectPathString());
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
bool FDataAssetValidation::RunTest(const FString& Parameters)
|
|
159
|
+
{
|
|
160
|
+
// Parameters = the object path string from OutTestCommands
|
|
161
|
+
UMyItemData* ItemData = LoadObject<UMyItemData>(nullptr, *Parameters);
|
|
162
|
+
UE_RETURN_ON_ERROR(ItemData != nullptr,
|
|
163
|
+
FString::Printf(TEXT("Failed to load %s"), *Parameters));
|
|
164
|
+
|
|
165
|
+
TestFalse(TEXT("DisplayName not empty"),
|
|
166
|
+
ItemData->DisplayName.IsEmpty());
|
|
167
|
+
TestTrue(TEXT("MaxStack > 0"),
|
|
168
|
+
ItemData->MaxStack > 0);
|
|
169
|
+
TestNotNull(TEXT("Icon set"), ItemData->Icon.LoadSynchronous());
|
|
170
|
+
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Pattern: Expected Error / Negative Test
|
|
178
|
+
|
|
179
|
+
Use `AddExpectedMessage` to suppress and verify expected error log output.
|
|
180
|
+
|
|
181
|
+
```cpp
|
|
182
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
183
|
+
FInventoryOverflowNegativeTest,
|
|
184
|
+
"MyGame.Inventory.OverflowRejectsItem",
|
|
185
|
+
EAutomationTestFlags::EditorContext |
|
|
186
|
+
EAutomationTestFlags::ProductFilter |
|
|
187
|
+
EAutomationTestFlags::NegativeFilter)
|
|
188
|
+
|
|
189
|
+
bool FInventoryOverflowNegativeTest::RunTest(const FString& Parameters)
|
|
190
|
+
{
|
|
191
|
+
// Tell the framework to expect (and suppress) this warning
|
|
192
|
+
AddExpectedMessage(TEXT("Inventory is full"),
|
|
193
|
+
ELogVerbosity::Warning,
|
|
194
|
+
EAutomationExpectedMessageFlags::Contains);
|
|
195
|
+
|
|
196
|
+
UInventoryComponent* Inv = NewObject<UInventoryComponent>(GetTransientPackage());
|
|
197
|
+
Inv->SetMaxSlots(1);
|
|
198
|
+
Inv->AddItem(FName("Sword"), 1); // fills the slot
|
|
199
|
+
|
|
200
|
+
// This should log "Inventory is full" and return false
|
|
201
|
+
bool bAdded = Inv->AddItem(FName("Shield"), 1);
|
|
202
|
+
TestFalse(TEXT("Second item rejected"), bAdded);
|
|
203
|
+
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Pattern: Latent Command — Wait for Delegate
|
|
211
|
+
|
|
212
|
+
```cpp
|
|
213
|
+
#include "Misc/AutomationTest.h"
|
|
214
|
+
|
|
215
|
+
// Command that waits until a shared bool flag is set
|
|
216
|
+
DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(
|
|
217
|
+
FWaitForBoolCommand, TSharedPtr<bool>, bDone);
|
|
218
|
+
|
|
219
|
+
bool FWaitForBoolCommand::Update()
|
|
220
|
+
{
|
|
221
|
+
return bDone.IsValid() && *bDone;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Command with timeout
|
|
225
|
+
DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(
|
|
226
|
+
FWaitForBoolOrTimeoutCommand, TSharedPtr<bool>, bDone, float, Timeout);
|
|
227
|
+
|
|
228
|
+
bool FWaitForBoolOrTimeoutCommand::Update()
|
|
229
|
+
{
|
|
230
|
+
if (bDone.IsValid() && *bDone) { return true; }
|
|
231
|
+
if (GetCurrentRunTime() > Timeout)
|
|
232
|
+
{
|
|
233
|
+
// Logging in a latent command does NOT add a test error automatically
|
|
234
|
+
// Store the test ref if you need to call AddError
|
|
235
|
+
return true; // done (timed out)
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Usage inside a test
|
|
241
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
242
|
+
FAsyncOperationTest,
|
|
243
|
+
"MyGame.Async.SubsystemReady",
|
|
244
|
+
EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter)
|
|
245
|
+
|
|
246
|
+
bool FAsyncOperationTest::RunTest(const FString& Parameters)
|
|
247
|
+
{
|
|
248
|
+
TSharedPtr<bool> bReady = MakeShared<bool>(false);
|
|
249
|
+
|
|
250
|
+
// Kick off async work, flip bReady when done
|
|
251
|
+
UMySubsystem* Sub = GEngine->GetEngineSubsystem<UMySubsystem>();
|
|
252
|
+
if (!TestNotNull(TEXT("Subsystem"), Sub)) { return false; }
|
|
253
|
+
|
|
254
|
+
Sub->InitAsync([bReady]() { *bReady = true; });
|
|
255
|
+
|
|
256
|
+
// Wait up to 5 seconds
|
|
257
|
+
ADD_LATENT_AUTOMATION_COMMAND(FWaitForBoolOrTimeoutCommand(bReady, 5.0f));
|
|
258
|
+
|
|
259
|
+
// Latent commands run after RunTest returns; assertions here run immediately
|
|
260
|
+
// For post-async assertions, use a custom latent command that captures the test ptr
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Pattern: Latent Command — Validate After Async
|
|
268
|
+
|
|
269
|
+
To make assertions after async work, capture the test reference in the latent command.
|
|
270
|
+
|
|
271
|
+
```cpp
|
|
272
|
+
class FValidateAfterAsyncCommand : public IAutomationLatentCommand
|
|
273
|
+
{
|
|
274
|
+
public:
|
|
275
|
+
FValidateAfterAsyncCommand(FAutomationTestBase* InTest,
|
|
276
|
+
TSharedPtr<bool> InbDone,
|
|
277
|
+
TSharedPtr<int32> InResult)
|
|
278
|
+
: Test(InTest), bDone(InbDone), Result(InResult) {}
|
|
279
|
+
|
|
280
|
+
virtual bool Update() override
|
|
281
|
+
{
|
|
282
|
+
if (!bDone.IsValid() || !*bDone) { return false; }
|
|
283
|
+
Test->TestEqual(TEXT("Async result"), *Result, 42);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private:
|
|
288
|
+
FAutomationTestBase* Test;
|
|
289
|
+
TSharedPtr<bool> bDone;
|
|
290
|
+
TSharedPtr<int32> Result;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// In RunTest:
|
|
294
|
+
bool FMyAsyncCalcTest::RunTest(const FString& Parameters)
|
|
295
|
+
{
|
|
296
|
+
auto bDone = MakeShared<bool>(false);
|
|
297
|
+
auto Result = MakeShared<int32>(0);
|
|
298
|
+
|
|
299
|
+
UMyCalc::RunAsync([bDone, Result](int32 Val)
|
|
300
|
+
{
|
|
301
|
+
*Result = Val;
|
|
302
|
+
*bDone = true;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
ADD_LATENT_AUTOMATION_COMMAND(FValidateAfterAsyncCommand(this, bDone, Result));
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Running Tests
|
|
313
|
+
|
|
314
|
+
### Session Frontend (Editor)
|
|
315
|
+
|
|
316
|
+
1. Open **Window > Session Frontend > Automation**.
|
|
317
|
+
2. Connect to the local session.
|
|
318
|
+
3. Filter by name or flags; check boxes and click **Start Tests**.
|
|
319
|
+
|
|
320
|
+
### Command Line (Unattended)
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
# Run all tests matching a name filter, exit when done
|
|
324
|
+
UnrealEditor-Cmd MyGame -ExecCmds="Automation RunTests MyGame.Inventory" \
|
|
325
|
+
-unattended -nopause -testexit="Automation Test Queue Empty" \
|
|
326
|
+
-log -abslog=TestOutput.log
|
|
327
|
+
|
|
328
|
+
# Run smoke tests only
|
|
329
|
+
UnrealEditor-Cmd MyGame \
|
|
330
|
+
-ExecCmds="Automation RunFilter Smoke" \
|
|
331
|
+
-unattended -nopause -testexit="Automation Test Queue Empty"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Programmatic (C++ / Python)
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
# Inside Editor Python (via Editor Python Plugin)
|
|
338
|
+
import unreal
|
|
339
|
+
subsystem = unreal.get_editor_subsystem(unreal.AutomationControllerManager)
|
|
340
|
+
# Use Automation Controller API to queue and run tests
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Context Stack for Test Grouping
|
|
346
|
+
|
|
347
|
+
Use `PushContext` / `PopContext` to annotate which sub-step caused a failure.
|
|
348
|
+
|
|
349
|
+
```cpp
|
|
350
|
+
bool FMyComplexTest::RunTest(const FString& Parameters)
|
|
351
|
+
{
|
|
352
|
+
ExecutionInfo.PushContext(TEXT("Phase 1: Setup"));
|
|
353
|
+
// ... setup assertions
|
|
354
|
+
ExecutionInfo.PopContext();
|
|
355
|
+
|
|
356
|
+
ExecutionInfo.PushContext(TEXT("Phase 2: Execute"));
|
|
357
|
+
// ... execute assertions
|
|
358
|
+
ExecutionInfo.PopContext();
|
|
359
|
+
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Errors recorded while a context is active will include the context string in the failure report.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Test Telemetry
|
|
369
|
+
|
|
370
|
+
Report numeric measurements alongside test results (captured by automation infrastructure):
|
|
371
|
+
|
|
372
|
+
```cpp
|
|
373
|
+
bool FMyPerfTest::RunTest(const FString& Parameters)
|
|
374
|
+
{
|
|
375
|
+
double StartTime = FPlatformTime::Seconds();
|
|
376
|
+
// ... operation
|
|
377
|
+
double Duration = FPlatformTime::Seconds() - StartTime;
|
|
378
|
+
|
|
379
|
+
// Reports DataPoint="SpawnTime", Measurement=Duration to telemetry
|
|
380
|
+
// ExecutionInfo.TelemetryStorage = TEXT("MyGamePerf");
|
|
381
|
+
// ExecutionInfo.TelemetryItems.Emplace(TEXT("SpawnTime"), Duration, TEXT("ms"));
|
|
382
|
+
FAutomationTestFramework::Get().AddAnalyticsItemToCurrentTest(
|
|
383
|
+
FString::Printf(TEXT("SpawnTime=%.2fms"), Duration * 1000.0));
|
|
384
|
+
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
```
|