bmad-method 6.0.0-alpha.17 → 6.0.0-alpha.18
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 +97 -0
- package/package.json +1 -1
- package/src/modules/bmgd/_module-installer/installer.js +160 -0
- package/src/modules/bmgd/_module-installer/platform-specifics/claude-code.js +23 -0
- package/src/modules/bmgd/_module-installer/platform-specifics/windsurf.js +18 -0
- package/src/modules/bmgd/agents/game-architect.agent.yaml +23 -8
- package/src/modules/bmgd/agents/game-designer.agent.yaml +38 -18
- package/src/modules/bmgd/agents/game-dev.agent.yaml +30 -14
- package/src/modules/bmgd/agents/game-qa.agent.yaml +64 -0
- package/src/modules/bmgd/agents/game-scrum-master.agent.yaml +27 -39
- package/src/modules/bmgd/agents/game-solo-dev.agent.yaml +56 -0
- package/src/modules/bmgd/docs/README.md +180 -0
- package/src/modules/bmgd/docs/agents-guide.md +407 -0
- package/src/modules/bmgd/docs/game-types-guide.md +503 -0
- package/src/modules/bmgd/docs/glossary.md +294 -0
- package/src/modules/bmgd/docs/quick-flow-guide.md +288 -0
- package/src/modules/bmgd/docs/quick-start.md +250 -0
- package/src/modules/bmgd/docs/troubleshooting.md +259 -0
- package/src/modules/bmgd/docs/workflow-overview.jpg +0 -0
- package/src/modules/bmgd/docs/workflows-guide.md +463 -0
- package/src/modules/bmgd/gametest/knowledge/balance-testing.md +220 -0
- package/src/modules/bmgd/gametest/knowledge/certification-testing.md +319 -0
- package/src/modules/bmgd/gametest/knowledge/compatibility-testing.md +228 -0
- package/src/modules/bmgd/gametest/knowledge/godot-testing.md +376 -0
- package/src/modules/bmgd/gametest/knowledge/input-testing.md +315 -0
- package/src/modules/bmgd/gametest/knowledge/localization-testing.md +304 -0
- package/src/modules/bmgd/gametest/knowledge/multiplayer-testing.md +322 -0
- package/src/modules/bmgd/gametest/knowledge/performance-testing.md +204 -0
- package/src/modules/bmgd/gametest/knowledge/playtesting.md +384 -0
- package/src/modules/bmgd/gametest/knowledge/qa-automation.md +190 -0
- package/src/modules/bmgd/gametest/knowledge/regression-testing.md +280 -0
- package/src/modules/bmgd/gametest/knowledge/save-testing.md +280 -0
- package/src/modules/bmgd/gametest/knowledge/smoke-testing.md +404 -0
- package/src/modules/bmgd/gametest/knowledge/test-priorities.md +271 -0
- package/src/modules/bmgd/gametest/knowledge/unity-testing.md +383 -0
- package/src/modules/bmgd/gametest/knowledge/unreal-testing.md +388 -0
- package/src/modules/bmgd/gametest/qa-index.csv +17 -0
- package/src/modules/bmgd/module.yaml +25 -9
- package/src/modules/bmgd/teams/default-party.csv +2 -0
- package/src/modules/bmgd/teams/team-gamedev.yaml +12 -1
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-01-init.md +164 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-02-context.md +210 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-03-ideation.md +289 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/steps/step-04-complete.md +275 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.md +49 -0
- package/src/modules/bmgd/workflows/1-preproduction/brainstorm-game/workflow.yaml +29 -8
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-01-init.md +223 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-01b-continue.md +151 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-02-vision.md +218 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-03-market.md +218 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-04-fundamentals.md +231 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-05-scope.md +242 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-06-references.md +224 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-07-content.md +282 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/steps/step-08-complete.md +296 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.md +62 -0
- package/src/modules/bmgd/workflows/1-preproduction/game-brief/workflow.yaml +40 -9
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-01-init.md +248 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-01b-continue.md +173 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-02-context.md +332 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-03-platforms.md +245 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-04-vision.md +229 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-05-core-gameplay.md +258 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-06-mechanics.md +249 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-07-game-type.md +266 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-08-progression.md +272 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-09-levels.md +264 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-10-art-audio.md +255 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-11-technical.md +275 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-12-epics.md +284 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-13-metrics.md +250 -0
- package/src/modules/bmgd/workflows/2-design/gdd/steps/step-14-complete.md +335 -0
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.md +61 -0
- package/src/modules/bmgd/workflows/2-design/gdd/workflow.yaml +27 -7
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-01-init.md +228 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-01b-continue.md +163 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-02-foundation.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-03-story.md +238 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-04-characters.md +297 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-05-world.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-06-dialogue.md +250 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-07-environmental.md +244 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-08-delivery.md +264 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-09-integration.md +254 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-10-production.md +262 -0
- package/src/modules/bmgd/workflows/2-design/narrative/steps/step-11-complete.md +331 -0
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.md +57 -0
- package/src/modules/bmgd/workflows/2-design/narrative/workflow.yaml +53 -8
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-01-init.md +223 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-01b-continue.md +153 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-02-context.md +262 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-03-starter.md +290 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-04-decisions.md +300 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-05-crosscutting.md +319 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-06-structure.md +304 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-07-patterns.md +349 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-08-validation.md +293 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/steps/step-09-complete.md +302 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.md +55 -0
- package/src/modules/bmgd/workflows/3-technical/game-architecture/workflow.yaml +50 -21
- package/src/modules/bmgd/workflows/4-production/code-review/checklist.md +23 -0
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.xml +225 -0
- package/src/modules/bmgd/workflows/4-production/code-review/workflow.yaml +18 -15
- package/src/modules/bmgd/workflows/4-production/correct-course/checklist.md +1 -1
- package/src/modules/bmgd/workflows/4-production/correct-course/instructions.md +1 -1
- package/src/modules/bmgd/workflows/4-production/correct-course/workflow.yaml +11 -6
- package/src/modules/bmgd/workflows/4-production/create-story/checklist.md +332 -214
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.xml +298 -0
- package/src/modules/bmgd/workflows/4-production/create-story/template.md +3 -5
- package/src/modules/bmgd/workflows/4-production/create-story/workflow.yaml +12 -7
- package/src/modules/bmgd/workflows/4-production/dev-story/checklist.md +65 -23
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.xml +409 -0
- package/src/modules/bmgd/workflows/4-production/dev-story/workflow.yaml +13 -3
- package/src/modules/bmgd/workflows/4-production/retrospective/instructions.md +4 -4
- package/src/modules/bmgd/workflows/4-production/retrospective/workflow.yaml +12 -7
- package/src/modules/bmgd/workflows/4-production/sprint-planning/instructions.md +32 -41
- package/src/modules/bmgd/workflows/4-production/sprint-planning/sprint-status-template.yaml +13 -13
- package/src/modules/bmgd/workflows/4-production/sprint-planning/workflow.yaml +6 -1
- package/src/modules/bmgd/workflows/4-production/sprint-status/instructions.md +229 -0
- package/src/modules/bmgd/workflows/4-production/sprint-status/workflow.yaml +35 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/create-tech-spec/instructions.md +140 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/create-tech-spec/workflow.yaml +27 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/checklist.md +37 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/instructions.md +220 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-dev/workflow.yaml +45 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/checklist.md +26 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/instructions.md +156 -0
- package/src/modules/bmgd/workflows/bmgd-quick-flow/quick-prototype/workflow.yaml +36 -0
- package/src/modules/bmgd/workflows/gametest/automate/checklist.md +93 -0
- package/src/modules/bmgd/workflows/gametest/automate/instructions.md +317 -0
- package/src/modules/bmgd/workflows/gametest/automate/workflow.yaml +50 -0
- package/src/modules/bmgd/workflows/gametest/performance/checklist.md +96 -0
- package/src/modules/bmgd/workflows/gametest/performance/instructions.md +323 -0
- package/src/modules/bmgd/workflows/gametest/performance/performance-template.md +256 -0
- package/src/modules/bmgd/workflows/gametest/performance/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/checklist.md +93 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/instructions.md +297 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/playtest-template.md +208 -0
- package/src/modules/bmgd/workflows/gametest/playtest-plan/workflow.yaml +59 -0
- package/src/modules/bmgd/workflows/gametest/test-design/checklist.md +98 -0
- package/src/modules/bmgd/workflows/gametest/test-design/instructions.md +280 -0
- package/src/modules/bmgd/workflows/gametest/test-design/test-design-template.md +205 -0
- package/src/modules/bmgd/workflows/gametest/test-design/workflow.yaml +47 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/checklist.md +103 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/instructions.md +348 -0
- package/src/modules/bmgd/workflows/gametest/test-framework/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/gametest/test-review/checklist.md +87 -0
- package/src/modules/bmgd/workflows/gametest/test-review/instructions.md +272 -0
- package/src/modules/bmgd/workflows/gametest/test-review/test-review-template.md +203 -0
- package/src/modules/bmgd/workflows/gametest/test-review/workflow.yaml +48 -0
- package/src/modules/bmgd/workflows/workflow-status/init/instructions.md +299 -0
- package/src/modules/bmgd/workflows/workflow-status/init/workflow.yaml +29 -0
- package/src/modules/bmgd/workflows/workflow-status/instructions.md +395 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/gamedev-brownfield.yaml +65 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/gamedev-greenfield.yaml +71 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/quickflow-brownfield.yaml +29 -0
- package/src/modules/bmgd/workflows/workflow-status/paths/quickflow-greenfield.yaml +39 -0
- package/src/modules/bmgd/workflows/workflow-status/project-levels.yaml +63 -0
- package/src/modules/bmgd/workflows/workflow-status/workflow-status-template.yaml +24 -0
- package/src/modules/bmgd/workflows/workflow-status/workflow.yaml +30 -0
- package/tools/cli/commands/install.js +9 -0
- package/tools/cli/installers/lib/core/installer.js +109 -109
- package/tools/cli/installers/lib/core/installer.js.bak +3204 -0
- package/tools/cli/installers/lib/modules/manager.js +16 -4
- package/tools/cli/lib/agent/compiler.js +99 -0
- package/tools/cli/lib/ui.js +78 -27
- package/src/modules/bmgd/workflows/2-design/gdd/instructions-gdd.md +0 -502
- package/src/modules/bmgd/workflows/4-production/code-review/instructions.md +0 -398
- package/src/modules/bmgd/workflows/4-production/create-story/instructions.md +0 -256
- package/src/modules/bmgd/workflows/4-production/dev-story/instructions.md +0 -267
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/checklist.md +0 -17
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/instructions.md +0 -164
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/template.md +0 -76
- package/src/modules/bmgd/workflows/4-production/epic-tech-context/workflow.yaml +0 -58
- package/src/modules/bmgd/workflows/4-production/story-context/checklist.md +0 -16
- package/src/modules/bmgd/workflows/4-production/story-context/context-template.xml +0 -34
- package/src/modules/bmgd/workflows/4-production/story-context/instructions.md +0 -209
- package/src/modules/bmgd/workflows/4-production/story-context/workflow.yaml +0 -63
- package/src/modules/bmgd/workflows/4-production/story-done/instructions.md +0 -111
- package/src/modules/bmgd/workflows/4-production/story-done/workflow.yaml +0 -28
- package/src/modules/bmgd/workflows/4-production/story-ready/instructions.md +0 -117
- package/src/modules/bmgd/workflows/4-production/story-ready/workflow.yaml +0 -25
- /package/src/modules/bmgd/workflows/1-preproduction/game-brief/{template.md → templates/game-brief-template.md} +0 -0
- /package/src/modules/bmgd/workflows/2-design/gdd/{gdd-template.md → templates/gdd-template.md} +0 -0
- /package/src/modules/bmgd/workflows/2-design/narrative/{narrative-template.md → templates/narrative-template.md} +0 -0
- /package/src/modules/bmgd/workflows/3-technical/game-architecture/{architecture-template.md → templates/architecture-template.md} +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Save System Testing Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Save system testing ensures data persistence, integrity, and compatibility across game versions. Save bugs are among the most frustrating for players—data loss destroys trust.
|
|
6
|
+
|
|
7
|
+
## Test Categories
|
|
8
|
+
|
|
9
|
+
### Data Integrity
|
|
10
|
+
|
|
11
|
+
| Test Type | Description | Priority |
|
|
12
|
+
| -------------------- | ------------------------------------------- | -------- |
|
|
13
|
+
| Round-trip | Save → Load → Verify all data matches | P0 |
|
|
14
|
+
| Corruption detection | Tampered/corrupted files handled gracefully | P0 |
|
|
15
|
+
| Partial write | Power loss during save doesn't corrupt | P0 |
|
|
16
|
+
| Large saves | Performance with max-size save files | P1 |
|
|
17
|
+
| Edge values | Min/max values for all saved fields | P1 |
|
|
18
|
+
|
|
19
|
+
### Version Compatibility
|
|
20
|
+
|
|
21
|
+
| Scenario | Expected Behavior |
|
|
22
|
+
| ----------------------- | ------------------------------------- |
|
|
23
|
+
| Current → Current | Full compatibility |
|
|
24
|
+
| Old → New (upgrade) | Migration with data preservation |
|
|
25
|
+
| New → Old (downgrade) | Graceful rejection or limited support |
|
|
26
|
+
| Corrupted version field | Fallback to recovery mode |
|
|
27
|
+
|
|
28
|
+
## Test Scenarios
|
|
29
|
+
|
|
30
|
+
### Core Save/Load Tests
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
SCENARIO: Basic Save Round-Trip
|
|
34
|
+
GIVEN player has 100 health, 50 gold, position (10, 5, 20)
|
|
35
|
+
AND player has inventory: ["sword", "potion", "key"]
|
|
36
|
+
WHEN game is saved
|
|
37
|
+
AND game is reloaded
|
|
38
|
+
THEN player health equals 100
|
|
39
|
+
AND player gold equals 50
|
|
40
|
+
AND player position equals (10, 5, 20)
|
|
41
|
+
AND inventory contains exactly ["sword", "potion", "key"]
|
|
42
|
+
|
|
43
|
+
SCENARIO: Save During Gameplay
|
|
44
|
+
GIVEN player is in combat
|
|
45
|
+
AND enemy has 50% health remaining
|
|
46
|
+
WHEN autosave triggers
|
|
47
|
+
AND game is reloaded
|
|
48
|
+
THEN combat state is restored
|
|
49
|
+
AND enemy health equals 50%
|
|
50
|
+
|
|
51
|
+
SCENARIO: Multiple Save Slots
|
|
52
|
+
GIVEN save slot 1 has character "Hero" at level 10
|
|
53
|
+
AND save slot 2 has character "Mage" at level 5
|
|
54
|
+
WHEN switching between slots
|
|
55
|
+
THEN correct character data loads for each slot
|
|
56
|
+
AND no cross-contamination between slots
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Edge Cases
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
SCENARIO: Maximum Inventory Save
|
|
63
|
+
GIVEN player has 999 items in inventory
|
|
64
|
+
WHEN game is saved
|
|
65
|
+
AND game is reloaded
|
|
66
|
+
THEN all 999 items are preserved
|
|
67
|
+
AND save/load completes within 5 seconds
|
|
68
|
+
|
|
69
|
+
SCENARIO: Unicode Character Names
|
|
70
|
+
GIVEN player name is "プレイヤー名"
|
|
71
|
+
WHEN game is saved
|
|
72
|
+
AND game is reloaded
|
|
73
|
+
THEN player name displays correctly
|
|
74
|
+
|
|
75
|
+
SCENARIO: Extreme Play Time
|
|
76
|
+
GIVEN play time is 9999:59:59
|
|
77
|
+
WHEN game is saved
|
|
78
|
+
AND game is reloaded
|
|
79
|
+
THEN play time displays correctly
|
|
80
|
+
AND timer continues from saved value
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Corruption Recovery
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
SCENARIO: Corrupted Save Detection
|
|
87
|
+
GIVEN save file has been manually corrupted
|
|
88
|
+
WHEN game attempts to load
|
|
89
|
+
THEN error is detected before loading
|
|
90
|
+
AND user is informed of corruption
|
|
91
|
+
AND game does not crash
|
|
92
|
+
|
|
93
|
+
SCENARIO: Missing Save File
|
|
94
|
+
GIVEN save file has been deleted externally
|
|
95
|
+
WHEN game attempts to load
|
|
96
|
+
THEN graceful error handling
|
|
97
|
+
AND option to start new game or restore backup
|
|
98
|
+
|
|
99
|
+
SCENARIO: Interrupted Save (Power Loss)
|
|
100
|
+
GIVEN save operation is interrupted mid-write
|
|
101
|
+
WHEN game restarts
|
|
102
|
+
THEN backup save is detected and offered
|
|
103
|
+
AND no data loss from previous valid save
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Platform-Specific Testing
|
|
107
|
+
|
|
108
|
+
### PC (Steam/Epic)
|
|
109
|
+
|
|
110
|
+
- Cloud save sync conflicts
|
|
111
|
+
- Multiple Steam accounts on same PC
|
|
112
|
+
- Offline → Online sync
|
|
113
|
+
- Save location permissions (Program Files issues)
|
|
114
|
+
|
|
115
|
+
### Console (PlayStation/Xbox/Switch)
|
|
116
|
+
|
|
117
|
+
- System-level save management
|
|
118
|
+
- Storage full scenarios
|
|
119
|
+
- User switching mid-game
|
|
120
|
+
- Suspend/resume with unsaved changes
|
|
121
|
+
- Cloud save quota limits
|
|
122
|
+
|
|
123
|
+
### Mobile
|
|
124
|
+
|
|
125
|
+
- App termination during save
|
|
126
|
+
- Low storage warnings
|
|
127
|
+
- iCloud/Google Play sync
|
|
128
|
+
- Device migration
|
|
129
|
+
|
|
130
|
+
## Automated Test Examples
|
|
131
|
+
|
|
132
|
+
### Unity
|
|
133
|
+
|
|
134
|
+
```csharp
|
|
135
|
+
[Test]
|
|
136
|
+
public void SaveLoad_PlayerStats_PreservesAllValues()
|
|
137
|
+
{
|
|
138
|
+
var original = new PlayerData
|
|
139
|
+
{
|
|
140
|
+
Health = 75,
|
|
141
|
+
MaxHealth = 100,
|
|
142
|
+
Gold = 1234567,
|
|
143
|
+
Position = new Vector3(100.5f, 0, -50.25f),
|
|
144
|
+
PlayTime = 36000f // 10 hours
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
SaveManager.Save(original, "test_slot");
|
|
148
|
+
var loaded = SaveManager.Load("test_slot");
|
|
149
|
+
|
|
150
|
+
Assert.AreEqual(original.Health, loaded.Health);
|
|
151
|
+
Assert.AreEqual(original.Gold, loaded.Gold);
|
|
152
|
+
Assert.AreEqual(original.Position, loaded.Position);
|
|
153
|
+
Assert.AreEqual(original.PlayTime, loaded.PlayTime, 0.01f);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
[Test]
|
|
157
|
+
public void SaveLoad_CorruptedFile_HandlesGracefully()
|
|
158
|
+
{
|
|
159
|
+
File.WriteAllText(SaveManager.GetPath("corrupt"), "INVALID DATA");
|
|
160
|
+
|
|
161
|
+
Assert.Throws<SaveCorruptedException>(() =>
|
|
162
|
+
SaveManager.Load("corrupt"));
|
|
163
|
+
|
|
164
|
+
// Game should not crash
|
|
165
|
+
Assert.IsTrue(SaveManager.IsValidSaveSlot("corrupt") == false);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Unreal
|
|
170
|
+
|
|
171
|
+
```cpp
|
|
172
|
+
bool FSaveSystemTest::RunTest(const FString& Parameters)
|
|
173
|
+
{
|
|
174
|
+
// Create test save
|
|
175
|
+
USaveGame* SaveGame = UGameplayStatics::CreateSaveGameObject(
|
|
176
|
+
UMySaveGame::StaticClass());
|
|
177
|
+
UMySaveGame* MySave = Cast<UMySaveGame>(SaveGame);
|
|
178
|
+
|
|
179
|
+
MySave->PlayerLevel = 50;
|
|
180
|
+
MySave->Gold = 999999;
|
|
181
|
+
MySave->QuestsCompleted = {"Quest1", "Quest2", "Quest3"};
|
|
182
|
+
|
|
183
|
+
// Save
|
|
184
|
+
UGameplayStatics::SaveGameToSlot(MySave, "TestSlot", 0);
|
|
185
|
+
|
|
186
|
+
// Load
|
|
187
|
+
USaveGame* Loaded = UGameplayStatics::LoadGameFromSlot("TestSlot", 0);
|
|
188
|
+
UMySaveGame* LoadedSave = Cast<UMySaveGame>(Loaded);
|
|
189
|
+
|
|
190
|
+
TestEqual("Level preserved", LoadedSave->PlayerLevel, 50);
|
|
191
|
+
TestEqual("Gold preserved", LoadedSave->Gold, 999999);
|
|
192
|
+
TestEqual("Quests count", LoadedSave->QuestsCompleted.Num(), 3);
|
|
193
|
+
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Godot
|
|
199
|
+
|
|
200
|
+
```gdscript
|
|
201
|
+
func test_save_load_round_trip():
|
|
202
|
+
var original = {
|
|
203
|
+
"health": 100,
|
|
204
|
+
"position": Vector3(10, 0, 20),
|
|
205
|
+
"inventory": ["sword", "shield"],
|
|
206
|
+
"quest_flags": {"intro_complete": true, "boss_defeated": false}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
SaveManager.save_game(original, "test_save")
|
|
210
|
+
var loaded = SaveManager.load_game("test_save")
|
|
211
|
+
|
|
212
|
+
assert_eq(loaded.health, 100)
|
|
213
|
+
assert_eq(loaded.position, Vector3(10, 0, 20))
|
|
214
|
+
assert_eq(loaded.inventory.size(), 2)
|
|
215
|
+
assert_true(loaded.quest_flags.intro_complete)
|
|
216
|
+
assert_false(loaded.quest_flags.boss_defeated)
|
|
217
|
+
|
|
218
|
+
func test_corrupted_save_detection():
|
|
219
|
+
var file = FileAccess.open("user://saves/corrupt.sav", FileAccess.WRITE)
|
|
220
|
+
file.store_string("CORRUPTED GARBAGE DATA")
|
|
221
|
+
file.close()
|
|
222
|
+
|
|
223
|
+
var result = SaveManager.load_game("corrupt")
|
|
224
|
+
|
|
225
|
+
assert_null(result, "Should return null for corrupted save")
|
|
226
|
+
assert_false(SaveManager.is_valid_save("corrupt"))
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Migration Testing
|
|
230
|
+
|
|
231
|
+
### Version Upgrade Matrix
|
|
232
|
+
|
|
233
|
+
| From Version | To Version | Test Focus |
|
|
234
|
+
| -------------- | ---------------- | ---------------------------- |
|
|
235
|
+
| 1.0 → 1.1 | Minor update | New fields default correctly |
|
|
236
|
+
| 1.x → 2.0 | Major update | Schema migration works |
|
|
237
|
+
| Beta → Release | Launch migration | All beta saves convert |
|
|
238
|
+
|
|
239
|
+
### Migration Test Template
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
SCENARIO: Save Migration v1.0 to v2.0
|
|
243
|
+
GIVEN save file from version 1.0
|
|
244
|
+
AND save contains old inventory format (array)
|
|
245
|
+
WHEN game version 2.0 loads the save
|
|
246
|
+
THEN inventory is migrated to new format (dictionary)
|
|
247
|
+
AND all items are preserved
|
|
248
|
+
AND migration is logged
|
|
249
|
+
AND backup of original is created
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Performance Benchmarks
|
|
253
|
+
|
|
254
|
+
| Metric | Target | Maximum |
|
|
255
|
+
| ------------------------ | --------------- | ------- |
|
|
256
|
+
| Save time (typical) | < 500ms | 2s |
|
|
257
|
+
| Save time (large) | < 2s | 5s |
|
|
258
|
+
| Load time (typical) | < 1s | 3s |
|
|
259
|
+
| Save file size (typical) | < 1MB | 10MB |
|
|
260
|
+
| Memory during save | < 50MB overhead | 100MB |
|
|
261
|
+
|
|
262
|
+
## Best Practices
|
|
263
|
+
|
|
264
|
+
### DO
|
|
265
|
+
|
|
266
|
+
- Use atomic saves (write to temp, then rename)
|
|
267
|
+
- Keep backup of previous save
|
|
268
|
+
- Version your save format
|
|
269
|
+
- Encrypt sensitive data
|
|
270
|
+
- Test on minimum-spec hardware
|
|
271
|
+
- Compress large saves
|
|
272
|
+
|
|
273
|
+
### DON'T
|
|
274
|
+
|
|
275
|
+
- Store absolute file paths
|
|
276
|
+
- Save derived/calculated data
|
|
277
|
+
- Trust save file contents blindly
|
|
278
|
+
- Block gameplay during save
|
|
279
|
+
- Forget to handle storage-full scenarios
|
|
280
|
+
- Skip testing save migration paths
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# Smoke Testing Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Smoke testing (Build Verification Testing) validates that a build's critical functionality works before investing time in detailed testing. A failed smoke test means "stop, this build is broken."
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
| Goal | Description |
|
|
10
|
+
| ------------------- | ---------------------------------------------- |
|
|
11
|
+
| Fast feedback | Know within minutes if build is viable |
|
|
12
|
+
| Block bad builds | Prevent broken builds from reaching QA/players |
|
|
13
|
+
| Critical path focus | Test only what matters most |
|
|
14
|
+
| CI/CD integration | Automated gate before deployment |
|
|
15
|
+
|
|
16
|
+
## Smoke Test Principles
|
|
17
|
+
|
|
18
|
+
### What Makes a Good Smoke Test
|
|
19
|
+
|
|
20
|
+
- **Fast**: Complete in 5-15 minutes
|
|
21
|
+
- **Critical**: Tests only essential functionality
|
|
22
|
+
- **Deterministic**: Same result every run
|
|
23
|
+
- **Automated**: No human intervention required
|
|
24
|
+
- **Clear**: Pass/fail with actionable feedback
|
|
25
|
+
|
|
26
|
+
### What to Include
|
|
27
|
+
|
|
28
|
+
| Category | Examples |
|
|
29
|
+
| ----------------- | ------------------------------ |
|
|
30
|
+
| Boot sequence | Game launches without crash |
|
|
31
|
+
| Core loop | Player can perform main action |
|
|
32
|
+
| Save/Load | Data persists correctly |
|
|
33
|
+
| Critical UI | Menus are navigable |
|
|
34
|
+
| Platform services | Connects to required services |
|
|
35
|
+
|
|
36
|
+
### What NOT to Include
|
|
37
|
+
|
|
38
|
+
- Edge cases and boundary conditions
|
|
39
|
+
- Performance benchmarks (separate tests)
|
|
40
|
+
- Full feature coverage
|
|
41
|
+
- Content verification
|
|
42
|
+
- Balance testing
|
|
43
|
+
|
|
44
|
+
## Smoke Test Scenarios
|
|
45
|
+
|
|
46
|
+
### Boot and Load
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
TEST: Game Launches
|
|
50
|
+
WHEN game executable is started
|
|
51
|
+
THEN main menu appears within 60 seconds
|
|
52
|
+
AND no crashes occur
|
|
53
|
+
AND required services connect
|
|
54
|
+
|
|
55
|
+
TEST: New Game Start
|
|
56
|
+
GIVEN game at main menu
|
|
57
|
+
WHEN "New Game" is selected
|
|
58
|
+
THEN gameplay loads within 30 seconds
|
|
59
|
+
AND player can control character
|
|
60
|
+
|
|
61
|
+
TEST: Continue Game
|
|
62
|
+
GIVEN existing save file
|
|
63
|
+
WHEN "Continue" is selected
|
|
64
|
+
THEN correct save loads
|
|
65
|
+
AND game state matches saved state
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Core Gameplay
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
TEST: Player Movement
|
|
72
|
+
GIVEN player in game world
|
|
73
|
+
WHEN movement input applied
|
|
74
|
+
THEN player moves in expected direction
|
|
75
|
+
AND no physics glitches occur
|
|
76
|
+
|
|
77
|
+
TEST: Core Action (Game-Specific)
|
|
78
|
+
GIVEN player can perform primary action
|
|
79
|
+
WHEN action is triggered
|
|
80
|
+
THEN action executes correctly
|
|
81
|
+
AND expected results occur
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
- Shooter: Can fire weapon, bullets hit targets
|
|
85
|
+
- RPG: Can attack enemy, damage is applied
|
|
86
|
+
- Puzzle: Can interact with puzzle elements
|
|
87
|
+
- Platformer: Can jump, platforms are solid
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Save System
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
TEST: Save Creates File
|
|
94
|
+
GIVEN player makes progress
|
|
95
|
+
WHEN save is triggered
|
|
96
|
+
THEN save file is created
|
|
97
|
+
AND save completes without error
|
|
98
|
+
|
|
99
|
+
TEST: Load Restores State
|
|
100
|
+
GIVEN valid save file exists
|
|
101
|
+
WHEN load is triggered
|
|
102
|
+
THEN saved state is restored
|
|
103
|
+
AND gameplay can continue
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Critical UI
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
TEST: Menu Navigation
|
|
110
|
+
GIVEN main menu is displayed
|
|
111
|
+
WHEN each menu option is selected
|
|
112
|
+
THEN correct screen/action occurs
|
|
113
|
+
AND navigation back works
|
|
114
|
+
|
|
115
|
+
TEST: Settings Persist
|
|
116
|
+
GIVEN settings are changed
|
|
117
|
+
WHEN game is restarted
|
|
118
|
+
THEN settings remain changed
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Automated Smoke Test Examples
|
|
122
|
+
|
|
123
|
+
### Unity
|
|
124
|
+
|
|
125
|
+
```csharp
|
|
126
|
+
using System.Collections;
|
|
127
|
+
using NUnit.Framework;
|
|
128
|
+
using UnityEngine;
|
|
129
|
+
using UnityEngine.UI;
|
|
130
|
+
using UnityEngine.TestTools;
|
|
131
|
+
using UnityEngine.SceneManagement;
|
|
132
|
+
|
|
133
|
+
[TestFixture]
|
|
134
|
+
public class SmokeTests
|
|
135
|
+
{
|
|
136
|
+
[UnityTest, Timeout(60000)]
|
|
137
|
+
public IEnumerator Game_Launches_ToMainMenu()
|
|
138
|
+
{
|
|
139
|
+
// Load main menu scene
|
|
140
|
+
SceneManager.LoadScene("MainMenu");
|
|
141
|
+
yield return new WaitForSeconds(5f);
|
|
142
|
+
|
|
143
|
+
// Verify menu is active
|
|
144
|
+
var mainMenu = GameObject.Find("MainMenuCanvas");
|
|
145
|
+
Assert.IsNotNull(mainMenu, "Main menu should be present");
|
|
146
|
+
Assert.IsTrue(mainMenu.activeInHierarchy, "Main menu should be active");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[UnityTest, Timeout(120000)]
|
|
150
|
+
public IEnumerator NewGame_LoadsGameplay()
|
|
151
|
+
{
|
|
152
|
+
// Start from main menu
|
|
153
|
+
SceneManager.LoadScene("MainMenu");
|
|
154
|
+
yield return new WaitForSeconds(2f);
|
|
155
|
+
|
|
156
|
+
// Click new game
|
|
157
|
+
var newGameButton = GameObject.Find("NewGameButton")
|
|
158
|
+
.GetComponent<Button>();
|
|
159
|
+
newGameButton.onClick.Invoke();
|
|
160
|
+
|
|
161
|
+
yield return new WaitForSeconds(10f);
|
|
162
|
+
|
|
163
|
+
// Verify gameplay scene loaded
|
|
164
|
+
Assert.AreEqual("GameplayScene", SceneManager.GetActiveScene().name);
|
|
165
|
+
|
|
166
|
+
// Verify player exists and can be controlled
|
|
167
|
+
var player = GameObject.FindWithTag("Player");
|
|
168
|
+
Assert.IsNotNull(player, "Player should exist");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
[UnityTest, Timeout(30000)]
|
|
172
|
+
public IEnumerator Player_CanMove()
|
|
173
|
+
{
|
|
174
|
+
// Load gameplay
|
|
175
|
+
SceneManager.LoadScene("GameplayScene");
|
|
176
|
+
yield return new WaitForSeconds(3f);
|
|
177
|
+
|
|
178
|
+
var player = GameObject.FindWithTag("Player");
|
|
179
|
+
var startPos = player.transform.position;
|
|
180
|
+
|
|
181
|
+
// Simulate movement input
|
|
182
|
+
var controller = player.GetComponent<PlayerController>();
|
|
183
|
+
controller.SetMoveInput(Vector2.right);
|
|
184
|
+
|
|
185
|
+
yield return new WaitForSeconds(1f);
|
|
186
|
+
|
|
187
|
+
// Verify movement occurred
|
|
188
|
+
Assert.Greater(player.transform.position.x, startPos.x,
|
|
189
|
+
"Player should have moved");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[UnityTest, Timeout(30000)]
|
|
193
|
+
public IEnumerator SaveLoad_RoundTrip_Works()
|
|
194
|
+
{
|
|
195
|
+
// Setup test state
|
|
196
|
+
SceneManager.LoadScene("GameplayScene");
|
|
197
|
+
yield return new WaitForSeconds(2f);
|
|
198
|
+
|
|
199
|
+
var player = GameObject.FindWithTag("Player");
|
|
200
|
+
player.transform.position = new Vector3(100, 0, 100);
|
|
201
|
+
|
|
202
|
+
// Save
|
|
203
|
+
SaveManager.Save("smoke_test");
|
|
204
|
+
yield return null;
|
|
205
|
+
|
|
206
|
+
// Reset position
|
|
207
|
+
player.transform.position = Vector3.zero;
|
|
208
|
+
|
|
209
|
+
// Load
|
|
210
|
+
SaveManager.Load("smoke_test");
|
|
211
|
+
yield return null;
|
|
212
|
+
|
|
213
|
+
// Verify
|
|
214
|
+
Assert.AreEqual(100f, player.transform.position.x, 1f);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Unreal
|
|
220
|
+
|
|
221
|
+
```cpp
|
|
222
|
+
// SmokeTests.cpp
|
|
223
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
224
|
+
FGameLaunchTest,
|
|
225
|
+
"Smoke.Launch.MainMenu",
|
|
226
|
+
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::SmokeFilter
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
bool FGameLaunchTest::RunTest(const FString& Parameters)
|
|
230
|
+
{
|
|
231
|
+
// Verify main menu widget exists
|
|
232
|
+
UWorld* World = GEngine->GetWorldContexts()[0].World();
|
|
233
|
+
APlayerController* PC = World->GetFirstPlayerController();
|
|
234
|
+
|
|
235
|
+
TestNotNull("Player controller exists", PC);
|
|
236
|
+
|
|
237
|
+
// Check main menu is visible
|
|
238
|
+
AMyHUD* HUD = Cast<AMyHUD>(PC->GetHUD());
|
|
239
|
+
TestTrue("Main menu is visible", HUD->IsMainMenuVisible());
|
|
240
|
+
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
|
245
|
+
FPlayerMovementTest,
|
|
246
|
+
"Smoke.Gameplay.Movement",
|
|
247
|
+
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::SmokeFilter
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
bool FPlayerMovementTest::RunTest(const FString& Parameters)
|
|
251
|
+
{
|
|
252
|
+
APawn* Player = GetTestPlayer();
|
|
253
|
+
FVector StartPos = Player->GetActorLocation();
|
|
254
|
+
|
|
255
|
+
// Apply movement
|
|
256
|
+
APlayerController* PC = Cast<APlayerController>(Player->GetController());
|
|
257
|
+
PC->AddMovementInput(FVector::ForwardVector, 1.0f);
|
|
258
|
+
|
|
259
|
+
// Wait for physics
|
|
260
|
+
ADD_LATENT_AUTOMATION_COMMAND(FWaitForSeconds(0.5f));
|
|
261
|
+
// Note: FVerifyPlayerMoved is a custom latent command - implement to verify player position changed
|
|
262
|
+
ADD_LATENT_AUTOMATION_COMMAND(FVerifyPlayerMoved(StartPos));
|
|
263
|
+
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Godot
|
|
269
|
+
|
|
270
|
+
```gdscript
|
|
271
|
+
# test_smoke.gd
|
|
272
|
+
extends GutTest
|
|
273
|
+
|
|
274
|
+
func test_game_launches():
|
|
275
|
+
# Switch to main menu
|
|
276
|
+
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
|
|
277
|
+
await get_tree().process_frame
|
|
278
|
+
await get_tree().create_timer(2.0).timeout
|
|
279
|
+
|
|
280
|
+
# Verify main menu loaded
|
|
281
|
+
var menu = get_tree().current_scene
|
|
282
|
+
assert_not_null(menu, "Main menu should load")
|
|
283
|
+
assert_eq(menu.name, "MainMenu", "Should be main menu scene")
|
|
284
|
+
|
|
285
|
+
func test_new_game_starts():
|
|
286
|
+
get_tree().change_scene_to_file("res://scenes/main_menu.tscn")
|
|
287
|
+
await get_tree().process_frame
|
|
288
|
+
|
|
289
|
+
# Find and click new game button
|
|
290
|
+
var button = get_tree().current_scene.get_node("NewGameButton")
|
|
291
|
+
button.pressed.emit()
|
|
292
|
+
|
|
293
|
+
await get_tree().create_timer(5.0).timeout
|
|
294
|
+
|
|
295
|
+
# Verify gameplay loaded
|
|
296
|
+
var scene = get_tree().current_scene
|
|
297
|
+
assert_eq(scene.name, "GameWorld", "Should load gameplay scene")
|
|
298
|
+
|
|
299
|
+
var player = scene.get_node("Player")
|
|
300
|
+
assert_not_null(player, "Player should exist")
|
|
301
|
+
|
|
302
|
+
func test_player_can_move():
|
|
303
|
+
get_tree().change_scene_to_file("res://scenes/game_world.tscn")
|
|
304
|
+
await get_tree().create_timer(1.0).timeout
|
|
305
|
+
|
|
306
|
+
var player = get_tree().current_scene.get_node("Player")
|
|
307
|
+
var start_pos = player.position
|
|
308
|
+
|
|
309
|
+
# Simulate input
|
|
310
|
+
Input.action_press("move_right")
|
|
311
|
+
await get_tree().create_timer(0.5).timeout
|
|
312
|
+
Input.action_release("move_right")
|
|
313
|
+
|
|
314
|
+
assert_gt(player.position.x, start_pos.x, "Player should have moved right")
|
|
315
|
+
|
|
316
|
+
func test_save_load_works():
|
|
317
|
+
get_tree().change_scene_to_file("res://scenes/game_world.tscn")
|
|
318
|
+
await get_tree().create_timer(1.0).timeout
|
|
319
|
+
|
|
320
|
+
var player = get_tree().current_scene.get_node("Player")
|
|
321
|
+
player.position = Vector2(500, 300)
|
|
322
|
+
|
|
323
|
+
# Save
|
|
324
|
+
SaveManager.save_game("smoke_test")
|
|
325
|
+
await get_tree().process_frame
|
|
326
|
+
|
|
327
|
+
# Reset
|
|
328
|
+
player.position = Vector2.ZERO
|
|
329
|
+
|
|
330
|
+
# Load
|
|
331
|
+
SaveManager.load_game("smoke_test")
|
|
332
|
+
await get_tree().process_frame
|
|
333
|
+
|
|
334
|
+
assert_almost_eq(player.position.x, 500.0, 1.0, "Position should restore")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## CI/CD Integration
|
|
338
|
+
|
|
339
|
+
### GitHub Actions Example
|
|
340
|
+
|
|
341
|
+
```yaml
|
|
342
|
+
name: Smoke Tests
|
|
343
|
+
|
|
344
|
+
on: [push, pull_request]
|
|
345
|
+
|
|
346
|
+
jobs:
|
|
347
|
+
smoke-test:
|
|
348
|
+
runs-on: ubuntu-latest
|
|
349
|
+
timeout-minutes: 20
|
|
350
|
+
|
|
351
|
+
steps:
|
|
352
|
+
- uses: actions/checkout@v4
|
|
353
|
+
|
|
354
|
+
- name: Build Game
|
|
355
|
+
run: ./build.sh --configuration Release
|
|
356
|
+
|
|
357
|
+
- name: Run Smoke Tests
|
|
358
|
+
run: |
|
|
359
|
+
./game --headless --run-tests=Smoke --test-timeout=600
|
|
360
|
+
|
|
361
|
+
- name: Upload Results
|
|
362
|
+
if: always()
|
|
363
|
+
uses: actions/upload-artifact@v4
|
|
364
|
+
with:
|
|
365
|
+
name: smoke-test-results
|
|
366
|
+
path: test-results/
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Test Execution Order
|
|
370
|
+
|
|
371
|
+
1. **Build verification** - Binary exists and is valid
|
|
372
|
+
2. **Launch test** - Game starts without crash
|
|
373
|
+
3. **Menu navigation** - Can navigate to gameplay
|
|
374
|
+
4. **Core loop** - Primary mechanic works
|
|
375
|
+
5. **Save/Load** - Persistence functions
|
|
376
|
+
6. **Cleanup** - No resource leaks
|
|
377
|
+
|
|
378
|
+
## Smoke Test Metrics
|
|
379
|
+
|
|
380
|
+
| Metric | Target | Action if Failed |
|
|
381
|
+
| -------------- | -------- | ------------------ |
|
|
382
|
+
| Pass rate | 100% | Block deployment |
|
|
383
|
+
| Execution time | < 15 min | Optimize tests |
|
|
384
|
+
| Flakiness | 0% | Fix or remove test |
|
|
385
|
+
|
|
386
|
+
## Best Practices
|
|
387
|
+
|
|
388
|
+
### DO
|
|
389
|
+
|
|
390
|
+
- Run smoke tests on every build
|
|
391
|
+
- Keep tests fast and focused
|
|
392
|
+
- Fail loudly and clearly
|
|
393
|
+
- Test on target platform, not just dev environment
|
|
394
|
+
- Include platform service connectivity
|
|
395
|
+
- Run before any manual QA begins
|
|
396
|
+
|
|
397
|
+
### DON'T
|
|
398
|
+
|
|
399
|
+
- Include slow or flaky tests
|
|
400
|
+
- Test edge cases or rare scenarios
|
|
401
|
+
- Allow smoke test failures to ship
|
|
402
|
+
- Skip smoke tests for "small" changes
|
|
403
|
+
- Make smoke tests depend on external services
|
|
404
|
+
- Let smoke suite grow beyond 15 minutes
|