test_art_frame.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. from unittest.mock import AsyncMock, MagicMock, patch
  2. import pytest
  3. from bleak.backends.device import BLEDevice
  4. from switchbot import SwitchBotAdvertisement
  5. from switchbot.devices.art_frame import COMMAND_SET_IMAGE, SwitchbotArtFrame
  6. from . import ART_FRAME_INFO
  7. from .test_adv_parser import AdvTestCase, generate_ble_device
  8. def create_device_for_command_testing(
  9. adv_info: AdvTestCase,
  10. init_data: dict | None = None,
  11. ) -> SwitchbotArtFrame:
  12. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  13. device = SwitchbotArtFrame(
  14. ble_device, "ff", "ffffffffffffffffffffffffffffffff", model=adv_info.modelName
  15. )
  16. device.update_from_advertisement(
  17. make_advertisement_data(ble_device, adv_info, init_data)
  18. )
  19. device._send_command = AsyncMock()
  20. device._check_command_result = MagicMock()
  21. device.update = AsyncMock()
  22. return device
  23. def make_advertisement_data(
  24. ble_device: BLEDevice, adv_info: AdvTestCase, init_data: dict | None = None
  25. ) -> SwitchBotAdvertisement:
  26. """Set advertisement data with defaults."""
  27. if init_data is None:
  28. init_data = {}
  29. return SwitchBotAdvertisement(
  30. address="aa:bb:cc:dd:ee:ff",
  31. data={
  32. "rawAdvData": adv_info.service_data,
  33. "data": adv_info.data | init_data,
  34. "isEncrypted": False,
  35. "model": adv_info.model,
  36. "modelFriendlyName": adv_info.modelFriendlyName,
  37. "modelName": adv_info.modelName,
  38. }
  39. | init_data,
  40. device=ble_device,
  41. rssi=-80,
  42. active=True,
  43. )
  44. @pytest.mark.asyncio
  45. async def test_get_basic_info_none() -> None:
  46. device = create_device_for_command_testing(ART_FRAME_INFO)
  47. device._get_basic_info = AsyncMock(return_value=None)
  48. assert await device.get_basic_info() is None
  49. with pytest.raises(
  50. RuntimeError, match=r"Failed to retrieve basic info for current image index."
  51. ):
  52. await device._get_current_image_index()
  53. @pytest.mark.asyncio
  54. @pytest.mark.parametrize(
  55. ("basic_info", "result"),
  56. [
  57. (
  58. b"\x016\x07\x01\x00\x00\x04\x00\xde\x18\xa5\x00\x00\x00\x00\x00\x00",
  59. [
  60. False,
  61. 54,
  62. 0.7,
  63. 1,
  64. 0,
  65. 0,
  66. 0,
  67. 0,
  68. 4,
  69. [0, 222, 24, 165],
  70. ],
  71. ),
  72. ],
  73. )
  74. async def test_get_basic_info_parsing(
  75. basic_info: str, result: list[bool | int | float | list[int]]
  76. ) -> None:
  77. device = create_device_for_command_testing(ART_FRAME_INFO)
  78. device._get_basic_info = AsyncMock(return_value=basic_info)
  79. info = await device.get_basic_info()
  80. assert info["battery_charging"] == result[0]
  81. assert info["battery"] == result[1]
  82. assert info["firmware"] == result[2]
  83. assert info["hardware"] == result[3]
  84. assert info["display_size"] == result[4]
  85. assert info["display_mode"] == result[5]
  86. assert info["last_network_status"] == result[6]
  87. assert info["current_image_index"] == result[7]
  88. assert info["total_num_of_images"] == result[8]
  89. assert info["all_images_index"] == result[9]
  90. device._update_parsed_data(info)
  91. assert device.get_all_images_index() == result[9]
  92. assert device.get_total_images() == result[8]
  93. assert device.get_current_image_index() == result[7]
  94. @pytest.mark.asyncio
  95. async def test_select_image_with_single_image() -> None:
  96. device = create_device_for_command_testing(ART_FRAME_INFO)
  97. with (
  98. patch.object(device, "get_all_images_index", return_value=[1]),
  99. pytest.raises(RuntimeError, match=r"No images available to select from."),
  100. ):
  101. device._select_image_index(1)
  102. @pytest.mark.asyncio
  103. @pytest.mark.parametrize(
  104. ("current_index", "all_images_index", "expected_cmd"),
  105. [
  106. (100, [1, 100, 150], "150"),
  107. (150, [1, 100, 150], "1"),
  108. (1, [1, 100, 150], "100"),
  109. ],
  110. )
  111. async def test_next_image(
  112. current_index: int, all_images_index: list[int], expected_cmd: str
  113. ) -> None:
  114. device = create_device_for_command_testing(ART_FRAME_INFO)
  115. with (
  116. patch.object(device, "get_current_image_index", return_value=current_index),
  117. patch.object(device, "get_all_images_index", return_value=all_images_index),
  118. ):
  119. await device.next_image()
  120. device._send_command.assert_awaited_with(
  121. COMMAND_SET_IMAGE.format(f"{int(expected_cmd):02X}")
  122. )
  123. @pytest.mark.asyncio
  124. @pytest.mark.parametrize(
  125. ("current_index", "all_images_index", "expected_cmd"),
  126. [
  127. (100, [1, 100, 150], "1"),
  128. (150, [1, 100, 150], "100"),
  129. (1, [1, 100, 150], "150"),
  130. ],
  131. )
  132. async def test_prev_image(
  133. current_index: int, all_images_index: list[int], expected_cmd: str
  134. ) -> None:
  135. device = create_device_for_command_testing(ART_FRAME_INFO)
  136. with (
  137. patch.object(device, "get_current_image_index", return_value=current_index),
  138. patch.object(device, "get_all_images_index", return_value=all_images_index),
  139. ):
  140. await device.prev_image()
  141. device._send_command.assert_awaited_with(
  142. COMMAND_SET_IMAGE.format(f"{int(expected_cmd):02X}")
  143. )
  144. @pytest.mark.asyncio
  145. async def test_set_image_with_invalid_index() -> None:
  146. device = create_device_for_command_testing(ART_FRAME_INFO)
  147. with (
  148. patch.object(device, "get_total_images", return_value=3),
  149. patch.object(device, "get_all_images_index", return_value=[1, 2, 3]),
  150. pytest.raises(
  151. ValueError, match=r"Image index 5 is out of range. Total images: 3."
  152. ),
  153. ):
  154. await device.set_image(5)
  155. @pytest.mark.asyncio
  156. async def test_set_image_with_valid_index() -> None:
  157. device = create_device_for_command_testing(ART_FRAME_INFO)
  158. with (
  159. patch.object(device, "get_total_images", return_value=3),
  160. patch.object(device, "get_all_images_index", return_value=[10, 20, 30]),
  161. ):
  162. await device.set_image(1)
  163. device._send_command.assert_awaited_with(COMMAND_SET_IMAGE.format("14"))
  164. def test_default_model_classvar() -> None:
  165. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  166. device = SwitchbotArtFrame(ble_device, "ff", "ffffffffffffffffffffffffffffffff")
  167. assert device._model == ART_FRAME_INFO.modelName