Adopt house golangci-lint config; fix all approved-linter findings
.golangci.yml is the prompts-repo standard verbatim plus one approved
exception (paralleltest, sneak 2026-07-06). Roughly 1,500 findings
fixed:
- autofix sweep (wsl_v5/nlreturn/intrange/modernize formatting), with
the misspell autofix REVERTED where it rewrote authentic C game text
("missle vanishes" stays, with nolint and a comment)
- error handling: errcheck sites get real handling — saveFile now
closes explicitly and removes corrupt saves, scoreboard writes are
documented best-effort, err113 sentinel errors (ErrSaveOutOfDate,
ErrScreenTooSmall); panics allowed for unrecoverable states per
MEMORY.md policy
- gosec: real fixes (ParseInt for SEED, 0600 scorefile) and justified
per-line nolints for provably-bounded conversions (randomMonsterLetter
helper collapses eight rnd(26)+'A' sites)
- API tidying from linters: pointer receivers on flag types
(recvcheck), msg helpers renamed addmsgf/doaddf/Printwf/MvPrintwf
(goprintffuncname), myExit()/findFloor(monst)/mkGameInput(t) drop
always-constant params (unparam), gameEnd carries no status
- gocritic/staticcheck: if-else chains to switches, the pack.c
inventory filter untangled into matchesFilter, main.go split so
defers run before exit (exitAfterDefer, funlen)
- revive doc comments on all exported flag types/consts/methods
Remaining findings are confined to linters pending sneak's exception
decision (mnd, gochecknoglobals, cyclop, nestif, gocognit, exhaustive,
goconst, testpackage); TODO.md records the state. MEMORY.md added at
repo root as the project's agent working notes.
This commit is contained in:
@@ -2,13 +2,16 @@ package game
|
||||
|
||||
import "testing"
|
||||
|
||||
// mkGameInput builds a headless game whose input plays the given script.
|
||||
func mkGameInput(t *testing.T, seed int32, input string) *RogueGame {
|
||||
// mkGameInput builds a headless game; tests script it via setInput. The
|
||||
// fixed seed keeps the scripted item/monster interactions stable.
|
||||
func mkGameInput(t *testing.T) *RogueGame {
|
||||
t.Helper()
|
||||
g := NewGame(Config{Seed: seed, Term: &testTerm{input: []byte(input)}})
|
||||
|
||||
g := NewGame(Config{Seed: 5, Term: &testTerm{}})
|
||||
g.NewLevel()
|
||||
g.Oldpos = g.Player.Pos
|
||||
g.Oldrp = g.roomin(g.Player.Pos)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
@@ -16,42 +19,61 @@ func mkGameInput(t *testing.T, seed int32, input string) *RogueGame {
|
||||
func give(g *RogueGame, obj *Object) byte {
|
||||
obj.Count = 1
|
||||
g.addPack(obj, true)
|
||||
|
||||
return obj.PackCh
|
||||
}
|
||||
|
||||
// setInput replaces the scripted terminal input.
|
||||
func setInput(t *testing.T, g *RogueGame, input ...byte) {
|
||||
t.Helper()
|
||||
|
||||
tt, ok := g.scr.term.(*testTerm)
|
||||
if !ok {
|
||||
t.Fatal("game terminal is not a testTerm")
|
||||
}
|
||||
|
||||
tt.input = input
|
||||
tt.pos = 0
|
||||
}
|
||||
|
||||
func TestQuaffHealingPotion(t *testing.T) {
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
pot := newObject()
|
||||
pot.Kind = KindPotion
|
||||
pot.Which = int(PotionHealing)
|
||||
ch := give(g, pot)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
g.Player.Stats.HP = 1
|
||||
g.quaff()
|
||||
|
||||
if g.Player.Stats.HP <= 1 {
|
||||
t.Error("healing potion did not heal")
|
||||
}
|
||||
|
||||
if !g.Items.Potions[PotionHealing].Know {
|
||||
t.Error("healing potion not identified after drinking")
|
||||
}
|
||||
|
||||
if len(g.Player.Pack) != 5 {
|
||||
t.Errorf("potion not consumed: %d items", len(g.Player.Pack))
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) {
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
pot := newObject()
|
||||
pot.Kind = KindPotion
|
||||
pot.Which = int(PotionConfusion)
|
||||
ch := give(g, pot)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
g.quaff()
|
||||
|
||||
if !g.Player.On(Confused) {
|
||||
t.Error("confusion potion did not confuse")
|
||||
}
|
||||
|
||||
if g.findSlot(DUnconfuse) == nil {
|
||||
t.Error("no unconfuse fuse pending")
|
||||
}
|
||||
@@ -59,21 +81,23 @@ func TestQuaffConfusionSetsFlagAndFuse(t *testing.T) {
|
||||
for range 30 {
|
||||
g.DoFuses(After)
|
||||
}
|
||||
|
||||
if g.Player.On(Confused) {
|
||||
t.Error("confusion never wore off")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadEnchantArmor(t *testing.T) {
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
scr := newObject()
|
||||
scr.Kind = KindScroll
|
||||
scr.Which = int(ScrollEnchantArmor)
|
||||
ch := give(g, scr)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
before := g.Player.CurArmor.ArmorClass
|
||||
g.readScroll()
|
||||
|
||||
if g.Player.CurArmor.ArmorClass != before-1 {
|
||||
t.Errorf("enchant armor: AC %d -> %d, want %d",
|
||||
before, g.Player.CurArmor.ArmorClass, before-1)
|
||||
@@ -86,17 +110,19 @@ func TestReadHoldMonsterFreezesAdjacent(t *testing.T) {
|
||||
// look(TRUE) at the end of read_scroll immediately re-wakes greedy
|
||||
// monsters. The port reproduces that quirk faithfully — see
|
||||
// TestHoldScrollGreedyMonsterQuirk.
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
tp := spawnAdjacent(g, 'Z')
|
||||
tp.Flags.Set(Awake)
|
||||
|
||||
scr := newObject()
|
||||
scr.Kind = KindScroll
|
||||
scr.Which = int(ScrollHoldMonster)
|
||||
ch := give(g, scr)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
g.readScroll()
|
||||
t.Logf("after scroll: flags=%o huh=%q", tp.Flags, g.Msgs.Huh)
|
||||
|
||||
if tp.On(Awake) || !tp.On(Held) {
|
||||
t.Error("hold monster scroll did not hold the adjacent monster")
|
||||
}
|
||||
@@ -107,21 +133,24 @@ func TestReadHoldMonsterFreezesAdjacent(t *testing.T) {
|
||||
// (orc) held by a scroll is re-woken by the look(TRUE) that read_scroll
|
||||
// performs, ending up both held and running again.
|
||||
func TestHoldScrollGreedyMonsterQuirk(t *testing.T) {
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
tp := spawnAdjacent(g, 'O')
|
||||
tp.Flags.Set(Awake)
|
||||
|
||||
scr := newObject()
|
||||
scr.Kind = KindScroll
|
||||
scr.Which = int(ScrollHoldMonster)
|
||||
ch := give(g, scr)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
g.readScroll()
|
||||
t.Logf("orc after scroll: flags=%o (Awake=%v Held=%v)",
|
||||
tp.Flags, tp.On(Awake), tp.On(Held))
|
||||
|
||||
if !tp.On(Held) {
|
||||
t.Error("orc lost Held entirely")
|
||||
}
|
||||
|
||||
if !tp.On(Awake) {
|
||||
t.Error("quirk changed: greedy monster stayed held; if this is a " +
|
||||
"deliberate fix, update this test and ARCHITECTURE.md")
|
||||
@@ -129,21 +158,25 @@ func TestHoldScrollGreedyMonsterQuirk(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestZapSlowMonster(t *testing.T) {
|
||||
g := mkGameInput(t, 5, "")
|
||||
g := mkGameInput(t)
|
||||
tp := spawnAdjacent(g, 'Z')
|
||||
stick := newObject()
|
||||
stick.Kind = KindWand
|
||||
stick.Which = int(WandSlowMonster)
|
||||
g.fixStick(stick)
|
||||
ch := give(g, stick)
|
||||
g.scr.term.(*testTerm).input = []byte{ch}
|
||||
setInput(t, g, ch)
|
||||
|
||||
g.Delta = Coord{X: 1, Y: 0} // aim at the monster
|
||||
|
||||
charges := stick.Charges
|
||||
|
||||
g.doZap()
|
||||
|
||||
if !tp.On(Slowed) {
|
||||
t.Error("slow monster wand did not slow")
|
||||
}
|
||||
|
||||
if stick.Charges != charges-1 {
|
||||
t.Error("zap did not use a charge")
|
||||
}
|
||||
@@ -152,18 +185,23 @@ func TestZapSlowMonster(t *testing.T) {
|
||||
func TestParseOpts(t *testing.T) {
|
||||
g := NewGame(Config{Seed: 1})
|
||||
g.ParseOpts("terse,nojump,name=Conan,fruit=mango,inven=slow")
|
||||
|
||||
if !g.Options.Terse {
|
||||
t.Error("terse not set")
|
||||
}
|
||||
|
||||
if g.Options.Jump {
|
||||
t.Error("nojump not honored")
|
||||
}
|
||||
|
||||
if g.Whoami != "Conan" {
|
||||
t.Errorf("name = %q", g.Whoami)
|
||||
}
|
||||
|
||||
if g.Fruit != "mango" {
|
||||
t.Errorf("fruit = %q", g.Fruit)
|
||||
}
|
||||
|
||||
if g.Options.InvType != InvSlow {
|
||||
t.Errorf("inven = %d", g.Options.InvType)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user