def test_change_not_existing_electric_meter(self):
        # Init
        meter_id = 10
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': []
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        self.logic.state.electric_meters = {}
        self.logic.state.electric_meters_dict = {}

        self.db_file = FileLikeMock(value=json.dumps({
            'data_per_hour': self.data_per_hour,
            'keep_raw': self.keep_raw,
            'keep_day': self.keep_day,
            'keep_month': self.keep_month,
            'keep_year': self.keep_year,
            'keep_years': self.keep_years,
            'data_sources': []
        }))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        self.assertRaises(KeyError, self.logic.change_electric_meter, meter_id)
    def setUp(self) -> None:
        self.data_per_hour = 1
        self.keep_raw = 3
        self.keep_day = 48
        self.keep_month = 30
        self.keep_year = 12
        self.keep_years = 3

        self.plugin_type = 'plugin1'

        self.db_file = FileLikeMock()
        self.state_file = FileLikeMock()

        self.database: Database = Database.create_database(
            self.database_file_callback, self.data_per_hour, self.keep_raw,
            self.keep_day, self.keep_month, self.keep_year, self.keep_years)
        self.plugin_loader: PluginLoader = PluginLoader(
            {'ElectricMeterPlugins': {
                self.plugin_type: 'TestPlugin'
            }}, 'testplugins')

        self.state: State = State.get_state(self.state_file_callback,
                                            self.deserialize_electric_meter)

        self.logic = Logic(self.state, self.database, self.plugin_loader,
                           self.data_per_hour)
Example #3
0
    def __init__(self, state: State, database: Database, plugin_loader: PluginLoader, data_per_hour: int):
        Logic.logger.info('Electric meter read frequency: %d / h', data_per_hour)
        self.state = state
        self.database = database
        self.plugin_loader = plugin_loader

        # Set Serialization hook of loaded electric meters
        for electric_meter in state.get_electric_meters():
            electric_meter.set_serialize_hook(self._call_serialize_hook)

        # Timer to read electric_meters every db_step
        schedule.every(int(3600 / data_per_hour)).seconds.do(self._read_electric_meters)
        schedule_thread = threading.Thread(target=_run_scheduler)
        schedule_thread.daemon = True
        schedule_thread.start()
    def test_load_empty_file(self):
        empty_file = FileLikeMock()
        file_like_object_callback = Mock(return_value=empty_file)
        deserialize_electric_meter_callback = Mock()

        # Test
        state = State.get_state(file_like_object_callback, deserialize_electric_meter_callback)

        # Assertion
        self.assertIsNotNone(state)
        deserialize_electric_meter_callback.assert_not_called()
        empty_file.seek(0)
        self.assertEqual({
            'next_id': 10,
            'electric_meters': []
        }, json.load(empty_file))
    def test_load_electric_meter(self):
        # Setup
        meter_id = 1
        value = 321
        pin = 1
        active_low = False
        name = 'test'
        count = 456

        serialized_electric_meter = {
            "name": name,
            "custom": {
                "pin": pin,
                "active_low": active_low,
                "value": value,
                "count": count,
            }
        }

        deserialized_electric_meter = Mock()
        deserialize_electric_meter_callback = Mock(return_value=deserialized_electric_meter)

        # Test
        state = State.get_state(
            Mock(return_value=create_state_file(meter_id+1, meter_id, value, pin, active_low, name, count)),
            deserialize_electric_meter_callback)

        # Assert
        self.assertIsNotNone(state)
        deserialize_electric_meter_callback.assert_called_once_with(serialized_electric_meter)
        loaded_meters_dict = state.get_electric_meters_dict()
        self.assertIn(meter_id, loaded_meters_dict.keys())
        loaded_meter_dict = loaded_meters_dict[meter_id]
        loaded_meters = state.get_electric_meters()
        self.assertIn(meter_id, loaded_meters.keys())
        loaded_meter = loaded_meters[meter_id]

        self.assertEqual(deserialized_electric_meter, loaded_meter)
        self.assertEqual(serialized_electric_meter, loaded_meter_dict)
 def setUp(self) -> None:
     self.state_file_mock = FileLikeMock('')
     self.state_file_callback = Mock(return_value=self.state_file_mock)
     self.state = State(self.state_file_callback)
     self.addCleanup(self.cleanup)
