class PowerControllerTest(unittest.TestCase): """ Tests for PowerStore. """ @classmethod def setUpClass(cls): SetTestMode() def setUp(self): self.power_communicator = mock.Mock() SetUpTestInjections(power_communicator=self.power_communicator, power_db=':memory:') self.store = PowerStore() def test_empty(self): """ Test an empty database. """ self.assertEqual({}, self.store.get_power_modules()) self.assertEqual(1, self.store.get_free_address()) self.store.register_power_module(1, POWER_MODULE) self.assertEqual( { 1: { 'id': 1, 'address': 1, 'name': u'', 'version': 8, 'input0': u'', 'input1': u'', 'input2': u'', 'input3': u'', 'input4': u'', 'input5': u'', 'input6': u'', 'input7': u'', 'sensor0': 0, 'sensor1': 0, 'sensor2': 0, 'sensor3': 0, 'sensor4': 0, 'sensor5': 0, 'sensor6': 0, 'sensor7': 0, 'times0': None, 'times1': None, 'times2': None, 'times3': None, 'times4': None, 'times5': None, 'times6': None, 'times7': None, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 } }, self.store.get_power_modules()) self.assertEqual(2, self.store.get_free_address()) self.store.register_power_module(5, POWER_MODULE) self.assertEqual( { 1: { 'id': 1, 'address': 1, 'name': u'', 'version': 8, 'input0': u'', 'input1': u'', 'input2': u'', 'input3': u'', 'input4': u'', 'input5': u'', 'input6': u'', 'input7': u'', 'sensor0': 0, 'sensor1': 0, 'sensor2': 0, 'sensor3': 0, 'sensor4': 0, 'sensor5': 0, 'sensor6': 0, 'sensor7': 0, 'times0': None, 'times1': None, 'times2': None, 'times3': None, 'times4': None, 'times5': None, 'times6': None, 'times7': None, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 }, 2: { 'id': 2, 'address': 5, 'name': u'', 'version': 8, 'input0': u'', 'input1': u'', 'input2': u'', 'input3': u'', 'input4': u'', 'input5': u'', 'input6': u'', 'input7': u'', 'sensor0': 0, 'sensor1': 0, 'sensor2': 0, 'sensor3': 0, 'sensor4': 0, 'sensor5': 0, 'sensor6': 0, 'sensor7': 0, 'times0': None, 'times1': None, 'times2': None, 'times3': None, 'times4': None, 'times5': None, 'times6': None, 'times7': None, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 } }, self.store.get_power_modules()) self.assertEqual(6, self.store.get_free_address()) def test_update(self): """ Test for updating the power module information. """ self.assertEqual({}, self.store.get_power_modules()) self.store.register_power_module(1, POWER_MODULE) self.assertEqual( { 1: { 'id': 1, 'address': 1, 'name': u'', 'version': 8, 'input0': u'', 'input1': u'', 'input2': u'', 'input3': u'', 'input4': u'', 'input5': u'', 'input6': u'', 'input7': u'', 'sensor0': 0, 'sensor1': 0, 'sensor2': 0, 'sensor3': 0, 'sensor4': 0, 'sensor5': 0, 'sensor6': 0, 'sensor7': 0, 'times0': None, 'times1': None, 'times2': None, 'times3': None, 'times4': None, 'times5': None, 'times6': None, 'times7': None, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 } }, self.store.get_power_modules()) times = ",".join(["00:00" for _ in range(14)]) self.store.update_power_module({ 'id': 1, 'name': 'module1', 'input0': 'in0', 'input1': 'in1', 'input2': 'in2', 'input3': 'in3', 'input4': 'in4', 'input5': 'in5', 'input6': 'in6', 'input7': 'in7', 'sensor0': 0, 'sensor1': 1, 'sensor2': 2, 'sensor3': 3, 'sensor4': 4, 'sensor5': 5, 'sensor6': 6, 'sensor7': 7, 'times0': times, 'times1': times, 'times2': times, 'times3': times, 'times4': times, 'times5': times, 'times6': times, 'times7': times, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 }) self.assertEqual( { 1: { 'id': 1, 'address': 1, 'version': 8, 'name': 'module1', 'input0': 'in0', 'input1': 'in1', 'input2': 'in2', 'input3': 'in3', 'input4': 'in4', 'input5': 'in5', 'input6': 'in6', 'input7': 'in7', 'sensor0': 0, 'sensor1': 1, 'sensor2': 2, 'sensor3': 3, 'sensor4': 4, 'sensor5': 5, 'sensor6': 6, 'sensor7': 7, 'times0': times, 'times1': times, 'times2': times, 'times3': times, 'times4': times, 'times5': times, 'times6': times, 'times7': times, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 } }, self.store.get_power_modules()) def test_module_exists(self): """ Test for module_exists. """ self.assertFalse(self.store.module_exists(1)) self.store.register_power_module(1, POWER_MODULE) self.assertTrue(self.store.module_exists(1)) self.assertFalse(self.store.module_exists(2)) def test_readdress_power_module(self): """ Test for readdress_power_module. """ self.store.register_power_module(1, POWER_MODULE) self.store.readdress_power_module(1, 2) self.assertFalse(self.store.module_exists(1)) self.assertTrue(self.store.module_exists(2)) self.assertEqual( { 1: { 'id': 1, 'address': 2, 'name': u'', 'version': 8, 'input0': u'', 'input1': u'', 'input2': u'', 'input3': u'', 'input4': u'', 'input5': u'', 'input6': u'', 'input7': u'', 'sensor0': 0, 'sensor1': 0, 'sensor2': 0, 'sensor3': 0, 'sensor4': 0, 'sensor5': 0, 'sensor6': 0, 'sensor7': 0, 'times0': None, 'times1': None, 'times2': None, 'times3': None, 'times4': None, 'times5': None, 'times6': None, 'times7': None, 'inverted0': 0, 'inverted1': 0, 'inverted2': 0, 'inverted3': 0, 'inverted4': 0, 'inverted5': 0, 'inverted6': 0, 'inverted7': 0 } }, self.store.get_power_modules()) def test_get_address(self): """ Test for get_address. """ self.assertEqual({}, self.store.get_power_modules()) self.store.register_power_module(1, POWER_MODULE) self.store.readdress_power_module(1, 3) self.assertEqual(3, self.store.get_address(1))
class PowerCommunicatorTest(unittest.TestCase): """ Tests for PowerCommunicator class """ @classmethod def setUpClass(cls): SetTestMode() def setUp(self): self.pubsub = PubSub() SetUpTestInjections(pubsub=self.pubsub) self.power_data = [] # type: list SetUpTestInjections(power_db=':memory:') self.serial = RS485(SerialMock(self.power_data)) self.store = PowerStore() SetUpTestInjections(power_serial=self.serial, power_store=self.store) self.communicator = PowerCommunicator() def tearDown(self): self.communicator.stop() self.serial.stop() def test_do_command(self): """ Test for standard behavior PowerCommunicator.do_command. """ action = power_api.get_voltage(power_api.POWER_MODULE) self.power_data.extend([ sin(action.create_input(1, 1)), sout(action.create_output(1, 1, 49.5)) ]) self.serial.start() self.communicator.start() output = self.communicator.do_command(1, action) self.assertEqual((49.5, ), output) self.assertEqual(14, self.communicator.get_communication_statistics()['bytes_written']) self.assertEqual(18, self.communicator.get_communication_statistics()['bytes_read']) def test_do_command_timeout_once(self): """ Test for timeout in PowerCommunicator.do_command. """ action = power_api.get_voltage(power_api.POWER_MODULE) self.power_data.extend([ sin(action.create_input(1, 1)), sout(bytearray()), sin(action.create_input(1, 2)), sout(action.create_output(1, 2, 49.5)) ]) self.serial.start() self.communicator.start() output = self.communicator.do_command(1, action) self.assertEqual((49.5, ), output) def test_do_command_timeout_twice(self): """ Test for timeout in PowerCommunicator.do_command. """ action = power_api.get_voltage(power_api.POWER_MODULE) self.power_data.extend([ sin(action.create_input(1, 1)), sout(bytearray()), sin(action.create_input(1, 2)), sout(bytearray()) ]) self.serial.start() self.communicator.start() with self.assertRaises(CommunicationTimedOutException): self.communicator.do_command(1, action) def test_do_command_split_data(self): """ Test PowerCommunicator.do_command when the data is split over multiple reads. """ action = power_api.get_voltage(power_api.POWER_MODULE) out = action.create_output(1, 1, 49.5) self.power_data.extend([ sin(action.create_input(1, 1)), sout(out[:5]), sout(out[5:]) ]) self.serial.start() self.communicator.start() output = self.communicator.do_command(1, action) self.assertEqual((49.5, ), output) def test_wrong_response(self): """ Test PowerCommunicator.do_command when the power module returns a wrong response. """ action_1 = power_api.get_voltage(power_api.POWER_MODULE) action_2 = power_api.get_frequency(power_api.POWER_MODULE) self.power_data.extend([ sin(action_1.create_input(1, 1)), sout(action_2.create_output(3, 2, 49.5)) ]) self.serial.start() self.communicator.start() with self.assertRaises(Exception): self.communicator.do_command(1, action_1) @mark.slow def test_address_mode(self): """ Test the address mode. """ events = [] def handle_events(master_event): events.append(master_event) self.pubsub.subscribe_master_events(PubSub.MasterTopics.POWER, handle_events) sad = power_api.set_addressmode(power_api.POWER_MODULE) sad_p1c = power_api.set_addressmode(power_api.P1_CONCENTRATOR) self.power_data.extend([ sin(sad.create_input(power_api.BROADCAST_ADDRESS, 1, power_api.ADDRESS_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 2, power_api.ADDRESS_MODE)), sout(power_api.want_an_address(power_api.POWER_MODULE).create_output(0, 0)), sin(power_api.set_address(power_api.POWER_MODULE).create_input(0, 0, 1)), sout(power_api.want_an_address(power_api.ENERGY_MODULE).create_output(0, 0)), sin(power_api.set_address(power_api.ENERGY_MODULE).create_input(0, 0, 2)), sout(power_api.want_an_address(power_api.P1_CONCENTRATOR).create_output(0, 0)), sin(power_api.set_address(power_api.P1_CONCENTRATOR).create_input(0, 0, 3)), sout(bytearray()), # Timeout read after 1 second sin(sad.create_input(power_api.BROADCAST_ADDRESS, 3, power_api.NORMAL_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 4, power_api.NORMAL_MODE)) ]) self.serial.start() self.communicator.start() self.assertEqual(self.store.get_free_address(), 1) self.communicator.start_address_mode() self.assertTrue(self.communicator.in_address_mode()) self.pubsub._publish_all_events() time.sleep(0.5) assert [] == events self.communicator.stop_address_mode() self.pubsub._publish_all_events() assert MasterEvent(MasterEvent.Types.POWER_ADDRESS_EXIT, {}) in events assert len(events) == 1 self.assertEqual(self.store.get_free_address(), 4) self.assertFalse(self.communicator.in_address_mode()) @mark.slow def test_do_command_in_address_mode(self): """ Test the behavior of do_command in address mode.""" action = power_api.get_voltage(power_api.POWER_MODULE) sad = power_api.set_addressmode(power_api.POWER_MODULE) sad_p1c = power_api.set_addressmode(power_api.P1_CONCENTRATOR) self.power_data.extend([ sin(sad.create_input(power_api.BROADCAST_ADDRESS, 1, power_api.ADDRESS_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 2, power_api.ADDRESS_MODE)), sout(bytearray()), # Timeout read after 1 second sin(sad.create_input(power_api.BROADCAST_ADDRESS, 3, power_api.NORMAL_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 4, power_api.NORMAL_MODE)), sin(action.create_input(1, 5)), sout(action.create_output(1, 5, 49.5)) ]) self.serial.start() self.communicator.start() self.communicator.start_address_mode() with self.assertRaises(InAddressModeException): self.communicator.do_command(1, action) self.communicator.stop_address_mode() self.assertEqual((49.5, ), self.communicator.do_command(1, action)) @mark.slow def test_address_mode_timeout(self): """ Test address mode timeout. """ action = power_api.get_voltage(power_api.POWER_MODULE) sad = power_api.set_addressmode(power_api.POWER_MODULE) sad_p1c = power_api.set_addressmode(power_api.P1_CONCENTRATOR) self.power_data.extend([ sin(sad.create_input(power_api.BROADCAST_ADDRESS, 1, power_api.ADDRESS_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 2, power_api.ADDRESS_MODE)), sout(bytearray()), # Timeout read after 1 second sin(sad.create_input(power_api.BROADCAST_ADDRESS, 3, power_api.NORMAL_MODE)), sin(sad_p1c.create_input(power_api.BROADCAST_ADDRESS, 4, power_api.NORMAL_MODE)), sin(action.create_input(1, 5)), sout(action.create_output(1, 5, 49.5)) ]) self.communicator = PowerCommunicator(address_mode_timeout=1) self.serial.start() self.communicator.start() self.communicator.start_address_mode() time.sleep(1.1) self.assertEqual((49.5, ), self.communicator.do_command(1, action)) @mark.slow def test_timekeeper(self): """ Test the TimeKeeper. """ self.store.register_power_module(1, power_api.POWER_MODULE) time_action = power_api.set_day_night(power_api.POWER_MODULE) times = [power_api.NIGHT for _ in range(8)] action = power_api.get_voltage(power_api.POWER_MODULE) self.power_data.extend([ sin(time_action.create_input(1, 1, *times)), sout(time_action.create_output(1, 1)), sin(action.create_input(1, 2)), sout(action.create_output(1, 2, 243)) ]) self.communicator = PowerCommunicator(time_keeper_period=1) self.serial.start() self.communicator.start() time.sleep(1.5) self.assertEqual((243, ), self.communicator.do_command(1, action))