test_sync.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. sync_play_video函数单元测试
  5. 测试KodiServer类中的sync_play_video方法的各种场景
  6. 从配置文件中读取客户端配置
  7. """
  8. import sys
  9. import os
  10. import unittest
  11. from unittest.mock import Mock, patch, MagicMock
  12. import time
  13. import yaml
  14. # 添加项目根目录到 Python 路径
  15. current_dir = os.path.dirname(os.path.abspath(__file__))
  16. sys.path.append(current_dir)
  17. from kodi_util.kodi_server import KodiServer
  18. from kodi_util.kodi_module import KodiClient
  19. class TestSyncPlayVideoWithConfig(unittest.TestCase):
  20. """测试sync_play_video函数的单元测试类 - 从配置文件读取配置"""
  21. def setUp(self):
  22. """测试前的准备工作"""
  23. # 读取配置文件
  24. self.config_path = "config/config.yaml"
  25. self.sound_config_path = "config/ButtonListenerTask.yaml"
  26. # 加载主配置
  27. with open(self.config_path, 'r', encoding='utf-8') as f:
  28. self.main_config = yaml.safe_load(f)
  29. # 加载声音配置
  30. with open(self.sound_config_path, 'r', encoding='utf-8') as f:
  31. self.sound_config = yaml.safe_load(f)
  32. # 获取客户端配置
  33. self.client_configs = self.main_config.get('kodi_clients', [])
  34. self.sound_client_index = self.sound_config.get('sound', {}).get('client_index', 0)
  35. self.default_volume = self.sound_config.get('sound', {}).get('volume', 100)
  36. # 创建mock客户端,基于配置文件中的客户端数量
  37. self.mock_clients = []
  38. for i, client_config in enumerate(self.client_configs):
  39. mock_client = Mock(spec=KodiClient)
  40. mock_client.host = client_config.get('ip', f'192.168.1.{100+i}')
  41. mock_client.port = client_config.get('port', 8080)
  42. mock_client.username = client_config.get('username', 'kodi')
  43. mock_client.password = client_config.get('password', '123')
  44. mock_client.stop_playback.return_value = True
  45. mock_client.set_volume.return_value = True
  46. mock_client.play_video.return_value = {"result": "OK"}
  47. self.mock_clients.append(mock_client)
  48. # 创建KodiServer实例并手动设置客户端
  49. with patch('kodi_util.kodi_server.KodiStateMonitorThread'):
  50. with patch('os.path.exists', return_value=False):
  51. # 跳过配置文件加载,直接创建空的KodiServer
  52. self.kodi_server = KodiServer.__new__(KodiServer)
  53. self.kodi_server.clients = []
  54. self.kodi_server.state_monitor = None
  55. def test_sync_play_video_no_clients(self):
  56. """测试没有客户端时的情况"""
  57. result = self.kodi_server.sync_play_video("/path/to/video.mp4")
  58. self.assertFalse(result["success"])
  59. self.assertEqual(result["message"], "没有可用的客户端")
  60. def test_sync_play_video_with_config_clients_success(self):
  61. """测试使用配置文件中的客户端成功播放的情况"""
  62. # 设置客户端(使用配置文件中的前3个客户端进行测试)
  63. test_clients = self.mock_clients[:3] if len(self.mock_clients) >= 3 else self.mock_clients
  64. self.kodi_server.clients = test_clients
  65. with patch('time.sleep'): # Mock sleep以加速测试
  66. result = self.kodi_server.sync_play_video(
  67. "/sdcard/Movies/0.mp4", # 使用配置文件中的视频路径
  68. sound_client_index=self.sound_client_index,
  69. default_volume=self.default_volume
  70. )
  71. # 验证结果
  72. self.assertTrue(result["success"])
  73. self.assertEqual(len(result["success_clients"]), len(test_clients))
  74. self.assertEqual(len(result["failed_clients"]), 0)
  75. # 验证所有客户端的stop_playback被调用
  76. for client in test_clients:
  77. client.stop_playback.assert_called()
  78. client.play_video.assert_called_with("/sdcard/Movies/0.mp4", loop=False)
  79. def test_sync_play_video_with_config_sound_settings(self):
  80. """测试使用配置文件中的声音设置"""
  81. # 使用配置文件中的前2个客户端
  82. test_clients = self.mock_clients[:2] if len(self.mock_clients) >= 2 else self.mock_clients
  83. self.kodi_server.clients = test_clients
  84. with patch('time.sleep'):
  85. with patch('builtins.print') as mock_print:
  86. result = self.kodi_server.sync_play_video(
  87. "/sdcard/Movies/硫酸钠.mp4", # 使用配置文件中的视频路径
  88. sound_client_index=self.sound_client_index,
  89. default_volume=self.default_volume
  90. )
  91. # 验证结果
  92. self.assertTrue(result["success"])
  93. # 验证音量设置根据配置文件
  94. if self.sound_client_index == -1:
  95. # 所有客户端都播放声音
  96. for client in test_clients:
  97. client.set_volume.assert_called_with(self.default_volume)
  98. else:
  99. # 只有指定客户端播放声音
  100. for idx, client in enumerate(test_clients):
  101. if idx == self.sound_client_index:
  102. client.set_volume.assert_called_with(self.default_volume)
  103. mock_print.assert_any_call(f"客户端 {client.host} 将播放声音,音量设置为 {self.default_volume}%")
  104. else:
  105. client.set_volume.assert_called_with(0)
  106. mock_print.assert_any_call(f"客户端 {client.host} 将静音播放")
  107. def test_sync_play_video_all_config_videos(self):
  108. """测试配置文件中的所有视频路径"""
  109. if not self.mock_clients:
  110. self.skipTest("配置文件中没有客户端配置")
  111. # 使用第一个客户端进行测试
  112. self.kodi_server.clients = [self.mock_clients[0]]
  113. # 获取配置文件中的视频路径
  114. video_paths = self.main_config.get('video_paths', [])
  115. if not video_paths:
  116. video_paths = self.sound_config.get('video_paths', [])
  117. if not video_paths:
  118. self.skipTest("配置文件中没有视频路径配置")
  119. # 测试每个视频路径
  120. for video_path in video_paths[:3]: # 只测试前3个视频路径
  121. with self.subTest(video_path=video_path):
  122. with patch('time.sleep'):
  123. result = self.kodi_server.sync_play_video(
  124. video_path,
  125. sound_client_index=0,
  126. default_volume=self.default_volume
  127. )
  128. self.assertTrue(result["success"])
  129. self.mock_clients[0].play_video.assert_called_with(video_path, loop=False)
  130. def test_sync_play_video_invalid_sound_client_index_with_config(self):
  131. """测试无效的声音客户端索引(超出配置文件中的客户端数量)"""
  132. # 使用配置文件中的客户端
  133. test_clients = self.mock_clients[:2] if len(self.mock_clients) >= 2 else self.mock_clients
  134. self.kodi_server.clients = test_clients
  135. # 使用超出范围的索引
  136. invalid_index = len(test_clients) + 5
  137. with patch('time.sleep'):
  138. with patch('builtins.print') as mock_print:
  139. result = self.kodi_server.sync_play_video(
  140. "/sdcard/Movies/0.mp4",
  141. sound_client_index=invalid_index,
  142. default_volume=self.default_volume
  143. )
  144. # 验证结果仍然成功(使用默认客户端)
  145. self.assertTrue(result["success"])
  146. # 验证打印了警告信息
  147. mock_print.assert_any_call(f"警告:指定的声音客户端索引 {invalid_index} 超出范围,将使用默认客户端")
  148. def test_sync_play_video_client_failure_with_config(self):
  149. """测试配置文件中的客户端播放失败的情况"""
  150. if len(self.mock_clients) < 2:
  151. self.skipTest("配置文件中客户端数量不足,需要至少2个客户端")
  152. # 设置第一个客户端播放失败,第二个客户端成功
  153. self.mock_clients[0].play_video.return_value = {"result": "ERROR"}
  154. self.mock_clients[1].play_video.return_value = {"result": "OK"}
  155. self.kodi_server.clients = self.mock_clients[:2]
  156. with patch('time.sleep'):
  157. result = self.kodi_server.sync_play_video(
  158. "/sdcard/Movies/0.mp4",
  159. sound_client_index=0, # 第一个客户端是主客户端
  160. default_volume=self.default_volume
  161. )
  162. # 验证结果:部分成功
  163. self.assertTrue(result["success"]) # 至少有一个客户端成功
  164. self.assertEqual(len(result["success_clients"]), 1)
  165. self.assertEqual(len(result["failed_clients"]), 1)
  166. self.assertIn(self.mock_clients[1].host, result["success_clients"])
  167. self.assertIn(self.mock_clients[0].host, result["failed_clients"])
  168. def test_config_file_loading(self):
  169. """测试配置文件加载是否正确"""
  170. # 验证主配置文件加载
  171. self.assertIn('kodi_clients', self.main_config)
  172. self.assertIsInstance(self.main_config['kodi_clients'], list)
  173. self.assertGreater(len(self.main_config['kodi_clients']), 0)
  174. # 验证声音配置文件加载
  175. self.assertIn('sound', self.sound_config)
  176. self.assertIn('client_index', self.sound_config['sound'])
  177. self.assertIn('volume', self.sound_config['sound'])
  178. # 验证配置值的合理性
  179. self.assertIsInstance(self.sound_client_index, int)
  180. self.assertGreaterEqual(self.sound_client_index, -1)
  181. self.assertIsInstance(self.default_volume, int)
  182. self.assertGreaterEqual(self.default_volume, 0)
  183. self.assertLessEqual(self.default_volume, 100)
  184. def test_sync_play_video_with_real_config_integration(self):
  185. """集成测试:使用真实配置文件的完整测试"""
  186. if not self.mock_clients:
  187. self.skipTest("配置文件中没有客户端配置")
  188. # 使用配置文件中的所有客户端(最多5个以避免测试时间过长)
  189. test_clients = self.mock_clients[:5]
  190. self.kodi_server.clients = test_clients
  191. # 使用配置文件中的视频路径
  192. video_paths = self.main_config.get('video_paths', [])
  193. if not video_paths:
  194. video_paths = self.sound_config.get('video_paths', [])
  195. if video_paths:
  196. test_video = video_paths[0]
  197. else:
  198. test_video = "/sdcard/Movies/0.mp4"
  199. with patch('time.sleep'):
  200. with patch('builtins.print') as mock_print:
  201. result = self.kodi_server.sync_play_video(
  202. test_video,
  203. sound_client_index=self.sound_client_index,
  204. default_volume=self.default_volume
  205. )
  206. # 验证结果
  207. self.assertTrue(result["success"])
  208. self.assertEqual(len(result["success_clients"]), len(test_clients))
  209. self.assertEqual(len(result["failed_clients"]), 0)
  210. # 验证所有客户端都被正确调用
  211. for client in test_clients:
  212. client.stop_playback.assert_called()
  213. client.play_video.assert_called_with(test_video, loop=False)
  214. print(f"\n集成测试完成:")
  215. print(f"- 测试客户端数量: {len(test_clients)}")
  216. print(f"- 声音客户端索引: {self.sound_client_index}")
  217. print(f"- 默认音量: {self.default_volume}")
  218. print(f"- 测试视频: {test_video}")
  219. def run_tests():
  220. """运行所有测试"""
  221. print("开始运行sync_play_video函数单元测试(从配置文件读取配置)...")
  222. print("=" * 70)
  223. # 检查配置文件是否存在
  224. config_files = ["config/config.yaml", "config/ButtonListenerTask.yaml"]
  225. for config_file in config_files:
  226. if not os.path.exists(config_file):
  227. print(f"错误:配置文件 {config_file} 不存在")
  228. return False
  229. # 创建测试套件
  230. test_suite = unittest.TestLoader().loadTestsFromTestCase(TestSyncPlayVideoWithConfig)
  231. # 运行测试
  232. runner = unittest.TextTestRunner(verbosity=2)
  233. result = runner.run(test_suite)
  234. print("=" * 70)
  235. print(f"测试完成!")
  236. print(f"运行测试数量: {result.testsRun}")
  237. print(f"失败数量: {len(result.failures)}")
  238. print(f"错误数量: {len(result.errors)}")
  239. if result.failures:
  240. print("\n失败的测试:")
  241. for test, traceback in result.failures:
  242. print(f"- {test}: {traceback}")
  243. if result.errors:
  244. print("\n错误的测试:")
  245. for test, traceback in result.errors:
  246. print(f"- {test}: {traceback}")
  247. return result.wasSuccessful()
  248. if __name__ == "__main__":
  249. success = run_tests()
  250. sys.exit(0 if success else 1)