def test_string_fixed_in_message(self): """ Test the StringFixed scenario in an integration test """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V values: - type: StringFixed amount: 2 value: --- """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_occurrences = 0 for message in self.__get_outputted_messages(): if b'\r\nV\t---' in message: message_occurrences += 1 self.assertEqual(message_occurrences, 2)
def test_loop_in_message(self): """ Test the Loop scenario in an integration test """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V values: - type: Loop amount: 2 values: - type: IntFixed amount: 2 value: 1 - type: IntFixed amount: 2 value: 2 """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_values = [] for message in self.__get_outputted_messages(): if b'\r\nV\t1' in message: message_values.append(1) elif b'\r\nV\t2' in message: message_values.append(2) self.assertEqual(message_values, [1, 1, 2, 2, 1, 1, 2, 2])
def test_bit_error(self): """ Test if adding bit errors works properly We can unfortunately not unit test for corrupting the checksum since there is still a small chance that the checksum is still correct after adding bit errors """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V unit: V values: - type: IntFixed amount: 2 value: 1 """) self.config.create_scenarios() self.config.set_bit_error_rate(0.5) self.config.set_bit_error_checksum(False) emulator = Emulator(self.config) emulator.run() for message in self.__get_outputted_messages(): self.assertNotEqual(b'\r\nV\t1\r\nChecksum\t\x06', message) # Check if the message is corrupted self.assertTrue(check_checksum( message)) # Check if the checksum is still correct
def test_not_split_message(self): """ Check that a message is not split if it is not too long """ fields = [] for i in range(0, 21): fields.append({ 'name': chr(65 + i), 'key': chr(65 + i), 'values': [{ 'type': 'IntFixed', 'amount': 1, 'value': 1 }] }) self.config.set_config_dict({ 'device': 'Device', 'name': 'TextTest', 'protocol': 'text', 'fields': fields }) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() messages = self.__get_outputted_messages() self.assertEqual(len(messages), 1)
def test_bitbuffer_in_message(self): """ Test the Bitbuffer scenario in an integration test """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V values: - type: BitBuffer values: - type: IntFixed bits: 5 amount: 2 value: 1 - type: IntFixed bits: 3 amount: 3 value: 2 """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_occurrences = 0 for message in self.__get_outputted_messages(): if b'\r\nV\t10' in message: # 10 = (1 << 3) + 2 message_occurrences += 1 self.assertEqual(message_occurrences, 2)
def test_checksum_in_message(self): """ Check that a checksum is added to the message and that it is valid """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V unit: V values: - type: IntFixed amount: 2 value: 1 """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() checksums_valid = True for message in self.__get_outputted_messages(): checksums_valid = message.count( b'Checksum') == 1 # Only one Checksum per message checksums_valid = checksums_valid and check_checksum( message) # Checksum is valid if not checksums_valid: break self.assertTrue(checksums_valid)
def test_mapping_in_message(self): """ Test the Mapping scenario in an integration test """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Enabled key: E values: - type: Mapping amount: 2 dict: { 'OFF': 'OFF', 'ON': 'ON' } """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_occurrences = 0 for message in self.__get_outputted_messages(): if b'\r\nE\tOFF' in message or b'\r\nE\tON' in message: # 10 = (1 << 3) + 2 message_occurrences += 1 self.assertEqual(message_occurrences, 2)
def main(): """ Run the main emulator program """ config = EmulatorConfig() ascii_art() logger.info('Started') logger.info('Parsing config') config.set_config_file(file) config.set_input(input) config.set_output(output) config.set_delay(2) config.set_stop_condition('none') config.set_bit_error_rate(0.04) config.set_timed(False) logger.debug(f'Config parsed, fields: {config.get_fields()}') logger.info('Creating generators.') config.create_scenarios() logger.info('Generators created') logger.info('Emulation phase') emulator = Emulator(config) emulator.run() logger.info('Finished')
def test_preset_random_in_message(self): """ Test the random hex preset """ config = EmulatorConfig() config.set_config(""" device: Device name: TextTest protocol: hex version: 0x1234 product_id: 0x5678 fields: - name: Voltage key: V unit: V values: - type: IntFixed amount: 1 value: 1 preset: test preset_hex_fields: - v_hex: random """) config.set_delay(0) config.create_scenarios() config.set_input(self.input) config.set_output(self.output) emulator = Emulator(config) self.input.writeline(b':734120008\n') emulator.run() self.output.write.assert_any_call(b':73412000FF009\n')
def test_split_message(self): """ Check that a message that is too long is split into multiple messages """ fields = [] for i in range(0, 22): fields.append({ 'name': chr(65 + i), 'key': chr(65 + i), 'values': [{ 'type': 'IntFixed', 'amount': 1, 'value': 1 }] }) self.config.set_config_dict({ 'device': 'Device', 'name': 'TextTest', 'protocol': 'text', 'fields': fields }) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() messages = self.__get_outputted_messages() self.assertEqual(len(messages), 2) messages_fields = [] for message in messages: message_fields = [] for line in message.split(b'\r\n'): field = line.split(b'\t') if len(field) == 2: message_fields.append(field[0]) messages_fields.append(message_fields) for message_fields in messages_fields: # Message has at most 22 fields self.assertLessEqual(len(message_fields), 22) # Verify that all fields are only sent once, apart from the Checksum, which should be in every message self.assertEqual([b'Checksum'], [ field for field in messages_fields[0] if field in messages_fields[1] ])
def emulate(cli_config, config, input, input_type, output, output_type, delay, stop_condition, bit_error_rate, timed): """ This function runs the emulator with specified parameters """ logger = cli_config.logger emulator_config = EmulatorConfig() ascii_art(logger) logger.info('Started') logger.info('Parsing config') emulator_config.set_config_file(config) if input_type == 'serial': emulator_config.set_input(SerialInput(input)) elif input_type == 'file': emulator_config.set_input(FileInput(input)) else: # This should never happen since Click filters the input raise ValueError if output is None and output_type is 'serial': raise ClickException('No serial port provided. Provide a serial port using the `--output` argument or use a different output type using `--output-type`. Run `vemulator-cli emulate --help` for more information.') if output_type == 'serial': emulator_config.set_output(SerialOutput(output)) elif output_type == 'file': emulator_config.set_output(FileOutput(output)) elif output_type == 'standard': emulator_config.set_output(StandardOutput()) else: # This should never happen since Click filters the input raise ValueError emulator_config.set_delay(delay) # todo async_hex_persist docs in config emulator_config.set_stop_condition(stop_condition) emulator_config.set_bit_error_rate(bit_error_rate) emulator_config.set_timed(timed) logger.debug(f'Config parsed, fields: {emulator_config.get_fields()}') logger.info('Creating scenarios') emulator_config.create_scenarios() logger.info('Scenarios created') logger.info('Emulation phase') emulator = Emulator(emulator_config) emulator.run() logger.info('Finished')
def test_preset_random_in_message(self): """ Test the random preset """ self.config.set_config(""" device: Device name: TextTest protocol: text preset: test preset_fields: - v: random """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_occurrences = 0 for message in self.__get_outputted_messages(): if b'\r\nV\t1' in message: message_occurrences += 1 self.assertEqual(message_occurrences, 1)
def test_determinism(self): """ Test that the emulator is deterministic """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V unit: V values: - type: IntRandom amount: 100 min: -1000 max: 1000 """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() first_run_messages = self.__get_outputted_messages() # Reset output self.output = mock.create_autospec(OutputInterface) self.output.available.return_value = True self.config.set_output(self.output) # Do a second run self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() second_run_messages = self.__get_outputted_messages() self.assertEqual(first_run_messages, second_run_messages)
def test_arithmetic_in_message(self): """ Test the Arithmetic scenario in an integration test """ self.config.set_config(""" device: Device name: TextTest protocol: text fields: - name: Voltage key: V values: - type: IntFixed amount: 2 value: 2 - name: Amperage key: A values: - type: IntFixed amount: 3 value: 3 - name: Power key: W values: - type: Arithmetic value: V * A """) self.config.create_scenarios() emulator = Emulator(self.config) emulator.run() message_occurrences = 0 for message in self.__get_outputted_messages(): if b'\r\nW\t6' in message: message_occurrences += 1 self.assertEqual(message_occurrences, 3)
class HexIntegrationTestCase(unittest.TestCase): emulator_config = """ device: Device name: HexTest protocol: text_hex version: 0x1234 product_id: 0x5678 fields: - name: Voltage key: V unit: V values: - type: IntFixed amount: 1 value: 1 hex_fields: - name: Voltage key: 0x1234 unit: V values: - type: IntFixed amount: 2 value: 0xF00F bits: 16 - name: Amperage key: 0x1235 unit: A writable: true values: - type: IntFixed amount: 2 value: 0x0FF0 bits: 16 - name: VA key: 0x1236 values: - type: BitBuffer values: - type: IntFixed bits: 5 amount: 2 value: 1 - type: IntFixed bits: 3 amount: 3 value: 2 """ def setUp(self) -> None: self.config = EmulatorConfig() self.config.set_config(self.emulator_config) self.config.set_delay(0) self.config.create_scenarios() self.input = TestInput() self.config.set_input(self.input) self.output = mock.create_autospec(OutputInterface) self.output.available.return_value = True self.config.set_output(self.output) self.emulator = Emulator(self.config) def test_boot(self): """ Try sending a boot command """ self.input.writeline(b':051FA51FA51FA51FA51FADE\n') self.emulator.run() self.output.write.assert_any_call(b':4000051\n') def test_ping(self): """ Try sending a ping """ self.input.writeline(b':154\n') self.emulator.run() self.output.write.assert_any_call(b':534120A\n') def test_version(self): """ Try getting the firmware version """ self.input.writeline(b':352\n') self.emulator.run() self.output.write.assert_any_call(b':134120E\n') def test_product_id(self): """ Try getting the product id """ self.input.writeline(b':451\n') self.emulator.run() self.output.write.assert_any_call(b':1785686\n') def test_get_exists(self): """ Try getting an existing field """ self.input.writeline(b':734120008\n') self.emulator.run() self.output.write.assert_any_call(b':73412000FF009\n') def test_get_not_exists(self): """ Try getting a non-existing field """ self.input.writeline(b':712340008\n') self.emulator.run() self.output.write.assert_any_call(b':712340107\n') def test_set_writable(self): """ Try writing to a writable hex field """ self.input.writeline(b':83512001234C0\n') self.emulator.run() self.output.write.assert_any_call(b':83512001234C0\n') def test_set_not_writable(self): """ Try writing to a read-only hex field """ self.input.writeline(b':83412001234C1\n') self.emulator.run() self.output.write.assert_any_call(b':83412020FF006\n') def test_set_not_exists(self): """ Try writing a non-existing field """ self.input.writeline(b':81234001234C1\n') self.emulator.run() self.output.write.assert_any_call(b':812340106\n') def test_set_invalid_size(self): """ Try setting a field with too many bytes """ self.input.writeline(b':83512001234566A\n') self.emulator.run() self.output.write.assert_any_call(b':8351204F00F03\n') def test_unknown_command(self): """ Try sending an unknown command """ self.input.writeline(b':253\n') self.emulator.run() self.output.write.assert_any_call(b':3020050\n') def test_invalid_checksum(self): """ Try sending a command with an invalid checksum """ self.input.writeline(b':155\n') self.emulator.run() self.output.write.assert_any_call(b':4AAAAFD\n') def test_bitbuffer(self): """ Test the bitbuffer scenario """ self.input.writeline(b':736120006\n') self.emulator.run() self.output.write.assert_any_call(b':73612000AFC\n') def test_preset_random_in_message(self): """ Test the random hex preset """ config = EmulatorConfig() config.set_config(""" device: Device name: TextTest protocol: hex version: 0x1234 product_id: 0x5678 fields: - name: Voltage key: V unit: V values: - type: IntFixed amount: 1 value: 1 preset: test preset_hex_fields: - v_hex: random """) config.set_delay(0) config.create_scenarios() config.set_input(self.input) config.set_output(self.output) emulator = Emulator(config) self.input.writeline(b':734120008\n') emulator.run() self.output.write.assert_any_call(b':73412000FF009\n')