class PersistenceTest(unittest.TestCase):

    def setUp(self) -> None:
        self.state_file_mock = FileLikeMock('')
        self.state_file_callback = Mock(return_value=self.state_file_mock)
        self.state = State(self.state_file_callback)
        self.addCleanup(self.cleanup)

    def cleanup(self) -> None:
        StringIO.close(self.state_file_mock)

    def test_get_state(self):
        file_like_object_callback = Mock(
            return_value=create_state_file(random.randint(10, 100), 10, 20, 1, False, 'Test', 5))
        state = State.get_state(file_like_object_callback, Mock())

        file_like_object_callback.assert_called_once_with('r')
        self.assertIsNotNone(state)

    def test_save_electric_meter(self):
        # Setup
        meter_id = random.randint(10, 100)
        self.state.next_id = meter_id + 1
        name = 'Name123'
        meter_type = 'TEST_PLUGIN'
        custom = {
            'custom1': 'value1',
            'custom2': 'value2',
            'custom3': 'value3'
        }
        self.state.electric_meters_dict = {
            meter_id: {
                'name': name,
                'type': meter_type,
                'custom': custom
            }
        }
        self.state.electric_meters = {
            meter_id: Mock()
        }

        # Test
        self.state.save_state()

        # Assertion
        self.state_file_callback.assert_called_with('w')
        self.assertTrue(self.state_file_mock.close_called)
        with self.state_file_mock:
            self.state_file_mock.seek(0)
            obj = json.load(self.state_file_mock)
            electric_meter_json = obj['electric_meters'][0]
            self.assertEqual({
                meter_id: {
                    'name': name,
                    'type': meter_type,
                    'custom': custom
                }
            }, self.state.electric_meters_dict)
            self.assertEqual(str(meter_id), str(electric_meter_json['id']))
            self.assertEqual(str(name), str(electric_meter_json['name']))
            self.assertEqual(str(meter_type), str(electric_meter_json['type']))
            self.assertEqual(str(custom), str(electric_meter_json['custom']))

    def test_load_electric_meter(self):
        # Setup
        meter_id = 1
        value = 321
        pin = 1
        active_low = False
        name = 'test'
        count = 456

        serialized_electric_meter = {
            "name": name,
            "custom": {
                "pin": pin,
                "active_low": active_low,
                "value": value,
                "count": count,
            }
        }

        deserialized_electric_meter = Mock()
        deserialize_electric_meter_callback = Mock(return_value=deserialized_electric_meter)

        # Test
        state = State.get_state(
            Mock(return_value=create_state_file(meter_id+1, meter_id, value, pin, active_low, name, count)),
            deserialize_electric_meter_callback)

        # Assert
        self.assertIsNotNone(state)
        deserialize_electric_meter_callback.assert_called_once_with(serialized_electric_meter)
        loaded_meters_dict = state.get_electric_meters_dict()
        self.assertIn(meter_id, loaded_meters_dict.keys())
        loaded_meter_dict = loaded_meters_dict[meter_id]
        loaded_meters = state.get_electric_meters()
        self.assertIn(meter_id, loaded_meters.keys())
        loaded_meter = loaded_meters[meter_id]

        self.assertEqual(deserialized_electric_meter, loaded_meter)
        self.assertEqual(serialized_electric_meter, loaded_meter_dict)

    def test_load_empty_file(self):
        empty_file = FileLikeMock()
        file_like_object_callback = Mock(return_value=empty_file)
        deserialize_electric_meter_callback = Mock()

        # Test
        state = State.get_state(file_like_object_callback, deserialize_electric_meter_callback)

        # Assertion
        self.assertIsNotNone(state)
        deserialize_electric_meter_callback.assert_not_called()
        empty_file.seek(0)
        self.assertEqual({
            'next_id': 10,
            'electric_meters': []
        }, json.load(empty_file))

    def test_load_invalid_syntax_file(self):
        # Setup
        # invalid file
        invalid_file = \
            '''{"next_id" next_id, 
                "electric_meters":[
                    {{"id": {id},
                     "vaue": {value},
                     "count": {count},
                     "nae: "{name}",
                     "pin: {pin},
                     "actie_low": act_low}
                    }}
                ]
            }}'''

        state_file_callback = Mock(return_value=StringIO(invalid_file))

        # Test
        # Assert
        self.assertRaises(Persistence.InvalidStateFileException, State.get_state, state_file_callback, Mock())

    def test_load_invalid_semantic_file(self):
        # Setup
        next_id = 20
        meter_id = 1
        value = -321
        pin = 123
        active_low = '\"invalid_semantics\"'
        name = 'test'
        count = 456
        error_message = 'Something bla bla could not be loaded'

        invalid_state_dict = {
            "next_id": next_id,
            "electric_meters": [{
                "id": meter_id,
                "name": name,
                "custom": {
                    "pin": pin,
                    "active_low": active_low,
                    "value": value,
                    "count": count
                }
            }]
        }

        state_file_callback = \
            Mock(return_value=StringIO(json.dumps(invalid_state_dict)))
        deserialize_electric_meter_callback = \
            Mock(side_effect=Plugin.ElectricMeterDeserializeException(error_message))

        # Test
        # Assert
        with self.assertLogs('logic.Persistence', level=logging.ERROR) as log:
            self.assertRaises(Plugin.ElectricMeterDeserializeException,
                              State.get_state, state_file_callback,
                              deserialize_electric_meter_callback)
            deserialize_electric_meter_callback.assert_called_once_with({
                "name": name,
                "custom": {
                    "pin": pin,
                    "active_low": active_low,
                    "value": value,
                    "count": count
                }
            })
            self.assertIn('ERROR:logic.Persistence:' + error_message, log.output)

    def test_update_electric_meter(self):
        electric_meter_id = random.randint(10, 100)
        electric_meter = Mock()
        electric_meter_dict = MagicMock()
        changed_electric_meter_dict = MagicMock()
        self.state.electric_meters = {
            electric_meter_id: electric_meter
        }
        self.state.electric_meters_dict = {
            electric_meter_id: electric_meter_dict
        }

        # Test
        self.state.update_electric_meter(electric_meter, changed_electric_meter_dict)

        # Assertion
        self.assertIn((electric_meter_id, electric_meter), self.state.electric_meters.items())
        self.assertIn((electric_meter_id, changed_electric_meter_dict), self.state.electric_meters_dict.items())

    def test_update_not_existing_electric_meter(self):
        not_existing_meter = Mock()

        # Test
        # TODO Exception type
        self.assertRaises(Exception, self.state.update_electric_meter, not_existing_meter, {})

    def test_add_electric_meter(self):
        electric_meter = Mock()
        self.state.save_state = Mock()
        expected_electric_meter_id = random.randint(10, 100)
        self.state.next_id = expected_electric_meter_id

        # Test
        electric_meter_id = self.state.add_electric_meter(electric_meter)

        # Assertions
        electric_meter.serialize.assert_called_once()
        self.state.save_state.assert_called_once()
        self.assertEqual(expected_electric_meter_id, electric_meter_id)
        self.assertEqual(self.state.next_id, electric_meter_id + 1)

    def test_add_duplicated_electric_meter(self):
        electric_meter = Mock()
        self.state.add_electric_meter(electric_meter)

        # Assertion
        # TODO Exception type
        self.assertRaises(Exception, self.state.add_electric_meter, electric_meter)

    def test_remove_electric_meter(self):
        meter_id = random.randint(10, 100)
        electric_meter = Mock()
        electric_meter_dict = MagicMock()
        self.state.electric_meters[meter_id] = electric_meter
        self.state.electric_meters_dict[meter_id] = electric_meter_dict

        # Test
        self.state.remove_electric_meter(meter_id)

        # Assertion
        self.state_file_callback.assert_called_with('w')
        self.assertNotIn((meter_id, electric_meter), self.state.electric_meters.items())
        self.assertNotIn((meter_id, electric_meter_dict), self.state.electric_meters_dict.items())

    def test_remove_non_existent_electric_meter(self):
        meter_id = random.randint(10, 100)

        # Assertion
        # TODO Exception type
        self.assertRaises(Exception, self.state.remove_electric_meter, meter_id)
    def test_change_invalid_custom_change_electric_meter(self):
        # Init
        meter_id = 10
        name = 'Electric Meter not to be changed'
        custom_value = 11111
        custom = {'num': custom_value}
        serialized_electric_meter = {
            'id': meter_id,
            'name': name,
            'type': self.plugin_type,
            'custom': {
                'custom_data': custom_value
            }
        }
        state_file_dict = {
            'next_id': 11,
            'electric_meters': [serialized_electric_meter]
        }
        self.state_file = FileLikeMock(json.dumps(state_file_dict))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        database_dict = {
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }
        self.db_file = FileLikeMock(json.dumps(database_dict))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        self.assertRaises(InvalidElectricMeterChangeException,
                          self.logic.change_electric_meter,
                          meter_id,
                          name=name,
                          custom={'num': -1})

        # Assert
        # Assert state file
        self.logic.state.save_state(
        )  # Trigger save of state to ensure changes are flushed to state_file
        self.assertEqual(state_file_dict,
                         json.loads(self.state_file.getvalue()))
        # Assert Electric Meter equality
        self.assertEqual(electric_meter.meter_type, self.plugin_type)
        self.assertEqual(electric_meter.name, name)
        self.assertEqual(electric_meter.custom_data, custom_value)
        # Assert Database
        self.logic.database._save_database()
        self.assertEqual(database_dict, json.loads(self.db_file.getvalue()))
    def test_change_electric_meter(self):
        # Init
        meter_id = 10
        changed_name = 'Changed name'
        changed_custom_value = 123
        serialized_electric_meter = {
            'id': meter_id,
            'name': 'Electric Meter to be deleted',
            'type': self.plugin_type,
            'custom': {
                'custom_data': 11111
            }
        }
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': [serialized_electric_meter]
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        electric_meter.set_serialize_hook(self.logic._call_serialize_hook)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        database_dict = {
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }
        self.db_file = FileLikeMock(value=json.dumps(database_dict))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        changed = self.logic.change_electric_meter(
            meter_id, name=changed_name, custom={'num': changed_custom_value})

        # Assert
        self.assertTrue(changed)
        # Assert state file
        self.logic.state.save_state(
        )  # Trigger save of state to ensure changes are flushed to state_file
        self.assertEqual(
            {
                'next_id':
                11,
                'electric_meters': [{
                    'id': meter_id,
                    'name': 'Changed name',
                    'type': self.plugin_type,
                    'custom': {
                        'custom_data': 123
                    }
                }]
            }, json.loads(self.state_file.getvalue()))
        # Assert Electric Meter changed
        self.assertEqual(electric_meter.meter_type, self.plugin_type)
        self.assertEqual(electric_meter.name, changed_name)
        self.assertEqual(electric_meter.custom_data, changed_custom_value)
        # Assert Database not changed
        self.logic.database._save_database()
        self.assertEqual(database_dict, json.loads(self.db_file.getvalue()))
    def test_remove_electric_meter(self):
        # Init
        meter_id = 10
        serialized_electric_meter = {
            'id': meter_id,
            'name': 'Electric Meter to be deleted',
            'type': self.plugin_type,
            'custom': {
                'custom_data': 11111
            }
        }
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': [serialized_electric_meter]
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        self.db_file = FileLikeMock(value=json.dumps({
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        removed_meter = self.logic.remove_electric_meter(meter_id)

        # Assert
        self.assertEqual(removed_meter, serialized_electric_meter)
        self.assertNotIn(electric_meter, self.state.electric_meters.values())
        self.assertEqual(0, len(self.state.electric_meters_dict.values()))
        self.assertEqual({
            'next_id': 11,
            'electric_meters': []
        }, json.loads(self.state_file.getvalue()))
        self.assertEqual(0, len(self.database.datasources))
        self.assertEqual(
            {
                'data_per_hour': self.data_per_hour,
                'keep_raw': self.keep_raw,
                'keep_day': self.keep_day,
                'keep_month': self.keep_month,
                'keep_year': self.keep_year,
                'keep_years': self.keep_years,
                'data_sources': []
            }, json.loads(self.db_file.getvalue()))