- EmotionEngine: 5状态马尔可夫情绪机 + 蒙特卡洛转移 - VectorMemory: TF-IDF向量记忆 + SQLite持久化 + RAG检索 - AgentBrain: Ollama/OpenAI/Dummy三后端LLM - BehaviorScheduler: 优先级/冷却/活跃度调度 - FastAPI服务器 + WebSocket实时推送 - perception: 键鼠监控 + 屏幕截图 - ui/pet_window: PySide6桌宠窗口 + 像素动画 - assets/pet: 5情绪各2帧像素艺术资源
169 lines
5.8 KiB
Python
169 lines
5.8 KiB
Python
"""
|
||
test_pet_window.py
|
||
==================
|
||
ui/pet_window 模块单元测试(使用 DummyPetWindow,headless 无 Qt 依赖)。
|
||
|
||
运行
|
||
----
|
||
cd /Users/e2hang/hermes/code/ezvibe
|
||
python -m ui.test_pet_window
|
||
"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
_ROOT = Path(__file__).parent.parent.resolve()
|
||
sys.path.insert(0, str(_ROOT))
|
||
|
||
import unittest
|
||
|
||
|
||
# ============================================================================
|
||
# DummyPetWindow Tests
|
||
# ============================================================================
|
||
|
||
class TestDummyPetWindow(unittest.TestCase):
|
||
"""DummyPetWindow API 契约测试。"""
|
||
|
||
def test_init_does_not_crash(self):
|
||
"""初始化不崩溃。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
self.assertIsInstance(w, DummyPetWindow)
|
||
|
||
def test_show_hide(self):
|
||
"""show/hide 控制可见性。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.show()
|
||
self.assertTrue(w.is_visible())
|
||
w.hide()
|
||
self.assertFalse(w.is_visible())
|
||
|
||
def test_close(self):
|
||
"""close 停止窗口。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.close()
|
||
self.assertFalse(w.is_visible())
|
||
|
||
def test_update_emotion_valid(self):
|
||
"""update_emotion 接受有效情绪。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.update_emotion("happy")
|
||
self.assertEqual(w.get_current_emotion(), "happy")
|
||
w.update_emotion("sleepy")
|
||
self.assertEqual(w.get_current_emotion(), "sleepy")
|
||
|
||
def test_update_emotion_invalid_defaults_to_idle(self):
|
||
"""update_emotion 忽略无效情绪,保持 idle。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.update_emotion("unknown_emotion")
|
||
self.assertEqual(w.get_current_emotion(), "idle")
|
||
|
||
def test_show_reminder_stores_message(self):
|
||
"""show_reminder 存储提醒消息。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.show_reminder("该喝水了!", priority=0)
|
||
w.show_reminder("站起来伸展一下!", priority=1)
|
||
self.assertEqual(len(w._reminders), 2)
|
||
self.assertIn("该喝水了!", w._reminders)
|
||
|
||
def test_position_get_set(self):
|
||
"""位置 get/set。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.set_position(200, 300)
|
||
x, y = w.get_position()
|
||
self.assertEqual((x, y), (200, 300))
|
||
|
||
def test_play_animation_no_exception(self):
|
||
"""play_animation 不抛异常。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
w.play_animation("bounce")
|
||
w.play_animation("wave")
|
||
w.play_animation("spin")
|
||
# 无断言——只要不崩溃即可
|
||
|
||
def test_repr(self):
|
||
"""repr 包含状态信息。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
w = DummyPetWindow()
|
||
r = repr(w)
|
||
self.assertIn("DummyPetWindow", r)
|
||
self.assertIn("idle", r)
|
||
|
||
|
||
# ============================================================================
|
||
# create_pet_window Factory Tests
|
||
# ============================================================================
|
||
|
||
class TestCreatePetWindow(unittest.TestCase):
|
||
"""create_pet_window 工厂函数测试。"""
|
||
|
||
def test_force_dummy_returns_dummy(self):
|
||
"""force_dummy=True 强制返回 DummyPetWindow。"""
|
||
from ui.pet_window import create_pet_window, DummyPetWindow
|
||
w = create_pet_window(force_dummy=True)
|
||
self.assertIsInstance(w, DummyPetWindow)
|
||
|
||
def test_default_returns_pet_window_or_dummy(self):
|
||
"""默认情况下返回 PetWindow 或 DummyPetWindow。"""
|
||
from ui.pet_window import create_pet_window, DummyPetWindow
|
||
w = create_pet_window()
|
||
# force_dummy 模式必定返回 DummyPetWindow
|
||
w2 = create_pet_window(force_dummy=True)
|
||
self.assertIsInstance(w2, DummyPetWindow)
|
||
|
||
def test_emotion_engine_wired(self):
|
||
"""emotion_engine 被正确传入。"""
|
||
from ui.pet_window import create_pet_window
|
||
mock_engine = object()
|
||
w = create_pet_window(emotion_engine=mock_engine)
|
||
self.assertIs(w._emotion, mock_engine)
|
||
|
||
def test_brain_wired(self):
|
||
"""brain 被正确传入。"""
|
||
from ui.pet_window import create_pet_window
|
||
mock_brain = object()
|
||
w = create_pet_window(brain=mock_brain)
|
||
self.assertIs(w._brain, mock_brain)
|
||
|
||
def test_scheduler_wired(self):
|
||
"""scheduler 被正确传入。"""
|
||
from ui.pet_window import create_pet_window
|
||
mock_scheduler = object()
|
||
w = create_pet_window(scheduler=mock_scheduler)
|
||
self.assertIs(w._scheduler, mock_scheduler)
|
||
|
||
|
||
# ============================================================================
|
||
# Emotion Emojis Mapping Tests
|
||
# ============================================================================
|
||
|
||
class TestEmotionEmojis(unittest.TestCase):
|
||
"""PetWindow.EMOTION_EMOJIS 完整性测试(通过 DummyPetWindow 访问)。"""
|
||
|
||
def test_all_emotions_have_emoji(self):
|
||
"""所有标准情绪都有 emoji 映射。"""
|
||
from ui.pet_window import DummyPetWindow
|
||
emotions = DummyPetWindow.EMOTIONS
|
||
emojis = DummyPetWindow.EMOTION_EMOJIS
|
||
for e in emotions:
|
||
self.assertIn(e, emojis, f"{e} 缺少 emoji 映射")
|
||
self.assertIsInstance(emojis[e], str)
|
||
self.assertGreater(len(emojis[e]), 0)
|
||
|
||
|
||
# ============================================================================
|
||
# Main
|
||
# ============================================================================
|
||
|
||
if __name__ == "__main__":
|
||
print("Running pet_window tests (headless, no Qt required) ...")
|
||
unittest.main(verbosity=2)
|