Files
snake/test_snake.py
T

110 lines
3.6 KiB
Python
Raw Normal View History

2026-09-27 22:55:32 +08:00
# 无头测试:桩替代 pygame,验证 snake 游戏核心逻辑
import sys, types, os, shutil, tempfile
# ---------------- pygame 桩 ----------------
class Surf:
def fill(self, *a, **k): pass
def blit(self, *a, **k): pass
class Font:
def __init__(self, *a, **k): pass
def render(self, text, aa, color):
s = Surf()
s.get_width = lambda: 10 * len(str(text))
s.get_height = lambda: 20
return s
class Rect:
def __init__(self, x, y, w, h, *a, **k):
self.x, self.y, self.width, self.height = x, y, w, h
@property
def right(self): return self.x + self.width
class Clock:
def tick(self, *a): return 0
def get_time(self): return 16
fake = types.ModuleType("pygame")
fake.init = lambda: None
fake.quit = lambda: None
fake.joystick = types.SimpleNamespace(init=lambda: None, Joystick=lambda i: None,
get_count=lambda: 0)
fake.display = types.SimpleNamespace(set_mode=lambda s: Surf(), set_caption=lambda s: None,
flip=lambda: None)
fake.font = types.SimpleNamespace(Font=Font)
fake.Rect = Rect
fake.draw = types.SimpleNamespace(rect=lambda *a, **k: None, line=lambda *a, **k: None)
fake.time = types.SimpleNamespace(Clock=Clock)
for name, val in dict(K_ESCAPE=27, K_UP=273, K_DOWN=274, K_LEFT=276, K_RIGHT=275,
K_w=119, K_s=115, K_a=97, K_d=100, K_RETURN=13, K_SPACE=32,
QUIT=256, KEYDOWN=768, JOYBUTTONDOWN=769, JOYHATMOTION=770,
JOYAXISMOTION=771).items():
setattr(fake, name, val)
sys.modules["pygame"] = fake
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"games", "snake"))
import importlib.util
spec = importlib.util.spec_from_file_location(
"snake_main", os.path.join(os.path.dirname(os.path.abspath(__file__)),
"games", "snake", "main.py"))
m = importlib.util.module_from_spec(spec)
spec.loader.exec_module(m)
# 把存档指到临时目录,避免污染
tmp = tempfile.mkdtemp()
m.SAVE_DIR = tmp
m.SAVE_FILE = os.path.join(tmp, "snake_best.txt")
g = m.SnakeGame()
# 1. 初始为菜单态
assert g.state == "menu"
g.confirm()
assert g.state == "play"
assert len(g.snake) == 3
print("1. 菜单→开始 OK")
# 2. 移动两步,蛇头前进
head0 = g.snake[0]
g.step()
assert g.snake[0] == (head0[0] + 1, head0[1]), g.snake[0]
assert len(g.snake) == 3
print("2. 移动 OK")
# 3. 禁止 180° 掉头
g.push_dir((-1, 0))
g.step()
assert g.dir == (1, 0), g.dir
print("3. 禁止掉头 OK")
# 4. 吃到苹果:加分 + 变长
g.apple = (g.snake[0][0] + 1, g.snake[0][1])
score0, len0 = g.score, len(g.snake)
g.step()
assert g.score == score0 + 1 and len(g.snake) == len0 + 1
print("4. 吃苹果 OK")
# 5. 撞墙 → 结束并保存最高分
g.snake = [(m.COLS - 1, 5), (m.COLS - 2, 5), (m.COLS - 3, 5)]
g.dir, g.pending = (1, 0), []
g.score = 12
g.step()
assert g.state == "over" and g.best == 12
assert open(m.SAVE_FILE).read() == "12"
print("5. 撞墙/最高分保存 OK")
# 6. 重新开始
g.confirm()
assert g.state == "play" and g.score == 0 and len(g.snake) == 3
print("6. 重开 OK")
# 7. 存档路径在仓库外(约定 games/_saves)
m.SAVE_DIR = os.path.normpath(os.path.join(
os.path.dirname(os.path.abspath(__file__)), "games", "snake", "..", "_saves"))
assert "_saves" in m.SAVE_DIR and "snake" not in m.SAVE_DIR.replace("_saves", "", 1).rstrip(os.sep).split(os.sep)[-1]
print("7. 存档位于仓库外 OK")
shutil.rmtree(tmp, ignore_errors=True)
print("\n游戏逻辑全部测试通过 ✔")