Decompose test functions to clear complexity findings (step 7)

TestSaveRestoreRoundTrip, TestNewGameRandomizesAppearances, and
TestNewLevelInvariants split their assertion blocks into t.Helper()
sub-checks. The lint run is now completely clean (0 issues).
This commit is contained in:
2026-07-22 22:11:10 +07:00
parent 5c14a829aa
commit 5b7e258195
3 changed files with 157 additions and 82 deletions

View File

@@ -38,6 +38,19 @@ func TestNewLevelInvariants(t *testing.T) {
for _, seed := range []int32{1, 12345, 2026, 99999} { for _, seed := range []int32{1, 12345, 2026, 99999} {
g := genLevel(t, seed) g := genLevel(t, seed)
checkHeroPlacement(t, g, seed)
checkRoomsDrawn(t, g, seed)
checkMonstersPlaced(t, g, seed)
checkObjectsPlaced(t, g, seed)
checkStartingKit(t, g, seed)
}
}
// checkHeroPlacement verifies the staircase and hero landed on valid,
// unoccupied cells.
func checkHeroPlacement(t *testing.T, g *RogueGame, seed int32) {
t.Helper()
// The staircase is somewhere real. // The staircase is somewhere real.
st := g.Level.Stairs st := g.Level.Stairs
if g.Level.Char(st.Y, st.X) != Stairs { if g.Level.Char(st.Y, st.X) != Stairs {
@@ -58,8 +71,12 @@ func TestNewLevelInvariants(t *testing.T) {
if g.Player.Room == nil { if g.Player.Room == nil {
t.Errorf("seed %d: hero not in any room", seed) t.Errorf("seed %d: hero not in any room", seed)
} }
}
// checkRoomsDrawn verifies rooms and floor/passages appear on the map.
func checkRoomsDrawn(t *testing.T, g *RogueGame, seed int32) {
t.Helper()
// Some rooms exist and are drawn.
m := renderMap(g) m := renderMap(g)
if !strings.Contains(m, "|") || !strings.Contains(m, "-") { if !strings.Contains(m, "|") || !strings.Contains(m, "-") {
t.Errorf("seed %d: no room walls drawn", seed) t.Errorf("seed %d: no room walls drawn", seed)
@@ -68,8 +85,13 @@ func TestNewLevelInvariants(t *testing.T) {
if !strings.Contains(m, ".") && !strings.Contains(m, "#") { if !strings.Contains(m, ".") && !strings.Contains(m, "#") {
t.Errorf("seed %d: no floor or passages drawn", seed) t.Errorf("seed %d: no floor or passages drawn", seed)
} }
}
// checkMonstersPlaced verifies every monster is indexed on the map and
// placed in a room.
func checkMonstersPlaced(t *testing.T, g *RogueGame, seed int32) {
t.Helper()
// Every monster is indexed on the map and placed in a room.
for _, mon := range g.Level.Monsters { for _, mon := range g.Level.Monsters {
if g.Level.MonsterAt(mon.Pos.Y, mon.Pos.X) != mon { if g.Level.MonsterAt(mon.Pos.Y, mon.Pos.X) != mon {
t.Errorf("seed %d: monster %c not indexed at its position", t.Errorf("seed %d: monster %c not indexed at its position",
@@ -80,9 +102,14 @@ func TestNewLevelInvariants(t *testing.T) {
t.Errorf("seed %d: monster %c has no room", seed, mon.Type) t.Errorf("seed %d: monster %c has no room", seed, mon.Type)
} }
} }
}
// checkObjectsPlaced verifies every level object sits on a cell
// displaying its type (items can share cells only with monsters standing
// on them).
func checkObjectsPlaced(t *testing.T, g *RogueGame, seed int32) {
t.Helper()
// Every level object sits on a cell displaying its type (items can
// share cells only with monsters standing on them).
for _, obj := range g.Level.Objects { for _, obj := range g.Level.Objects {
ch := g.Level.Char(obj.Pos.Y, obj.Pos.X) ch := g.Level.Char(obj.Pos.Y, obj.Pos.X)
if ch != obj.Kind.Glyph() && if ch != obj.Kind.Glyph() &&
@@ -91,8 +118,13 @@ func TestNewLevelInvariants(t *testing.T) {
seed, obj.Kind, obj.Pos.Y, obj.Pos.X, ch) seed, obj.Kind, obj.Pos.Y, obj.Pos.X, ch)
} }
} }
}
// checkStartingKit verifies the player has her starting kit: food, armor,
// mace, bow, arrows.
func checkStartingKit(t *testing.T, g *RogueGame, seed int32) {
t.Helper()
// The player has her starting kit: food, armor, mace, bow, arrows.
if len(g.Player.Pack) != 5 { if len(g.Player.Pack) != 5 {
t.Errorf("seed %d: starting pack has %d items, want 5", t.Errorf("seed %d: starting pack has %d items, want 5",
seed, len(g.Player.Pack)) seed, len(g.Player.Pack))
@@ -107,7 +139,6 @@ func TestNewLevelInvariants(t *testing.T) {
g.Player.CurArmor.ArmorKind() != ArmorRingMail { g.Player.CurArmor.ArmorKind() != ArmorRingMail {
t.Errorf("seed %d: not wearing the starting ring mail", seed) t.Errorf("seed %d: not wearing the starting ring mail", seed)
} }
}
} }
func TestNewLevelDeterministic(t *testing.T) { func TestNewLevelDeterministic(t *testing.T) {

View File

@@ -39,6 +39,15 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
t.Error("save file not deleted on restore (C anti-restart rule)") t.Error("save file not deleted on restore (C anti-restart rule)")
} }
checkRestoredState(t, g, h)
checkRestoredMonsters(t, g, h)
checkEquipmentAliasing(t, g, h)
}
// checkRestoredState verifies the scalar state survived the round trip.
func checkRestoredState(t *testing.T, g, h *RogueGame) {
t.Helper()
if h.Player.Purse != 123 || h.Player.FoodLeft != 777 { if h.Player.Purse != 123 || h.Player.FoodLeft != 777 {
t.Errorf("player state lost: purse=%d food=%d", t.Errorf("player state lost: purse=%d food=%d",
h.Player.Purse, h.Player.FoodLeft) h.Player.Purse, h.Player.FoodLeft)
@@ -67,13 +76,22 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
if renderMap(h) != renderMap(g) { if renderMap(h) != renderMap(g) {
t.Error("restored level map differs") t.Error("restored level map differs")
} }
}
// checkRestoredMonsters verifies the monster list and its pointer fixups
// survived the round trip.
func checkRestoredMonsters(t *testing.T, g, h *RogueGame) {
t.Helper()
if len(h.Level.Monsters) != len(g.Level.Monsters) { if len(h.Level.Monsters) != len(g.Level.Monsters) {
t.Fatalf("monster count %d != %d", t.Fatalf("monster count %d != %d",
len(h.Level.Monsters), len(g.Level.Monsters)) len(h.Level.Monsters), len(g.Level.Monsters))
} }
if len(g.Level.Monsters) > 0 { if len(g.Level.Monsters) == 0 {
return
}
m := h.Level.Monsters[0] m := h.Level.Monsters[0]
if m.Dest != &h.Player.Pos { if m.Dest != &h.Player.Pos {
t.Error("monster chase target not re-aliased to the hero") t.Error("monster chase target not re-aliased to the hero")
@@ -86,9 +104,13 @@ func TestSaveRestoreRoundTrip(t *testing.T) {
if m.Room == nil { if m.Room == nil {
t.Error("monster room pointer not rebuilt") t.Error("monster room pointer not rebuilt")
} }
} }
// Equipment aliasing: the wielded mace must be the same *Object as the
// one in the pack. // checkEquipmentAliasing verifies the wielded mace is the same *Object as
// the one in the pack.
func checkEquipmentAliasing(t *testing.T, g, h *RogueGame) {
t.Helper()
st := g.snapshot() st := g.snapshot()
t.Logf("snapshot indices: weapon=%d armor=%d rings=%v packlen=%d", t.Logf("snapshot indices: weapon=%d armor=%d rings=%v packlen=%d",
st.Player.CurWeapon, st.Player.CurArmor, st.Player.CurRing, st.Player.CurWeapon, st.Player.CurArmor, st.Player.CurRing,

View File

@@ -48,6 +48,22 @@ func TestInitProbsCumulative(t *testing.T) {
func TestNewGameRandomizesAppearances(t *testing.T) { func TestNewGameRandomizesAppearances(t *testing.T) {
g := NewGame(Config{Seed: 12345}) g := NewGame(Config{Seed: 12345})
checkPotionColors(t, g)
checkScrollNames(t, g)
checkWandMaterials(t, g)
// Determinism: same seed, same appearances.
h := NewGame(Config{Seed: 12345})
if h.Items != g.Items {
t.Error("two games with the same seed produced different item lore")
}
}
// checkPotionColors verifies every potion has a distinct color.
func checkPotionColors(t *testing.T, g *RogueGame) {
t.Helper()
seen := map[string]bool{} seen := map[string]bool{}
for i, c := range g.Items.PotColors { for i, c := range g.Items.PotColors {
@@ -61,6 +77,12 @@ func TestNewGameRandomizesAppearances(t *testing.T) {
seen[c] = true seen[c] = true
} }
}
// checkScrollNames verifies every scroll has a name within the C buffer
// limit.
func checkScrollNames(t *testing.T, g *RogueGame) {
t.Helper()
for i, n := range g.Items.ScrNames { for i, n := range g.Items.ScrNames {
if n == "" { if n == "" {
@@ -71,9 +93,15 @@ func TestNewGameRandomizesAppearances(t *testing.T) {
t.Errorf("scroll name %q longer than C buffer allows", n) t.Errorf("scroll name %q longer than C buffer allows", n)
} }
} }
}
// checkWandMaterials verifies every stick has a wand/staff type and a
// material.
func checkWandMaterials(t *testing.T, g *RogueGame) {
t.Helper()
for i := range g.Items.WandType { for i := range g.Items.WandType {
if g.Items.WandType[i] != "wand" && g.Items.WandType[i] != "staff" { if g.Items.WandType[i] != wandName && g.Items.WandType[i] != staffName {
t.Errorf("stick %d has type %q", i, g.Items.WandType[i]) t.Errorf("stick %d has type %q", i, g.Items.WandType[i])
} }
@@ -81,12 +109,6 @@ func TestNewGameRandomizesAppearances(t *testing.T) {
t.Errorf("stick %d has no material", i) t.Errorf("stick %d has no material", i)
} }
} }
// Determinism: same seed, same appearances.
h := NewGame(Config{Seed: 12345})
if h.Items != g.Items {
t.Error("two games with the same seed produced different item lore")
}
} }
func TestMonsterTable(t *testing.T) { func TestMonsterTable(t *testing.T) {