Replace .golangci.yml with the shared canonical config. The old config's top-level linters-settings block was silently ignored under the v2 schema, so the lll/funlen/cyclop/dupl thresholds now actually apply. The four repo-specific disables (mnd, exhaustive, paralleltest, testpackage) move out of the config into targeted in-code nolint directives carrying their original approval dates, keeping the config byte-identical to the canonical one. Fixes surfaced by the stricter settings: t.Parallel() added to all 32 tests, 24 overlong lines wrapped or their comments tightened, tcell control-code returns rewritten as character literals, dupl markers on the identically-shaped item data tables, and two wsl_v5 defer cuddles. No behavior changes. The repo has no golangci-lint version pin (no Dockerfile or CI; make lint runs the host binary, currently v2.12.2), so there was nothing to bump.
133 lines
2.9 KiB
Go
133 lines
2.9 KiB
Go
//nolint:testpackage // white-box tests reach unexported state (approved 2026-07-07)
|
|
package game
|
|
|
|
import "testing"
|
|
|
|
// badcheck from init.c: every probability table must sum to exactly 100.
|
|
func TestProbabilitiesSumTo100(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
sum := func(info []ObjInfo) int {
|
|
s := 0
|
|
for _, oi := range info {
|
|
s += oi.Prob
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
data := newGameData()
|
|
|
|
tables := map[string][]ObjInfo{
|
|
"things": data.baseThings[:],
|
|
"potions": data.basePotInfo[:],
|
|
"scrolls": data.baseScrInfo[:],
|
|
"rings": data.baseRingInfo[:],
|
|
"sticks": data.baseWsInfo[:],
|
|
"weapons": data.baseWeapInfo[:NumWeaponTypes], // excludes the flame entry
|
|
"armor": data.baseArmInfo[:],
|
|
}
|
|
for name, tab := range tables {
|
|
if s := sum(tab); s != 100 {
|
|
t.Errorf("bad percentages for %s: sum = %d, want 100", name, s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInitProbsCumulative(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := New(Params{Seed: 1})
|
|
|
|
last := g.Items.Potions[NumPotionTypes-1].Prob
|
|
if last != 100 {
|
|
t.Errorf("cumulative potion probability ends at %d, want 100", last)
|
|
}
|
|
|
|
for i := PotionKind(1); i < NumPotionTypes; i++ {
|
|
if g.Items.Potions[i].Prob < g.Items.Potions[i-1].Prob {
|
|
t.Errorf("potion probs not nondecreasing at %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewGameRandomizesAppearances(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
g := New(Params{Seed: 12345})
|
|
|
|
checkPotionColors(t, g)
|
|
checkScrollNames(t, g)
|
|
checkWandMaterials(t, g)
|
|
|
|
// Determinism: same seed, same appearances.
|
|
h := New(Params{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{}
|
|
|
|
for i, c := range g.Items.PotColors {
|
|
if c == "" {
|
|
t.Fatalf("potion %d has no color", i)
|
|
}
|
|
|
|
if seen[c] {
|
|
t.Errorf("potion color %q assigned twice", c)
|
|
}
|
|
|
|
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 {
|
|
if n == "" {
|
|
t.Fatalf("scroll %d has no name", i)
|
|
}
|
|
|
|
if len(n) > MaxNameLen+1 {
|
|
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 {
|
|
if g.Items.WandType[i] != wandName && g.Items.WandType[i] != staffName {
|
|
t.Errorf("stick %d has type %q", i, g.Items.WandType[i])
|
|
}
|
|
|
|
if g.Items.WandMade[i] == "" {
|
|
t.Errorf("stick %d has no material", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMonsterTable(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := newGameData()
|
|
if data.monsterTable[0].Name != "aquator" || data.monsterTable[25].Name != "zombie" {
|
|
t.Error("monster table order broken")
|
|
}
|
|
|
|
if data.monsterTable['D'-'A'].Name != "dragon" {
|
|
t.Error("letter indexing broken")
|
|
}
|
|
}
|