A clone of: https://github.com/nutechsoftware/alarmdecoder This is requires as they dropped support for older firmware releases w/o building in backward compatibility code, and they had previously hardcoded pyserial to a python2 only version.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

444 lines
16 KiB

  1. from unittest import TestCase
  2. from mock import Mock, MagicMock, patch
  3. from serial import Serial, SerialException
  4. import sys
  5. import socket
  6. import time
  7. import tempfile
  8. import os
  9. import select
  10. from alarmdecoder.devices import USBDevice, SerialDevice, SocketDevice
  11. from alarmdecoder.util import NoDeviceError, CommError, TimeoutError
  12. # Optional FTDI tests
  13. try:
  14. from pyftdi.pyftdi.ftdi import Ftdi, FtdiError
  15. from usb.core import USBError, Device as USBCoreDevice
  16. have_pyftdi = True
  17. except ImportError:
  18. have_pyftdi = False
  19. # Optional SSL tests
  20. try:
  21. from OpenSSL import SSL, crypto
  22. have_openssl = True
  23. except ImportError:
  24. have_openssl = False
  25. class TestSerialDevice(TestCase):
  26. def setUp(self):
  27. self._device = SerialDevice()
  28. self._device._device = Mock(spec=Serial)
  29. self._device._device.open = Mock()
  30. def tearDown(self):
  31. self._device.close()
  32. ### Tests
  33. def test_open(self):
  34. self._device.interface = '/dev/ttyS0'
  35. with patch.object(self._device._device, 'open') as mock:
  36. self._device.open(no_reader_thread=True)
  37. mock.assert_called_with()
  38. def test_open_no_interface(self):
  39. with self.assertRaises(NoDeviceError):
  40. self._device.open(no_reader_thread=True)
  41. self.assertFalse(self._device._running)
  42. def test_open_failed(self):
  43. self._device.interface = '/dev/ttyS0'
  44. with patch.object(self._device._device, 'open', side_effect=[SerialException, ValueError]):
  45. with self.assertRaises(NoDeviceError):
  46. self._device.open(no_reader_thread=True)
  47. with self.assertRaises(NoDeviceError):
  48. self._device.open(no_reader_thread=True)
  49. def test_write(self):
  50. self._device.interface = '/dev/ttyS0'
  51. self._device.open(no_reader_thread=True)
  52. with patch.object(self._device._device, 'write') as mock:
  53. self._device.write(b'test')
  54. mock.assert_called_with(b'test')
  55. def test_write_exception(self):
  56. with patch.object(self._device._device, 'write', side_effect=SerialException):
  57. with self.assertRaises(CommError):
  58. self._device.write(b'test')
  59. def test_read(self):
  60. self._device.interface = '/dev/ttyS0'
  61. self._device.open(no_reader_thread=True)
  62. with patch.object(self._device._device, 'read') as mock:
  63. with patch('serial.Serial.fileno', return_value=1):
  64. with patch.object(select, 'select', return_value=[[1], [], []]):
  65. ret = self._device.read()
  66. mock.assert_called_with(1)
  67. def test_read_exception(self):
  68. with patch.object(self._device._device, 'read', side_effect=SerialException):
  69. with patch('serial.Serial.fileno', return_value=1):
  70. with patch.object(select, 'select', return_value=[[1], [], []]):
  71. with self.assertRaises(CommError):
  72. self._device.read()
  73. def test_read_line(self):
  74. side_effect = list("testing\r\n")
  75. if sys.version_info > (3,):
  76. side_effect = [chr(x).encode('utf-8') for x in b"testing\r\n"]
  77. with patch.object(self._device._device, 'read', side_effect=side_effect):
  78. with patch('serial.Serial.fileno', return_value=1):
  79. with patch.object(select, 'select', return_value=[[1], [], []]):
  80. ret = None
  81. try:
  82. ret = self._device.read_line()
  83. except StopIteration:
  84. pass
  85. self.assertEquals(ret, "testing")
  86. def test_read_line_timeout(self):
  87. with patch.object(self._device._device, 'read', return_value=b'a') as mock:
  88. with patch('serial.Serial.fileno', return_value=1):
  89. with patch.object(select, 'select', return_value=[[1], [], []]):
  90. with self.assertRaises(TimeoutError):
  91. self._device.read_line(timeout=0.1)
  92. self.assertIn('a', self._device._buffer.decode('utf-8'))
  93. def test_read_line_exception(self):
  94. with patch.object(self._device._device, 'read', side_effect=[OSError, SerialException]):
  95. with patch('serial.Serial.fileno', return_value=1):
  96. with patch.object(select, 'select', return_value=[[1], [], []]):
  97. with self.assertRaises(CommError):
  98. self._device.read_line()
  99. with self.assertRaises(CommError):
  100. self._device.read_line()
  101. class TestSocketDevice(TestCase):
  102. def setUp(self):
  103. self._device = SocketDevice()
  104. self._device._device = Mock(spec=socket.socket)
  105. def tearDown(self):
  106. self._device.close()
  107. ### Tests
  108. def test_open(self):
  109. with patch.object(socket.socket, '__init__', return_value=None):
  110. with patch.object(socket.socket, 'connect', return_value=None) as mock:
  111. self._device.open(no_reader_thread=True)
  112. mock.assert_called_with(self._device.interface)
  113. def test_open_failed(self):
  114. with patch.object(socket.socket, 'connect', side_effect=socket.error):
  115. with self.assertRaises(NoDeviceError):
  116. self._device.open(no_reader_thread=True)
  117. def test_write(self):
  118. with patch.object(socket.socket, '__init__', return_value=None):
  119. with patch.object(socket.socket, 'connect', return_value=None):
  120. self._device.open(no_reader_thread=True)
  121. with patch.object(socket.socket, 'send') as mock:
  122. self._device.write(b'test')
  123. mock.assert_called_with(b'test')
  124. def test_write_exception(self):
  125. side_effects = [socket.error]
  126. if (have_openssl):
  127. side_effects.append(SSL.Error)
  128. with patch.object(self._device._device, 'send', side_effect=side_effects):
  129. with self.assertRaises(CommError):
  130. self._device.write(b'test')
  131. def test_read(self):
  132. with patch.object(socket.socket, '__init__', return_value=None):
  133. with patch.object(socket.socket, 'connect', return_value=None):
  134. self._device.open(no_reader_thread=True)
  135. with patch('socket.socket.fileno', return_value=1):
  136. with patch.object(select, 'select', return_value=[[1], [], []]):
  137. with patch.object(socket.socket, 'recv') as mock:
  138. self._device.read()
  139. mock.assert_called_with(1)
  140. def test_read_exception(self):
  141. with patch('socket.socket.fileno', return_value=1):
  142. with patch.object(select, 'select', return_value=[[1], [], []]):
  143. with patch.object(self._device._device, 'recv', side_effect=socket.error):
  144. with self.assertRaises(CommError):
  145. self._device.read()
  146. def test_read_line(self):
  147. side_effect = list("testing\r\n")
  148. if sys.version_info > (3,):
  149. side_effect = [chr(x).encode('utf-8') for x in b"testing\r\n"]
  150. with patch('socket.socket.fileno', return_value=1):
  151. with patch.object(select, 'select', return_value=[[1], [], []]):
  152. with patch.object(self._device._device, 'recv', side_effect=side_effect):
  153. ret = None
  154. try:
  155. ret = self._device.read_line()
  156. except StopIteration:
  157. pass
  158. self.assertEquals(ret, "testing")
  159. def test_read_line_timeout(self):
  160. with patch('socket.socket.fileno', return_value=1):
  161. with patch.object(select, 'select', return_value=[[1], [], []]):
  162. with patch.object(self._device._device, 'recv', return_value=b'a') as mock:
  163. with self.assertRaises(TimeoutError):
  164. self._device.read_line(timeout=0.1)
  165. self.assertIn('a', self._device._buffer.decode('utf-8'))
  166. def test_read_line_exception(self):
  167. with patch('socket.socket.fileno', return_value=1):
  168. with patch.object(select, 'select', return_value=[[1], [], []]):
  169. with patch.object(self._device._device, 'recv', side_effect=socket.error):
  170. with self.assertRaises(CommError):
  171. self._device.read_line()
  172. with self.assertRaises(CommError):
  173. self._device.read_line()
  174. def test_ssl(self):
  175. if not have_openssl:
  176. return
  177. ssl_key = crypto.PKey()
  178. ssl_key.generate_key(crypto.TYPE_RSA, 2048)
  179. ssl_cert = crypto.X509()
  180. ssl_cert.set_pubkey(ssl_key)
  181. ssl_ca_key = crypto.PKey()
  182. ssl_ca_key.generate_key(crypto.TYPE_RSA, 2048)
  183. ssl_ca_cert = crypto.X509()
  184. ssl_ca_cert.set_pubkey(ssl_ca_key)
  185. self._device.ssl = True
  186. self._device.ssl_key = ssl_key
  187. self._device.ssl_certificate = ssl_cert
  188. self._device.ssl_ca = ssl_ca_cert
  189. fileno, path = tempfile.mkstemp()
  190. # ..there has to be a better way..
  191. with patch.object(socket.socket, '__init__', return_value=None):
  192. with patch.object(socket.socket, 'connect', return_value=None) as mock:
  193. with patch.object(socket.socket, '_sock'):
  194. with patch.object(socket.socket, 'fileno', return_value=fileno):
  195. try:
  196. self._device.open(no_reader_thread=True)
  197. except SSL.SysCallError as ex:
  198. pass
  199. os.close(fileno)
  200. os.unlink(path)
  201. mock.assert_called_with(self._device.interface)
  202. self.assertIsInstance(self._device._device, SSL.Connection)
  203. def test_ssl_exception(self):
  204. if not have_openssl:
  205. return
  206. self._device.ssl = True
  207. self._device.ssl_key = 'None'
  208. self._device.ssl_certificate = 'None'
  209. self._device.ssl_ca = 'None'
  210. fileno, path = tempfile.mkstemp()
  211. # ..there has to be a better way..
  212. with patch.object(socket.socket, '__init__', return_value=None):
  213. with patch.object(socket.socket, 'connect', return_value=None) as mock:
  214. with patch.object(socket.socket, '_sock'):
  215. with patch.object(socket.socket, 'fileno', return_value=fileno):
  216. with self.assertRaises(CommError):
  217. try:
  218. self._device.open(no_reader_thread=True)
  219. except SSL.SysCallError as ex:
  220. pass
  221. os.close(fileno)
  222. os.unlink(path)
  223. if have_pyftdi:
  224. class TestUSBDevice(TestCase):
  225. def setUp(self):
  226. self._device = USBDevice()
  227. self._device._device = Mock(spec=Ftdi)
  228. self._device._device.usb_dev = Mock(spec=USBCoreDevice)
  229. self._device._device.usb_dev.bus = 0
  230. self._device._device.usb_dev.address = 0
  231. self._attached = False
  232. self._detached = False
  233. def tearDown(self):
  234. self._device.close()
  235. ### Library events
  236. def attached_event(self, sender, *args, **kwargs):
  237. self._attached = True
  238. def detached_event(self, sender, *args, **kwargs):
  239. self._detached = True
  240. ### Tests
  241. def test_find_default_param(self):
  242. with patch.object(Ftdi, 'find_all', return_value=[(0, 0, 'AD2', 1, 'AD2')]):
  243. device = USBDevice.find()
  244. self.assertEquals(device.interface, 'AD2')
  245. def test_find_with_param(self):
  246. with patch.object(Ftdi, 'find_all', return_value=[(0, 0, 'AD2-1', 1, 'AD2'), (0, 0, 'AD2-2', 1, 'AD2')]):
  247. device = USBDevice.find((0, 0, 'AD2-1', 1, 'AD2'))
  248. self.assertEquals(device.interface, 'AD2-1')
  249. device = USBDevice.find((0, 0, 'AD2-2', 1, 'AD2'))
  250. self.assertEquals(device.interface, 'AD2-2')
  251. def test_events(self):
  252. self.assertFalse(self._attached)
  253. self.assertFalse(self._detached)
  254. # this is ugly, but it works.
  255. with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2-1', 1, 'AD2'), (0, 0, 'AD2-2', 1, 'AD2')]):
  256. USBDevice.start_detection(on_attached=self.attached_event, on_detached=self.detached_event)
  257. with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2-2', 1, 'AD2')]):
  258. USBDevice.find_all()
  259. time.sleep(1)
  260. USBDevice.stop_detection()
  261. self.assertTrue(self._attached)
  262. self.assertTrue(self._detached)
  263. def test_find_all(self):
  264. with patch.object(USBDevice, 'find_all', return_value=[]) as mock:
  265. devices = USBDevice.find_all()
  266. self.assertEquals(devices, [])
  267. def test_find_all_exception(self):
  268. with patch.object(Ftdi, 'find_all', side_effect=[USBError('testing'), FtdiError]) as mock:
  269. with self.assertRaises(CommError):
  270. devices = USBDevice.find_all()
  271. with self.assertRaises(CommError):
  272. devices = USBDevice.find_all()
  273. def test_interface_serial_number(self):
  274. self._device.interface = 'AD2USB'
  275. self.assertEquals(self._device.interface, 'AD2USB')
  276. self.assertEquals(self._device.serial_number, 'AD2USB')
  277. self.assertEquals(self._device._device_number, 0)
  278. def test_interface_index(self):
  279. self._device.interface = 1
  280. self.assertEquals(self._device.interface, 1)
  281. self.assertEquals(self._device.serial_number, None)
  282. self.assertEquals(self._device._device_number, 1)
  283. def test_open(self):
  284. self._device.interface = 'AD2USB'
  285. with patch.object(self._device._device, 'open') as mock:
  286. self._device.open(no_reader_thread=True)
  287. mock.assert_called()
  288. def test_open_failed(self):
  289. self._device.interface = 'AD2USB'
  290. with patch.object(self._device._device, 'open', side_effect=[USBError('testing'), FtdiError]):
  291. with self.assertRaises(NoDeviceError):
  292. self._device.open(no_reader_thread=True)
  293. with self.assertRaises(NoDeviceError):
  294. self._device.open(no_reader_thread=True)
  295. def test_write(self):
  296. self._device.interface = 'AD2USB'
  297. self._device.open(no_reader_thread=True)
  298. with patch.object(self._device._device, 'write_data') as mock:
  299. self._device.write(b'test')
  300. mock.assert_called_with(b'test')
  301. def test_write_exception(self):
  302. with patch.object(self._device._device, 'write_data', side_effect=FtdiError):
  303. with self.assertRaises(CommError):
  304. self._device.write(b'test')
  305. def test_read(self):
  306. self._device.interface = 'AD2USB'
  307. self._device.open(no_reader_thread=True)
  308. with patch.object(self._device._device, 'read_data') as mock:
  309. self._device.read()
  310. mock.assert_called_with(1)
  311. def test_read_exception(self):
  312. with patch.object(self._device._device, 'read_data', side_effect=[USBError('testing'), FtdiError]):
  313. with self.assertRaises(CommError):
  314. self._device.read()
  315. with self.assertRaises(CommError):
  316. self._device.read()
  317. def test_read_line(self):
  318. with patch.object(self._device._device, 'read_data', side_effect=list("testing\r\n")):
  319. ret = None
  320. try:
  321. ret = self._device.read_line()
  322. except StopIteration:
  323. pass
  324. self.assertEquals(ret, b"testing")
  325. def test_read_line_timeout(self):
  326. with patch.object(self._device._device, 'read_data', return_value='a') as mock:
  327. with self.assertRaises(TimeoutError):
  328. self._device.read_line(timeout=0.1)
  329. self.assertIn('a', self._device._buffer)
  330. def test_read_line_exception(self):
  331. with patch.object(self._device._device, 'read_data', side_effect=[USBError('testing'), FtdiError]):
  332. with self.assertRaises(CommError):
  333. self._device.read_line()
  334. with self.assertRaises(CommError):
  335. self._device.read_line()