interview / tests /test_bfs.py
Lee93whut
feat(env): Gymnasium maze env, 3-channel obs, BFS reachability
fe0625d
"""tests/test_bfs.py —— BFS 寻路算法单元测试
覆盖范围
--------
TC-1 标准唯一通路
TC-2 起点与终点重合
TC-3 绝对无解(终点被墙封死)
TC-4 多条通路最优性(BFS 必须返回最短路)
TC-5 起点本身是墙(非法输入)
TC-6 性能基准:10×10 空旷地图 < 2ms
"""
from __future__ import annotations
import numpy as np
from maze_env.bfs import bfs
# ---------------------------------------------------------------------------
# TC-1 标准可行通路
# ---------------------------------------------------------------------------
def test_standard_path() -> None:
"""TC-1:唯一通路,验证 success / steps / path 首尾 / 路径长度一致性。
地图::
█ █ █ █ █
█ · · · █
█ █ █ · █
█ █ █ · █
█ █ █ █ █
起点 (1,1) → 终点 (3,3),唯一通路共 4 步。
"""
grid = np.array([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 0, 1],
[1, 1, 1, 0, 1],
[1, 1, 1, 1, 1],
], dtype=np.int32)
r = bfs(grid, start=(1, 1), end=(3, 3))
assert r["success"] is True
assert r["steps"] == 4
assert r["path"][0] == (1, 1)
assert r["path"][-1] == (3, 3)
assert len(r["path"]) == r["steps"] + 1
assert isinstance(r["execution_time_ms"], float)
# ---------------------------------------------------------------------------
# TC-2 起点与终点重合
# ---------------------------------------------------------------------------
def test_same_start_end() -> None:
"""TC-2:起点与终点重合,steps=0,path=[start]。"""
grid = np.array([
[1, 1, 1],
[1, 0, 1],
[1, 1, 1],
], dtype=np.int32)
r = bfs(grid, start=(1, 1), end=(1, 1))
assert r["success"] is True
assert r["steps"] == 0
assert r["path"] == [(1, 1)]
# ---------------------------------------------------------------------------
# TC-3 绝对无解(终点被墙四面封死)
# ---------------------------------------------------------------------------
def test_no_path_enclosed() -> None:
"""TC-3:终点被墙封死,无任何通路,优雅返回 False。
地图::
· █ █ █ █
█ █ █ █ █
█ █ · █ █
█ █ █ █ █
█ █ █ █ █
起点 (0,0) 孤立,终点 (2,2) 被墙完全包围,两者均无通路。
"""
grid = np.array([
[0, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 0, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
], dtype=np.int32)
r = bfs(grid, start=(0, 0), end=(2, 2))
assert r["success"] is False
assert r["steps"] == -1
assert r["path"] == []
# ---------------------------------------------------------------------------
# TC-4 多条通路最优性
# ---------------------------------------------------------------------------
def test_multiple_paths_shortest() -> None:
"""TC-4:多条通路最优性验证 —— BFS 必须返回最短路,而非 DFS 的长路。
设计说明
--------
6×6 开放网格(仅保留外围墙),起点 (1,1) 终点 (4,4)。
同时存在多条通路;曼哈顿距离下界 = |4-1| + |4-1| = 6。
断言 steps == 6 验证 BFS 返回最短路,而非 DFS / 随机游走可能返回的冗长路径。
"""
grid = np.array([
[1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1],
], dtype=np.int32)
r = bfs(grid, start=(1, 1), end=(4, 4))
assert r["success"] is True
# ── 最优性断言:BFS 必须返回最短路,非 DFS 可能返回的更长路径 ──────────
assert r["steps"] == 6, (
f"BFS 最优性失败:期望最短步数 6(曼哈顿距离),实际得到 {r['steps']}。"
"若此断言失败,说明算法使用了 DFS 或其他非最短路策略。"
)
assert r["path"][0] == (1, 1)
assert r["path"][-1] == (4, 4)
# 每步必须是四邻域移动(步距 == 1,不允许斜跨)
for i in range(len(r["path"]) - 1):
rr, rc = r["path"][i]
nr, nc = r["path"][i + 1]
assert abs(nr - rr) + abs(nc - rc) == 1, (
f"路径第 {i}{i+1} 步不是四邻域移动:{r['path'][i]}{r['path'][i+1]}"
)
# ---------------------------------------------------------------------------
# TC-5 起点本身是墙(非法输入)
# ---------------------------------------------------------------------------
def test_start_is_wall() -> None:
"""TC-5:起点本身是墙(非法输入),立即返回 success=False。"""
grid = np.array([
[1, 1, 1],
[1, 0, 1],
[1, 1, 1],
], dtype=np.int32)
r = bfs(grid, start=(0, 0), end=(1, 1)) # (0,0) 是墙
assert r["success"] is False
assert r["steps"] == -1
assert r["path"] == []
# ---------------------------------------------------------------------------
# TC-6 性能基准
# ---------------------------------------------------------------------------
def test_performance_10x10_empty() -> None:
"""TC-6:性能基准 —— 10×10 空旷地图单次 BFS 耗时必须 < 2ms。
设计说明
--------
10×10 地图(外围墙 + 全内部可通行),起点 (1,1) 终点 (8,8),
最大可能节点数 = 8×8 = 64,BFS 应在亚毫秒级完成。
若此断言失败,说明实现存在严重性能缺陷(如在循环内做 O(N) 查找),
应检查 visited 是否使用 set(而非 list)。
"""
grid = np.ones((10, 10), dtype=np.int32)
grid[1:9, 1:9] = 0 # 内部 8×8 区域全部清空(可通行)
result = bfs(grid, start=(1, 1), end=(8, 8))
assert result["success"] is True, "10×10 空旷地图应能找到路径"
assert result["steps"] == 14, (
f"10×10 空旷地图最短步数期望 14(曼哈顿距离),实际 {result['steps']}"
)
# ── 性能断言:< 2ms ────────────────────────────────────────────────────
assert result["execution_time_ms"] < 2.0, (
f"BFS 性能不达标:10×10 地图耗时 {result['execution_time_ms']:.4f}ms,"
f"阈值 2.0ms。请检查 visited 集合实现(应为 set,而非 list)。"
)