200 lines
6.4 KiB
Go
200 lines
6.4 KiB
Go
package main
|
||
|
||
import (
|
||
"regexp"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
var ansiEscapeRe = regexp.MustCompile("\x1b\\[[0-9;]*m")
|
||
|
||
func stripANSI(s string) string {
|
||
return ansiEscapeRe.ReplaceAllString(s, "")
|
||
}
|
||
|
||
// TestKlondikeTableauAlignmentWithMixedCardWidths проверяет
|
||
// исправление бага: карта "10" (единственный ранг длиной в 2
|
||
// символа) без паддинга оказывалась короче остальных карт и
|
||
// закрытой клетки "[?]", из-за чего колонки правее в той же строке
|
||
// сдвигались и вертикальное выравнивание таблицы ломалось.
|
||
func TestKlondikeTableauAlignmentWithMixedCardWidths(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
m.game.Tableau[0] = []KlondikeCard{{Card: Card{Rank: Ace, Suit: Spades}, FaceUp: true}}
|
||
m.game.Tableau[1] = []KlondikeCard{{Card: Card{Rank: Ten, Suit: Hearts}, FaceUp: true}}
|
||
m.game.Tableau[2] = []KlondikeCard{{Card: Card{Rank: King, Suit: Clubs}, FaceUp: false}}
|
||
m.game.Tableau[3] = []KlondikeCard{{Card: Card{Rank: Queen, Suit: Diamonds}, FaceUp: true}}
|
||
m.game.Tableau[4] = []KlondikeCard{}
|
||
m.game.Tableau[5] = []KlondikeCard{}
|
||
m.game.Tableau[6] = []KlondikeCard{}
|
||
|
||
rendered := stripANSI(m.renderTableau())
|
||
lines := strings.Split(rendered, "\n")
|
||
if len(lines) < 2 {
|
||
t.Fatalf("ожидалось минимум 2 строки (заголовок + ряд карт), получено %d", len(lines))
|
||
}
|
||
|
||
headerWidth := len([]rune(lines[0]))
|
||
for i, line := range lines[1:] {
|
||
if w := len([]rune(line)); w != headerWidth {
|
||
t.Errorf("строка %d имеет ширину %d, отличную от заголовка (%d): %q", i+1, w, headerWidth, line)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelDrawViaKeys(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
stockBefore := len(m.game.Stock)
|
||
|
||
next, _ := m.Update(key("d"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка: %s", m.message)
|
||
}
|
||
if len(m.game.Stock) != stockBefore-1 {
|
||
t.Fatalf("колода должна была уменьшиться на 1")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelSelectAndMoveTableauToTableau(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
m.game.Tableau[0] = []KlondikeCard{{Card: c(Seven, Hearts), FaceUp: true}}
|
||
m.game.Tableau[1] = []KlondikeCard{{Card: c(Six, Spades), FaceUp: true}}
|
||
|
||
next, _ := m.Update(key("2"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка выбора: %s", m.message)
|
||
}
|
||
if m.selected != klondikePileRef(1) {
|
||
t.Fatalf("ожидался выбор колонны 1 (индекс), получено %v", m.selected)
|
||
}
|
||
|
||
next, _ = m.Update(key("1"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка хода: %s", m.message)
|
||
}
|
||
if len(m.game.Tableau[0]) != 2 {
|
||
t.Fatalf("ожидалось 2 карты в целевой колонне, получено %d", len(m.game.Tableau[0]))
|
||
}
|
||
if m.selected != klondikeNoSelection {
|
||
t.Errorf("выбор должен был сброситься после хода")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelSelectWasteAndSendToFoundation(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
m.game.Waste = []Card{c(Ace, Hearts)}
|
||
|
||
next, _ := m.Update(key("0"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка: %s", m.message)
|
||
}
|
||
if m.selected != klondikeWasteRef {
|
||
t.Fatalf("ожидался выбор отбоя")
|
||
}
|
||
|
||
next, _ = m.Update(key("f"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка отправки на фундамент: %s", m.message)
|
||
}
|
||
if m.game.Foundations[Hearts] != int(Ace) {
|
||
t.Errorf("туз червей должен был оказаться на фундаменте")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelEscClearsSelection(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
m.game.Tableau[0] = []KlondikeCard{{Card: c(Seven, Hearts), FaceUp: true}}
|
||
|
||
next, _ := m.Update(key("1"))
|
||
m = next.(KlondikeModel)
|
||
if m.selected == klondikeNoSelection {
|
||
t.Fatalf("ожидался выбор колонны 1")
|
||
}
|
||
|
||
next, _ = m.Update(key("esc"))
|
||
m = next.(KlondikeModel)
|
||
if m.selected != klondikeNoSelection {
|
||
t.Errorf("esc должен был сбросить выбор")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelUndoViaKeys(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
m.game.Waste = []Card{c(Ace, Hearts)}
|
||
next, _ := m.Update(key("0"))
|
||
m = next.(KlondikeModel)
|
||
next, _ = m.Update(key("f"))
|
||
m = next.(KlondikeModel)
|
||
if m.game.Foundations[Hearts] != int(Ace) {
|
||
t.Fatalf("ход должен был примениться")
|
||
}
|
||
|
||
next, _ = m.Update(key("u"))
|
||
m = next.(KlondikeModel)
|
||
if m.isError {
|
||
t.Fatalf("неожиданная ошибка отмены: %s", m.message)
|
||
}
|
||
if m.game.Foundations[Hearts] != 0 {
|
||
t.Errorf("после отмены фундамент должен был вернуться в исходное состояние")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelNewGameViaKeys(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
oldStock := m.game.Stock
|
||
|
||
next, _ := m.Update(key("n"))
|
||
m = next.(KlondikeModel)
|
||
if len(m.game.Stock)+28 != 52 {
|
||
t.Fatalf("новая партия должна быть корректно раздана")
|
||
}
|
||
same := true
|
||
for i := range oldStock {
|
||
if i >= len(m.game.Stock) || oldStock[i] != m.game.Stock[i] {
|
||
same = false
|
||
break
|
||
}
|
||
}
|
||
if same {
|
||
t.Errorf("новая партия должна была перетасовать колоду заново")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelQReturnsToMenu(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
next, _ := m.Update(key("q"))
|
||
m = next.(KlondikeModel)
|
||
if !m.backToMenu {
|
||
t.Fatalf("ожидался флаг backToMenu после 'q'")
|
||
}
|
||
if m.quitting {
|
||
t.Fatalf("'q' не должен закрывать всё приложение")
|
||
}
|
||
}
|
||
|
||
func TestKlondikeModelWinShowsResultAndAllowsNewGame(t *testing.T) {
|
||
m := NewKlondikeModel()
|
||
for s := Clubs; s <= Spades; s++ {
|
||
m.game.Foundations[s] = int(King)
|
||
}
|
||
m.game.Foundations[Hearts] = int(Queen)
|
||
m.game.Waste = []Card{c(King, Hearts)}
|
||
|
||
next, _ := m.Update(key("0"))
|
||
m = next.(KlondikeModel)
|
||
next, _ = m.Update(key("f"))
|
||
m = next.(KlondikeModel)
|
||
if !m.game.Won {
|
||
t.Fatalf("партия должна была завершиться победой")
|
||
}
|
||
|
||
next, _ = m.Update(key("n"))
|
||
m = next.(KlondikeModel)
|
||
if m.game.Won {
|
||
t.Errorf("новая партия не должна была начаться выигранной")
|
||
}
|
||
}
|