package game import ( "strings" "testing" ) // driveTurns runs the game's per-turn loop up to n times, doing the same // first-level and pre-play setup Run() does. Run() itself no longer // returns — game-over exits the process — so tests drive command() // directly, with short scripts that avoid quitting, saving, or playing // long enough to starve, any of which would exit the test binary. func driveTurns(t *testing.T, g *RogueGame, n int) { t.Helper() g.startLevel() g.prePlay() for range n { g.command() } } // TestRunDownStairs stands the hero on the staircase and descends via the // '>' command through the real turn loop, then checks the level changed. func TestRunDownStairs(t *testing.T) { // '>' is a free action (After=false), so it is followed by a paying // rest ('.') to end the command() call; without a paying action the // turn loop would spin forever on the auto-fed prompt input. tt := &testTerm{input: []byte(">.")} g := New(Params{Seed: 7, Term: tt}) g.NewLevel() g.Player.Pos = g.Level.Stairs // stand on the stairs g.restored = true // keep startLevel from regenerating g.Daemons = DaemonList{} // and give it a fresh daemon table g.StartDaemon(DRunners, 0, After) g.StartDaemon(DDoctor, 0, After) g.Fuse(DSwander, 0, wanderTime(g), After) g.StartDaemon(DStomach, 0, After) driveTurns(t, g, 1) if g.Depth != 2 { t.Errorf("depth = %d after descending, want 2", g.Depth) } } // TestScoreRendersList checks that the scoreboard is drawn on the screen // (in C it went to stdout after endwin; here it stays on the screen). The // quit and death paths that normally show it now exit the process, so the // display is exercised through score() directly. func TestScoreRendersList(t *testing.T) { g := New(Params{Seed: 1, Term: &testTerm{}}) g.Player.Purse = 100 g.score(g.Player.Purse, 1, 0) // flags 1 = quit; posts the top-ten list found := false for y := range NumLines { if strings.Contains(g.scr.Std.Line(y), "Top Ten") { found = true } } if !found { t.Error("score list not on screen") } }