179 lines
5.4 KiB
Go
179 lines
5.4 KiB
Go
package main
|
||
|
||
import "testing"
|
||
|
||
func TestMinesweeperModelRevealViaKey(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.cursor = MinesweeperPos{Row: 4, Col: 4}
|
||
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(MinesweeperModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка: %s", m.message)
|
||
}
|
||
if !m.game.Board[4][4].Revealed {
|
||
t.Fatalf("клетка должна была открыться")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelFlagViaKey(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.cursor = MinesweeperPos{Row: 4, Col: 4}
|
||
|
||
next, _ := m.Update(key("f"))
|
||
m = next.(MinesweeperModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка: %s", m.message)
|
||
}
|
||
if !m.game.Board[4][4].Flagged {
|
||
t.Fatalf("клетка должна была получить флаг")
|
||
}
|
||
|
||
next, _ = m.Update(key("f"))
|
||
m = next.(MinesweeperModel)
|
||
if m.game.Board[4][4].Flagged {
|
||
t.Fatalf("флаг должен был сняться")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelCursorMovement(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.cursor = MinesweeperPos{Row: 4, Col: 4}
|
||
|
||
next, _ := m.Update(key("up"))
|
||
m = next.(MinesweeperModel)
|
||
if m.cursor.Row != 3 {
|
||
t.Errorf("курсор должен был сдвинуться вверх")
|
||
}
|
||
next, _ = m.Update(key("right"))
|
||
m = next.(MinesweeperModel)
|
||
if m.cursor.Col != 5 {
|
||
t.Errorf("курсор должен был сдвинуться вправо")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelCursorClampsAtEdges(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.cursor = MinesweeperPos{Row: 0, Col: 0}
|
||
|
||
next, _ := m.Update(key("up"))
|
||
m = next.(MinesweeperModel)
|
||
if m.cursor.Row != 0 {
|
||
t.Errorf("курсор не должен был уйти за верхний край")
|
||
}
|
||
next, _ = m.Update(key("left"))
|
||
m = next.(MinesweeperModel)
|
||
if m.cursor.Col != 0 {
|
||
t.Errorf("курсор не должен был уйти за левый край")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelCannotRevealFlaggedCell(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.cursor = MinesweeperPos{Row: 4, Col: 4}
|
||
next, _ := m.Update(key("f"))
|
||
m = next.(MinesweeperModel)
|
||
|
||
next, _ = m.Update(key("enter"))
|
||
m = next.(MinesweeperModel)
|
||
if !m.isError {
|
||
t.Fatalf("ожидалась ошибка открытия помеченной клетки")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelQReturnsToMenu(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
next, _ := m.Update(key("q"))
|
||
m = next.(MinesweeperModel)
|
||
if !m.backToMenu {
|
||
t.Fatalf("ожидался флаг backToMenu после 'q'")
|
||
}
|
||
if m.quitting {
|
||
t.Fatalf("'q' не должен закрывать всё приложение")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelNewGameAfterResult(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
m.game.Result = &MinesweeperResult{Won: false}
|
||
|
||
next, _ := m.Update(key("n"))
|
||
m = next.(MinesweeperModel)
|
||
if m.game.Result != nil {
|
||
t.Errorf("новая партия не должна была начаться с результатом")
|
||
}
|
||
if m.game.Width != 9 || m.game.Height != 9 || m.game.MineCount != 10 {
|
||
t.Errorf("новая партия должна была сохранить параметры поля")
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelSupportsAllPresets(t *testing.T) {
|
||
presets := []struct{ w, h, mines int }{
|
||
{9, 9, 10},
|
||
{16, 16, 40},
|
||
{30, 16, 99},
|
||
}
|
||
for _, p := range presets {
|
||
m := NewMinesweeperModel(p.w, p.h, p.mines)
|
||
if m.game.Width != p.w || m.game.Height != p.h || m.game.MineCount != p.mines {
|
||
t.Errorf("некорректные параметры для пресета %+v: %+v", p, m.game)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestMinesweeperModelChordViaKeyOnRevealedCell(t *testing.T) {
|
||
m := NewMinesweeperModel(5, 5, 2)
|
||
m.game.minesPlaced = true
|
||
m.game.Board[0][1].IsMine = true
|
||
m.game.Board[1][0].IsMine = true
|
||
for r := 0; r < 5; r++ {
|
||
for c := 0; c < 5; c++ {
|
||
if m.game.Board[r][c].IsMine {
|
||
continue
|
||
}
|
||
n := 0
|
||
for _, nb := range msNeighbors(msp(r, c)) {
|
||
if m.game.inBounds(nb) && m.game.Board[nb.Row][nb.Col].IsMine {
|
||
n++
|
||
}
|
||
}
|
||
m.game.Board[r][c].AdjacentMines = n
|
||
}
|
||
}
|
||
m.game.Board[0][0].Revealed = true
|
||
m.game.RevealedCount++
|
||
m.game.ToggleFlag(msp(0, 1))
|
||
m.game.ToggleFlag(msp(1, 0))
|
||
|
||
m.cursor = msp(0, 0)
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(MinesweeperModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка хорды через клавишу: %s", m.message)
|
||
}
|
||
if !m.game.Board[1][1].Revealed {
|
||
t.Errorf("хорда через enter на открытой клетке должна была открыть соседа (1,1)")
|
||
}
|
||
}
|
||
|
||
// TestMinesweeperModelFullAutoPlaythrough прогоняет TUI-модель через
|
||
// последовательность нажатий на всех клетках поля, проверяя, что
|
||
// путь через Update не паникует и партия завершается (тем или иным
|
||
// исходом).
|
||
func TestMinesweeperModelFullAutoPlaythrough(t *testing.T) {
|
||
m := NewMinesweeperModel(9, 9, 10)
|
||
|
||
for r := 0; r < 9 && m.game.Result == nil; r++ {
|
||
for c := 0; c < 9 && m.game.Result == nil; c++ {
|
||
m.cursor = MinesweeperPos{Row: r, Col: c}
|
||
if m.game.Board[r][c].Revealed {
|
||
continue
|
||
}
|
||
next, _ := m.Update(key("enter"))
|
||
m = next.(MinesweeperModel)
|
||
}
|
||
}
|
||
if m.game.Result == nil {
|
||
t.Fatalf("партия должна была завершиться после перебора всех клеток")
|
||
}
|
||
}
|