| @@ -0,0 +1,323 @@ | |||||
| import time | |||||
| from unittest import TestCase | |||||
| from mock import Mock, MagicMock, patch | |||||
| from ..ad2 import Overseer, AD2 | |||||
| from ..devices import USBDevice | |||||
| from ..messages import Message, RFMessage, LRRMessage, ExpanderMessage | |||||
| from ..event.event import Event, EventHandler | |||||
| from ..zonetracking import Zonetracker | |||||
| class TestOverseer(TestCase): | |||||
| def setUp(self): | |||||
| self._attached = False | |||||
| self._detached = False | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2', 1, 'AD2')]): | |||||
| self._overseer = Overseer() | |||||
| def tearDown(self): | |||||
| self._overseer.stop() | |||||
| def attached_event(self, sender, args): | |||||
| self._attached = True | |||||
| def detached_event(self, sender, args): | |||||
| self._detached = True | |||||
| def test_find_all(self): | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2', 1, 'AD2')]): | |||||
| devices = Overseer.find_all() | |||||
| self.assertEquals(devices[0][2], 'AD2') | |||||
| def test_create_default_param(self): | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2', 1, 'AD2')]): | |||||
| device = Overseer.create() | |||||
| self.assertEquals(device._device.interface, ('AD2', 0)) | |||||
| def test_create_with_param(self): | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2-1', 1, 'AD2'), (0, 0, 'AD2-2', 1, 'AD2')]): | |||||
| device = Overseer.create((0, 0, 'AD2-1', 1, 'AD2')) | |||||
| self.assertEquals(device._device.interface, ('AD2-1', 0)) | |||||
| device = Overseer.create((0, 0, 'AD2-2', 1, 'AD2')) | |||||
| self.assertEquals(device._device.interface, ('AD2-2', 0)) | |||||
| def test_events(self): | |||||
| self.assertEquals(self._attached, False) | |||||
| self.assertEquals(self._detached, False) | |||||
| # this is ugly, but it works. | |||||
| self._overseer.stop() | |||||
| self._overseer._detect_thread = Overseer.DetectThread(self._overseer) | |||||
| self._overseer.on_attached += self.attached_event | |||||
| self._overseer.on_detached += self.detached_event | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2-1', 1, 'AD2'), (0, 0, 'AD2-2', 1, 'AD2')]): | |||||
| self._overseer.start() | |||||
| with patch.object(USBDevice, 'find_all', return_value=[(0, 0, 'AD2-2', 1, 'AD2')]): | |||||
| Overseer.find_all() | |||||
| time.sleep(1) | |||||
| self._overseer.stop() | |||||
| self.assertEquals(self._attached, True) | |||||
| self.assertEquals(self._detached, True) | |||||
| class TestAD2(TestCase): | |||||
| def setUp(self): | |||||
| self._panicked = False | |||||
| self._relay_changed = False | |||||
| self._power_changed = False | |||||
| self._alarmed = False | |||||
| self._bypassed = False | |||||
| self._battery = (False, 0) | |||||
| self._fire = (False, 0) | |||||
| self._armed = False | |||||
| self._got_config = False | |||||
| self._message_received = False | |||||
| self._rfx_message_received = False | |||||
| self._lrr_message_received = False | |||||
| self._device = Mock(spec=USBDevice) | |||||
| self._device.on_open = EventHandler(Event(), self._device) | |||||
| self._device.on_close = EventHandler(Event(), self._device) | |||||
| self._device.on_read = EventHandler(Event(), self._device) | |||||
| self._device.on_write = EventHandler(Event(), self._device) | |||||
| self._ad2 = AD2(self._device) | |||||
| self._ad2._zonetracker = Mock(spec=Zonetracker) | |||||
| self._ad2._zonetracker.on_fault = EventHandler(Event(), self._ad2._zonetracker) | |||||
| self._ad2._zonetracker.on_restore = EventHandler(Event(), self._ad2._zonetracker) | |||||
| self._ad2.on_panic += self.on_panic | |||||
| self._ad2.on_relay_changed += self.on_relay_changed | |||||
| self._ad2.on_power_changed += self.on_power_changed | |||||
| self._ad2.on_alarm += self.on_alarm | |||||
| self._ad2.on_bypass += self.on_bypass | |||||
| self._ad2.on_low_battery += self.on_battery | |||||
| self._ad2.on_fire += self.on_fire | |||||
| self._ad2.on_arm += self.on_arm | |||||
| self._ad2.on_disarm += self.on_disarm | |||||
| self._ad2.on_config_received += self.on_config | |||||
| self._ad2.on_message += self.on_message | |||||
| self._ad2.on_rfx_message += self.on_rfx_message | |||||
| self._ad2.on_lrr_message += self.on_lrr_message | |||||
| self._ad2.address_mask = int('ffffffff', 16) | |||||
| self._ad2.open() | |||||
| def tearDown(self): | |||||
| pass | |||||
| def on_panic(self, sender, args): | |||||
| self._panicked = args | |||||
| def on_relay_changed(self, sender, args): | |||||
| self._relay_changed = True | |||||
| def on_power_changed(self, sender, args): | |||||
| self._power_changed = args | |||||
| def on_alarm(self, sender, args): | |||||
| self._alarmed = args | |||||
| def on_bypass(self, sender, args): | |||||
| self._bypassed = args | |||||
| def on_battery(self, sender, args): | |||||
| self._battery = args | |||||
| def on_fire(self, sender, args): | |||||
| self._fire = args | |||||
| def on_arm(self, sender, args): | |||||
| self._armed = True | |||||
| def on_disarm(self, sender, args): | |||||
| self._armed = False | |||||
| def on_config(self, sender, args): | |||||
| self._got_config = True | |||||
| def on_message(self, sender, args): | |||||
| self._message_received = True | |||||
| def on_rfx_message(self, sender, args): | |||||
| self._rfx_message_received = True | |||||
| def on_lrr_message(self, sender, args): | |||||
| self._lrr_message_received = True | |||||
| def test_open(self): | |||||
| self._ad2.open() | |||||
| self._device.open.assert_any_calls() | |||||
| def test_close(self): | |||||
| self._ad2.open() | |||||
| self._ad2.close() | |||||
| self._device.close.assert_any_calls() | |||||
| def test_send(self): | |||||
| self._ad2.send('test') | |||||
| self._device.write.assert_called_with('test') | |||||
| def test_get_config(self): | |||||
| self._ad2.get_config() | |||||
| self._device.write.assert_called_with("C\r") | |||||
| def test_save_config(self): | |||||
| self._ad2.save_config() | |||||
| self._device.write.assert_any_calls() | |||||
| def test_reboot(self): | |||||
| self._ad2.reboot() | |||||
| self._device.write.assert_called_with('=') | |||||
| def test_fault(self): | |||||
| self._ad2.fault_zone(1) | |||||
| self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 1)) | |||||
| def test_fault_wireproblem(self): | |||||
| self._ad2.fault_zone(1, simulate_wire_problem=True) | |||||
| self._device.write.assert_called_with("L{0:02}{1}\r".format(1, 2)) | |||||
| def test_clear_zone(self): | |||||
| self._ad2.clear_zone(1) | |||||
| self._device.write.assert_called_with("L{0:02}0\r".format(1)) | |||||
| def test_message(self): | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertIsInstance(msg, Message) | |||||
| self._ad2._on_read(self, '[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertTrue(self._message_received) | |||||
| def test_message_kpe(self): | |||||
| msg = self._ad2._handle_message('!KPE:[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertIsInstance(msg, Message) | |||||
| self._ad2._on_read(self, '[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertTrue(self._message_received) | |||||
| def test_expander_message(self): | |||||
| msg = self._ad2._handle_message('!EXP:07,01,01') | |||||
| self.assertIsInstance(msg, ExpanderMessage) | |||||
| def test_relay_message(self): | |||||
| self._ad2.open() | |||||
| msg = self._ad2._handle_message('!REL:12,01,01') | |||||
| self.assertIsInstance(msg, ExpanderMessage) | |||||
| self.assertEquals(self._relay_changed, True) | |||||
| def test_rfx_message(self): | |||||
| msg = self._ad2._handle_message('!RFX:0180036,80') | |||||
| self.assertIsInstance(msg, RFMessage) | |||||
| self.assertTrue(self._rfx_message_received) | |||||
| def test_panic(self): | |||||
| self._ad2.open() | |||||
| msg = self._ad2._handle_message('!LRR:012,1,ALARM_PANIC') | |||||
| self.assertEquals(self._panicked, True) | |||||
| msg = self._ad2._handle_message('!LRR:012,1,CANCEL') | |||||
| self.assertEquals(self._panicked, False) | |||||
| self.assertIsInstance(msg, LRRMessage) | |||||
| def test_config_message(self): | |||||
| self._ad2.open() | |||||
| msg = self._ad2._handle_message('!CONFIG>ADDRESS=18&CONFIGBITS=ff00&LRR=N&EXP=NNNNN&REL=NNNN&MASK=ffffffff&DEDUPLICATE=N') | |||||
| self.assertEquals(self._ad2.address, 18) | |||||
| self.assertEquals(self._ad2.configbits, int('ff00', 16)) | |||||
| self.assertEquals(self._ad2.address_mask, int('ffffffff', 16)) | |||||
| self.assertEquals(self._ad2.emulate_zone, [False for x in range(5)]) | |||||
| self.assertEquals(self._ad2.emulate_relay, [False for x in range(4)]) | |||||
| self.assertEquals(self._ad2.emulate_lrr, False) | |||||
| self.assertEquals(self._ad2.deduplicate, False) | |||||
| self.assertEquals(self._got_config, True) | |||||
| def test_power_changed_event(self): | |||||
| msg = self._ad2._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._power_changed, False) # Not set first time we hit it. | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._power_changed, False) | |||||
| msg = self._ad2._handle_message('[0000000100000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._power_changed, True) | |||||
| def test_alarm_event(self): | |||||
| msg = self._ad2._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._alarmed, False) # Not set first time we hit it. | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._alarmed, False) | |||||
| msg = self._ad2._handle_message('[0000000000100000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._alarmed, True) | |||||
| def test_zone_bypassed_event(self): | |||||
| msg = self._ad2._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._bypassed, False) # Not set first time we hit it. | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._bypassed, False) | |||||
| msg = self._ad2._handle_message('[0000001000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._bypassed, True) | |||||
| def test_armed_away_event(self): | |||||
| msg = self._ad2._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, False) # Not set first time we hit it. | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, False) | |||||
| msg = self._ad2._handle_message('[0100000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, True) | |||||
| self._armed = False | |||||
| msg = self._ad2._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, False) # Not set first time we hit it. | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, False) | |||||
| msg = self._ad2._handle_message('[0010000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._armed, True) | |||||
| def test_battery_low_event(self): | |||||
| msg = self._ad2._handle_message('[0000000000010000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._battery[0], True) | |||||
| # force the timeout to expire. | |||||
| with patch.object(time, 'time', return_value=self._battery[1] + 35): | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._battery[0], False) | |||||
| def test_fire_alarm_event(self): | |||||
| msg = self._ad2._handle_message('[0000000000000100----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._fire[0], True) | |||||
| # force the timeout to expire. | |||||
| with patch.object(time, 'time', return_value=self._fire[1] + 35): | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self.assertEquals(self._fire[0], False) | |||||
| def test_hit_for_faults(self): | |||||
| self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000],"Hit * for faults "') | |||||
| self._ad2._device.write.assert_called_with('*') | |||||
| def test_zonetracker_update(self): | |||||
| msg = self._ad2._handle_message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self._ad2._zonetracker.update.assert_called_with(msg) | |||||
| @@ -0,0 +1,315 @@ | |||||
| from unittest import TestCase | |||||
| from mock import Mock, MagicMock, patch | |||||
| from serial import Serial, SerialException | |||||
| from pyftdi.pyftdi.ftdi import Ftdi, FtdiError | |||||
| from usb.core import USBError, Device as USBCoreDevice | |||||
| import socket | |||||
| from OpenSSL import SSL, crypto | |||||
| from ..devices import USBDevice, SerialDevice, SocketDevice | |||||
| from ..util import NoDeviceError, CommError, TimeoutError | |||||
| class TestUSBDevice(TestCase): | |||||
| def setUp(self): | |||||
| self._device = USBDevice() | |||||
| self._device._device = Mock(spec=Ftdi) | |||||
| self._device._device.usb_dev = Mock(spec=USBCoreDevice) | |||||
| self._device._device.usb_dev.bus = 0 | |||||
| self._device._device.usb_dev.address = 0 | |||||
| def tearDown(self): | |||||
| self._device.close() | |||||
| def test_find_all(self): | |||||
| with patch.object(USBDevice, 'find_all', return_value=[]) as mock: | |||||
| devices = USBDevice.find_all() | |||||
| self.assertEquals(devices, []) | |||||
| def test_find_all_exception(self): | |||||
| with patch.object(Ftdi, 'find_all', side_effect=[USBError('testing'), FtdiError]) as mock: | |||||
| with self.assertRaises(CommError): | |||||
| devices = USBDevice.find_all() | |||||
| with self.assertRaises(CommError): | |||||
| devices = USBDevice.find_all() | |||||
| def test_open(self): | |||||
| self._device.interface = ('AD2USB', 0) | |||||
| with patch.object(self._device._device, 'open') as mock: | |||||
| self._device.open(no_reader_thread=True) | |||||
| mock.assert_any_calls() | |||||
| def test_open_failed(self): | |||||
| self._device.interface = ('AD2USB', 0) | |||||
| with patch.object(self._device._device, 'open', side_effect=[USBError('testing'), FtdiError]): | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| def test_write(self): | |||||
| self._device.interface = ('AD2USB', 0) | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(self._device._device, 'write_data') as mock: | |||||
| self._device.write('test') | |||||
| mock.assert_called_with('test') | |||||
| def test_write_exception(self): | |||||
| with patch.object(self._device._device, 'write_data', side_effect=FtdiError): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.write('test') | |||||
| def test_read(self): | |||||
| self._device.interface = ('AD2USB', 0) | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(self._device._device, 'read_data') as mock: | |||||
| self._device.read() | |||||
| mock.assert_called_with(1) | |||||
| def test_read_exception(self): | |||||
| with patch.object(self._device._device, 'read_data', side_effect=[USBError('testing'), FtdiError]): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read() | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read() | |||||
| def test_read_line(self): | |||||
| with patch.object(self._device._device, 'read_data', side_effect=list("testing\r\n")): | |||||
| ret = None | |||||
| try: | |||||
| ret = self._device.read_line() | |||||
| except StopIteration: | |||||
| pass | |||||
| self.assertEquals(ret, "testing") | |||||
| def test_read_line_timeout(self): | |||||
| with patch.object(self._device._device, 'read_data', return_value='a') as mock: | |||||
| with self.assertRaises(TimeoutError): | |||||
| self._device.read_line(timeout=0.1) | |||||
| self.assertIn('a', self._device._buffer) | |||||
| def test_read_line_exception(self): | |||||
| with patch.object(self._device._device, 'read_data', side_effect=[USBError('testing'), FtdiError]): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| class TestSerialDevice(TestCase): | |||||
| def setUp(self): | |||||
| self._device = SerialDevice() | |||||
| self._device._device = Mock(spec=Serial) | |||||
| self._device._device.open = Mock() | |||||
| def tearDown(self): | |||||
| self._device.close() | |||||
| def test_open(self): | |||||
| self._device.interface = '/dev/ttyS0' | |||||
| with patch.object(self._device._device, 'open') as mock: | |||||
| self._device.open(no_reader_thread=True) | |||||
| mock.assert_called_with() | |||||
| def test_open_no_interface(self): | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| self.assertFalse(self._device._running) | |||||
| def test_open_failed(self): | |||||
| self._device.interface = '/dev/ttyS0' | |||||
| with patch.object(self._device._device, 'open', side_effect=[SerialException, ValueError]): | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| def test_write(self): | |||||
| self._device.interface = '/dev/ttyS0' | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(self._device._device, 'write') as mock: | |||||
| self._device.write('test') | |||||
| mock.assert_called_with('test') | |||||
| def test_write_exception(self): | |||||
| with patch.object(self._device._device, 'write', side_effect=SerialException): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.write('test') | |||||
| def test_read(self): | |||||
| self._device.interface = '/dev/ttyS0' | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(self._device._device, 'read') as mock: | |||||
| self._device.read() | |||||
| mock.assert_called_with(1) | |||||
| def test_read_exception(self): | |||||
| with patch.object(self._device._device, 'read', side_effect=SerialException): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read() | |||||
| def test_read_line(self): | |||||
| with patch.object(self._device._device, 'read', side_effect=list("testing\r\n")): | |||||
| ret = None | |||||
| try: | |||||
| ret = self._device.read_line() | |||||
| except StopIteration: | |||||
| pass | |||||
| self.assertEquals(ret, "testing") | |||||
| def test_read_line_timeout(self): | |||||
| with patch.object(self._device._device, 'read', return_value='a') as mock: | |||||
| with self.assertRaises(TimeoutError): | |||||
| self._device.read_line(timeout=0.1) | |||||
| self.assertIn('a', self._device._buffer) | |||||
| def test_read_line_exception(self): | |||||
| with patch.object(self._device._device, 'read', side_effect=[OSError, SerialException]): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| class TestSocketDevice(TestCase): | |||||
| def setUp(self): | |||||
| self._device = SocketDevice() | |||||
| self._device._device = Mock(spec=socket.socket) | |||||
| def tearDown(self): | |||||
| self._device.close() | |||||
| def test_open(self): | |||||
| with patch.object(socket.socket, '__init__', return_value=None): | |||||
| with patch.object(socket.socket, 'connect', return_value=None) as mock: | |||||
| self._device.open(no_reader_thread=True) | |||||
| mock.assert_called_with(self._device.interface) | |||||
| def test_open_no_interface(self): | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| self.assertFalse(self._device._running) | |||||
| def test_open_failed(self): | |||||
| with patch.object(self._device._device, 'connect', side_effect=socket.error): | |||||
| with self.assertRaises(NoDeviceError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| def test_write(self): | |||||
| with patch.object(socket.socket, '__init__', return_value=None): | |||||
| with patch.object(socket.socket, 'connect', return_value=None): | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(socket.socket, 'send') as mock: | |||||
| self._device.write('test') | |||||
| mock.assert_called_with('test') | |||||
| def test_write_exception(self): | |||||
| with patch.object(self._device._device, 'send', side_effect=[SSL.Error, socket.error]): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.write('test') | |||||
| def test_read(self): | |||||
| with patch.object(socket.socket, '__init__', return_value=None): | |||||
| with patch.object(socket.socket, 'connect', return_value=None): | |||||
| self._device.open(no_reader_thread=True) | |||||
| with patch.object(socket.socket, 'recv') as mock: | |||||
| self._device.read() | |||||
| mock.assert_called_with(1) | |||||
| def test_read_exception(self): | |||||
| with patch.object(self._device._device, 'recv', side_effect=socket.error): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read() | |||||
| def test_read_line(self): | |||||
| with patch.object(self._device._device, 'recv', side_effect=list("testing\r\n")): | |||||
| ret = None | |||||
| try: | |||||
| ret = self._device.read_line() | |||||
| except StopIteration: | |||||
| pass | |||||
| self.assertEquals(ret, "testing") | |||||
| def test_read_line_timeout(self): | |||||
| with patch.object(self._device._device, 'recv', return_value='a') as mock: | |||||
| with self.assertRaises(TimeoutError): | |||||
| self._device.read_line(timeout=0.1) | |||||
| self.assertIn('a', self._device._buffer) | |||||
| def test_read_line_exception(self): | |||||
| with patch.object(self._device._device, 'recv', side_effect=socket.error): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| with self.assertRaises(CommError): | |||||
| self._device.read_line() | |||||
| def test_ssl(self): | |||||
| ssl_key = crypto.PKey() | |||||
| ssl_key.generate_key(crypto.TYPE_RSA, 2048) | |||||
| ssl_cert = crypto.X509() | |||||
| ssl_cert.set_pubkey(ssl_key) | |||||
| ssl_ca_key = crypto.PKey() | |||||
| ssl_ca_key.generate_key(crypto.TYPE_RSA, 2048) | |||||
| ssl_ca_cert = crypto.X509() | |||||
| ssl_ca_cert.set_pubkey(ssl_ca_key) | |||||
| self._device.ssl = True | |||||
| self._device.ssl_key = ssl_key | |||||
| self._device.ssl_certificate = ssl_cert | |||||
| self._device.ssl_ca = ssl_ca_cert | |||||
| # ..there has to be a better way.. | |||||
| with patch.object(socket.socket, '__init__', return_value=None): | |||||
| with patch.object(socket.socket, 'connect', return_value=None) as mock: | |||||
| with patch.object(socket.socket, '_sock'): | |||||
| with patch.object(socket.socket, 'fileno', return_value=1): | |||||
| self._device.open(no_reader_thread=True) | |||||
| mock.assert_called_with(self._device.interface) | |||||
| self.assertIsInstance(self._device._device, SSL.Connection) | |||||
| def test_ssl_exception(self): | |||||
| self._device.ssl = True | |||||
| self._device.ssl_key = 'None' | |||||
| self._device.ssl_certificate = 'None' | |||||
| self._device.ssl_ca = 'None' | |||||
| # ..there has to be a better way.. | |||||
| with patch.object(socket.socket, '__init__', return_value=None): | |||||
| with patch.object(socket.socket, 'connect', return_value=None) as mock: | |||||
| with patch.object(socket.socket, '_sock'): | |||||
| with patch.object(socket.socket, 'fileno', return_value=1): | |||||
| with self.assertRaises(CommError): | |||||
| self._device.open(no_reader_thread=True) | |||||
| @@ -0,0 +1,47 @@ | |||||
| from unittest import TestCase | |||||
| from ..messages import Message, ExpanderMessage, RFMessage, LRRMessage | |||||
| from ..util import InvalidMessageError | |||||
| class TestMessages(TestCase): | |||||
| def setUp(self): | |||||
| pass | |||||
| def tearDown(self): | |||||
| pass | |||||
| def test_message_parse(self): | |||||
| msg = Message('[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "') | |||||
| self.assertEquals(msg.numeric_code, '001') | |||||
| def test_message_parse_fail(self): | |||||
| with self.assertRaises(InvalidMessageError): | |||||
| msg = Message('') | |||||
| def test_expander_message_parse(self): | |||||
| msg = ExpanderMessage('!EXP:07,01,01') | |||||
| self.assertEquals(msg.address, 7) | |||||
| def test_expander_message_parse_fail(self): | |||||
| with self.assertRaises(InvalidMessageError): | |||||
| msg = ExpanderMessage('') | |||||
| def test_rf_message_parse(self): | |||||
| msg = RFMessage('!RFX:0180036,80') | |||||
| self.assertEquals(msg.serial_number, '0180036') | |||||
| def test_rf_message_parse_fail(self): | |||||
| with self.assertRaises(InvalidMessageError): | |||||
| msg = RFMessage('') | |||||
| def test_lrr_message_parse(self): | |||||
| msg = LRRMessage('!LRR:012,1,ARM_STAY') | |||||
| self.assertEquals(msg.event_type, 'ARM_STAY') | |||||
| def test_lrr_message_parse_fail(self): | |||||
| with self.assertRaises(InvalidMessageError): | |||||
| msg = LRRMessage('') | |||||
| @@ -0,0 +1,144 @@ | |||||
| from unittest import TestCase | |||||
| from mock import Mock, MagicMock | |||||
| from ..messages import Message, ExpanderMessage | |||||
| from ..zonetracking import Zonetracker, Zone | |||||
| class TestZonetracking(TestCase): | |||||
| def setUp(self): | |||||
| self._zonetracker = Zonetracker() | |||||
| self._zonetracker.on_fault += self.fault_event | |||||
| self._zonetracker.on_restore += self.restore_event | |||||
| self._faulted = False | |||||
| self._restored = False | |||||
| def tearDown(self): | |||||
| pass | |||||
| def fault_event(self, sender, args): | |||||
| self._faulted = True | |||||
| def restore_event(self, sender, args): | |||||
| self._restored = True | |||||
| def _build_expander_message(self, msg): | |||||
| msg = ExpanderMessage(msg) | |||||
| zone = self._zonetracker._expander_to_zone(msg.address, msg.channel) | |||||
| return zone, msg | |||||
| def test_zone_fault(self): | |||||
| zone, msg = self._build_expander_message('!EXP:07,01,01') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(self._zonetracker._zones[zone].status, Zone.FAULT) | |||||
| self.assertTrue(self._faulted) | |||||
| def test_zone_restore(self): | |||||
| zone, msg = self._build_expander_message('!EXP:07,01,01') | |||||
| self._zonetracker.update(msg) | |||||
| zone, msg = self._build_expander_message('!EXP:07,01,00') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(self._zonetracker._zones[zone].status, Zone.CLEAR) | |||||
| self.assertTrue(self._restored) | |||||
| def test_message_ready(self): | |||||
| msg = Message('[0000000000000010----],001,[f707000600e5800c0c020000]," "') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(len(self._zonetracker._zones_faulted), 1) | |||||
| msg = Message('[1000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(len(self._zonetracker._zones_faulted), 0) | |||||
| def test_message_fault_text(self): | |||||
| msg = Message('[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(len(self._zonetracker._zones_faulted), 1) | |||||
| def test_ECP_failure(self): | |||||
| msg = Message('[0000000000000010----],0bf,[f707000600e5800c0c020000],"CHECK 1 "') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertEquals(self._zonetracker._zones['1'].status, Zone.CHECK) | |||||
| def test_zone_restore_skip(self): | |||||
| panel_messages = [ | |||||
| '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "', | |||||
| '[0000000000000000----],002,[f707000600e5800c0c020000],"FAULT 2 "', | |||||
| '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "', | |||||
| '[0000000000000000----],001,[f707000600e5800c0c020000],"FAULT 1 "' | |||||
| ] | |||||
| for m in panel_messages: | |||||
| msg = Message(m) | |||||
| self._zonetracker.update(msg) | |||||
| self.assertIn(1, self._zonetracker._zones_faulted) | |||||
| self.assertNotIn(2, self._zonetracker._zones_faulted) | |||||
| def test_zone_out_of_order_fault(self): | |||||
| panel_messages = [ | |||||
| '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| '[0000000000000010----],003,[f707000600e5800c0c020000],"FAULT 3 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| ] | |||||
| for m in panel_messages: | |||||
| msg = Message(m) | |||||
| self._zonetracker.update(msg) | |||||
| self.assertIn(1, self._zonetracker._zones_faulted) | |||||
| self.assertIn(3, self._zonetracker._zones_faulted) | |||||
| self.assertIn(4, self._zonetracker._zones_faulted) | |||||
| def test_zone_multi_zone_skip_restore(self): | |||||
| panel_messages = [ | |||||
| '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| '[0000000000000010----],002,[f707000600e5800c0c020000],"FAULT 2 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| ] | |||||
| for m in panel_messages: | |||||
| msg = Message(m) | |||||
| self._zonetracker.update(msg) | |||||
| self.assertNotIn(1, self._zonetracker._zones_faulted) | |||||
| self.assertNotIn(2, self._zonetracker._zones_faulted) | |||||
| self.assertIn(4, self._zonetracker._zones_faulted) | |||||
| def test_zone_timeout_restore(self): | |||||
| panel_messages = [ | |||||
| '[0000000000000010----],001,[f707000600e5800c0c020000],"FAULT 1 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| '[0000000000000010----],002,[f707000600e5800c0c020000],"FAULT 2 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| '[0000000000000010----],004,[f707000600e5800c0c020000],"FAULT 4 "', | |||||
| ] | |||||
| for m in panel_messages: | |||||
| msg = Message(m) | |||||
| self._zonetracker.update(msg) | |||||
| self.assertIn(4, self._zonetracker._zones_faulted) | |||||
| self._zonetracker._zones[4].timestamp -= 35 # forcefully expire the zone | |||||
| # generic message to force an update. | |||||
| msg = Message('[0000000000000000----],000,[f707000600e5800c0c020000]," "') | |||||
| self._zonetracker.update(msg) | |||||
| self.assertNotIn(4, self._zonetracker._zones_faulted) | |||||
| @@ -21,9 +21,11 @@ setup(name='pyad2', | |||||
| license='', | license='', | ||||
| packages=['pyad2'], | packages=['pyad2'], | ||||
| install_requires=[ | install_requires=[ | ||||
| 'OpenSSL', | |||||
| 'pyopenssl', | |||||
| 'pyftdi' | 'pyftdi' | ||||
| ], | ], | ||||
| test_suite='nose.collector', | |||||
| tests_require=['nose', 'mock'], | |||||
| scripts=['bin/ad2-sslterm', 'bin/ad2-firmwareupload'], | scripts=['bin/ad2-sslterm', 'bin/ad2-firmwareupload'], | ||||
| include_package_data=True, | include_package_data=True, | ||||
| zip_safe=False) | zip_safe=False) | ||||