class ErrorPacket2(Packet): __fields__ = [ ShortField('hello', 2), ShortField('potatoes', 3), ShortField('players', 2), ShortField('hello', 3) # hello field comes in double ]
class MiniBody(Packet): # noinspection PyArgumentList __fields__ = [ ShortField('arms', 2), ByteField('head', 1), ShortField('foot', 2), ShortField('teeth', 32), ByteField('nose', 1) ]
def test_should_correctly_initialize_field(self): field = ConditionalField(ShortField('foo', 3), lambda x: x) assert 'foo' == field.name assert 3 == field.value == field.default assert '!H' == field.struct_format assert 2 == field.size assert callable(field.condition)
def test_should_return_correct_byte_value_when_calling_raw_method( self, value, expected_data): packet = DummyPacket() packet.apples = value field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert expected_data == field.raw(packet) # type: ignore
def test_should_return_a_copy_of_field_when_calling_clone_method(self): field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) cloned_field = field.clone() assert cloned_field == field assert cloned_field is not field assert cloned_field._field is not field._field
def mini_ip_fields() -> List[Field]: """Returns list of fields used in various packet tests.""" return [ ByteBitsField([FieldPart('version', 4, 4), FieldPart('ihl', 5, 4)]), ShortField('length', 20), ShortEnumField('identification', 1, Identification), ShortBitsField([FieldPart('flags', 0b010, 3, Flags), FieldPart('offset', 0, 13)], hex=True), ]
def test_should_raise_error_if_callable_does_not_take_exactly_one_argument( self): def condition(x, y): return x + y with pytest.raises(TypeError) as exc_info: ConditionalField(ShortField('foo', 2), condition) # type: ignore message = f'callable {condition.__name__} must takes one parameter (a packet instance) but yours has 2' assert message == str(exc_info.value)
class MiniIP(Packet): # noinspection PyArgumentList __fields__ = [ ByteBitsField([FieldPart('version', 4, 4), FieldPart('ihl', 5, 4)]), ShortField('length', 20), ShortEnumField('identification', 1, Identification), ShortBitsField( [FieldPart('flags', 0b010, 3, Flags), FieldPart('offset', 0, 13)], hex=True), ]
def test_should_correctly_compute_field_value_when_calling_compute_value_method( self, apples, expected_data, value, value_was_computed): packet = DummyPacket() packet.apples = apples field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert field.value_was_computed is False assert expected_data == field.compute_value(b'\x00\x04hello', packet) # type: ignore assert value == field.value assert field.value_was_computed is value_was_computed
class TestCreatePacketClass: """Tests function create_packet_class""" # noinspection PyTypeChecker @pytest.mark.parametrize('class_name', [b'Foo', 4]) def test_should_raise_error_when_class_name_does_not_have_correct_type(self, class_name, mini_ip_fields): with pytest.raises(TypeError) as exc_info: create_packet_class(class_name, mini_ip_fields) assert f'class name must be a string but you provided {class_name}' == str(exc_info.value) @pytest.mark.parametrize('fields', [(), []]) def test_should_raise_error_when_list_field_is_empty(self, fields): with pytest.raises(ValueError) as exc_info: create_packet_class('MiniIP', fields) assert 'the list of fields must not be empty' == str(exc_info.value) # noinspection PyTypeChecker,PyArgumentList @pytest.mark.parametrize(('fields', 'message'), [ ('hello', f'each item in the list must be a Field object but you provided h'), ( [ShortField('length', 20), b'fast food'], f'each item in the list must be a Field object but you provided {b"fast food"}' ) ]) def test_should_raise_error_if_an_item_in_the_list_is_not_a_field_object(self, fields, message): with pytest.raises(TypeError) as exc_info: create_packet_class('MiniIP', fields) assert message == str(exc_info.value) def test_should_create_class_with_correct_attributes_when_giving_correct_input(self, mini_ip_fields): for fields in [mini_ip_fields, tuple(mini_ip_fields)]: mini_ip_class = create_packet_class('MiniIP', fields) assert issubclass(mini_ip_class, Packet) assert mini_ip_class.__fields__ == fields
class TestConditionalField: """Tests class ConditionalField.""" @pytest.mark.parametrize('arguments', [('foo', lambda x: x), (ShortField('foo', 2), 'foo')]) def test_should_raise_error_when_given_arguments_are_not_correct( self, arguments): with pytest.raises(TypeError): ConditionalField(*arguments) def test_should_raise_error_if_callable_does_not_take_exactly_one_argument( self): def condition(x, y): return x + y with pytest.raises(TypeError) as exc_info: ConditionalField(ShortField('foo', 2), condition) # type: ignore message = f'callable {condition.__name__} must takes one parameter (a packet instance) but yours has 2' assert message == str(exc_info.value) # test implementation of default field properties def test_should_correctly_initialize_field(self): field = ConditionalField(ShortField('foo', 3), lambda x: x) assert 'foo' == field.name assert 3 == field.value == field.default assert '!H' == field.struct_format assert 2 == field.size assert callable(field.condition) # test of value setter method def test_should_update_field_value_when_giving_correct_value(self): field = ConditionalField(ShortField('foo', 2), lambda x: x) field.value = 5 assert 5 == field.value # test of raw method @pytest.mark.parametrize(('value', 'expected_data'), [(2, b'\x00\x02'), (1, b'')]) def test_should_return_correct_byte_value_when_calling_raw_method( self, value, expected_data): packet = DummyPacket() packet.apples = value field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert expected_data == field.raw(packet) # type: ignore # test of compute_value method @pytest.mark.parametrize( ('apples', 'expected_data', 'value', 'value_was_computed'), [(2, b'hello', 4, True), (1, b'\x00\x04hello', 2, False)]) def test_should_correctly_compute_field_value_when_calling_compute_value_method( self, apples, expected_data, value, value_was_computed): packet = DummyPacket() packet.apples = apples field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert field.value_was_computed is False assert expected_data == field.compute_value(b'\x00\x04hello', packet) # type: ignore assert value == field.value assert field.value_was_computed is value_was_computed # test of clone method def test_should_return_a_copy_of_field_when_calling_clone_method(self): field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) cloned_field = field.clone() assert cloned_field == field assert cloned_field is not field assert cloned_field._field is not field._field # test of random method def test_should_return_a_correct_value_when_calling_random_value_method( self): field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert 0 <= field.random_value() <= RIGHT_SHORT # test of __repr__ method def test_should_return_correct_field_representation_when_calling_repr_function( self): inner_field = ShortField('foo', 2) field = ConditionalField(inner_field, lambda x: x) assert repr(inner_field) == repr(field) # test of __getattr__ method def test_should_find_inner_field_method_when_called_method_is_not_defined_in_conditional_field( self): class CustomShortField(ShortField): @staticmethod def return_hello(): return 'hello' field = ConditionalField(CustomShortField('foo', 2), lambda x: x) assert 'hello' == field.return_hello()
def test_should_return_correct_field_representation_when_calling_repr_function( self): inner_field = ShortField('foo', 2) field = ConditionalField(inner_field, lambda x: x) assert repr(inner_field) == repr(field)
def test_should_return_a_correct_value_when_calling_random_value_method( self): field = ConditionalField(ShortField('foo', 2), lambda pkt: pkt.apples in [0, 2]) assert 0 <= field.random_value() <= RIGHT_SHORT
class ErrorPacket1(Packet): __fields__ = [ ShortField('hello', 2), ByteBitsField([FieldPart('hello', 4, 4), FieldPart('foo', 4, 4)]) # hello field comes in double ]
def test_should_update_field_value_when_giving_correct_value(self): field = ConditionalField(ShortField('foo', 2), lambda x: x) field.value = 5 assert 5 == field.value
class Fruit(Packet): __fields__ = [ ShortField('apples', 2), ConditionalField(ShortField('pie', 1), lambda p: p.apples <= 2), ConditionalField(ShortField('juice', 1), lambda p: p.apples > 2) ]
class TestPacketClass: """Tests Packet class implementation through the use of custom MiniIP class""" @pytest.mark.parametrize('packet_class', [ErrorPacket1, ErrorPacket2]) def test_should_raise_error_when_a_field_name_is_defined_more_than_once(self, packet_class): with pytest.raises(AttributeError) as exc_info: packet_class() assert 'you already have a field with name hello' == str(exc_info.value) def test_should_raise_error_when_given_attribute_is_not_a_valid_field_name(self): with pytest.raises(AttributeError) as exc_info: MiniIP(version=5, foo=4, ihl=6) assert 'there is no attribute with name foo' == str(exc_info.value) def test_should_correctly_instantiate_packet_when_giving_no_arguments(self): mini_ip = MiniIP() assert mini_ip.version == 4 assert mini_ip.ihl == 5 assert mini_ip.length == 20 assert mini_ip.identification == 1 assert mini_ip.flags == 2 assert mini_ip.offset == 0 assert mini_ip.fields == mini_ip._fields == mini_ip.__fields__ assert mini_ip.fields is not mini_ip.__fields__ assert mini_ip.fields is not mini_ip._fields @pytest.mark.parametrize(('arguments', 'version', 'offset'), [ ({'version': 5}, 5, 0), ({'version': 5, 'offset': 12}, 5, 12) ]) def test_should_correctly_instantiate_packet_when_giving_correct_arguments(self, arguments, version, offset): mini_ip = MiniIP(**arguments) assert mini_ip.version == version assert mini_ip.length == 20 assert mini_ip.identification == 1 assert mini_ip.flags == 2 assert mini_ip.offset == offset assert mini_ip.__fields__ != mini_ip.fields # test of __setattr__ method def test_should_respect_default_setattr_behaviour_when_given_name_is_not_a_field_name(self, custom_ip): custom_ip.foo = 'bar' assert custom_ip.foo == 'bar' @pytest.mark.parametrize(('field', 'value', 'error'), [ (FixedStringField('game', 'pacman', 6, decode=True), 'super mario bros', ValueError), (ShortEnumField('identification', 1, Identification), 'youkoulele', ValueError), (ShortField('length', 3), 4.5, TypeError), (ShortBitsField([FieldPart('flags', 0b010, 3, Flags), FieldPart('offset', 0, 13)]), 'foo', ValueError), ]) def test_should_raise_error_when_given_value_is_not_correct(self, field, value, error): custom_class = create_packet_class('Custom', [field]) instance = custom_class() name = 'flags' if isinstance(field, ShortBitsField) else field.name with pytest.raises(error) as exc_info: setattr(instance, name, value) if name == 'identification': assert f'{name} has no value represented by {value}' == str(exc_info.value) @pytest.mark.parametrize(('field', 'value'), [ (FixedStringField('game', 'pacman', 6, decode=True), 'foobar'), (CustomStringField('fruit', 'apple', decode=True), 'pineapple'), (ShortField('length', 3), 10), ]) def test_should_set_field_value_when_giving_correct_name_and_value(self, field, value): custom_class = create_packet_class('Custom', [field]) instance = custom_class() setattr(instance, field.name, value) assert getattr(instance, field.name) == value @pytest.mark.parametrize(('field', 'given_value', 'expected_value'), [ (ShortEnumField('identification', 1, Identification), 3, 3), (ShortEnumField('identification', 1, Identification), Identification.lion.name, Identification.lion.value), (ShortEnumField('identification', 1, Identification), Identification.lion, Identification.lion.value) ]) def test_should_set_enum_field_value_when_giving_correct_name_and_value(self, field, given_value, expected_value): custom_class = create_packet_class('Custom', [field]) instance = custom_class() setattr(instance, field.name, given_value) assert getattr(instance, field.name) == expected_value @pytest.mark.parametrize(('given_value', 'expected_value'), [ (Flags.df.value, Flags.df.value), (Flags.df.name, Flags.df.value), (Flags.df, Flags.df.value) ]) def test_should_set_bits_field_value_when_giving_correct_name_and_value(self, given_value, expected_value): fields = [ShortBitsField([FieldPart('flags', 0b010, 3, Flags), FieldPart('offset', 0, 13)])] bit_class = create_packet_class('Bits', fields) instance = bit_class() instance.flags = given_value assert instance.flags == expected_value # test of raw property def test_should_correctly_compute_packet_byte_value_when_calling_raw_property(self, custom_ip, raw_mini_ip): assert raw_mini_ip == custom_ip.raw @pytest.mark.parametrize(('apples', 'pie', 'juice', 'data'), [ (1, 1, 0, b'\x00\x01\x00\x01'), (8, 0, 3, b'\x00\x08\x00\x03') ]) def test_should_compute_packet_byte_value_taking_in_account_conditional_fields(self, apples, pie, juice, data): fruit = Fruit(apples=apples, pie=pie, juice=juice) assert data == fruit.raw # test of all_fields_are_computed property def test_should_return_false_when_data_is_incomplete(self): fruit = Fruit.from_bytes(b'\x00\x01') assert fruit.all_fields_are_computed is False assert fruit.apples == 1 for field in fruit.fields[1:]: assert field.value_was_computed is False def test_should_return_correct_true_when_data_is_complete(self): fruit = Fruit.from_bytes(b'\x00\x01\x00\x01') assert fruit.all_fields_are_computed is True assert fruit.apples == 1 assert fruit.pie == 1 # pie field assert fruit.fields[1].value_was_computed is True # juice field assert fruit.fields[-1].value_was_computed is False # test of __bytes__ method def test_should_return_byte_value_when_calling_bytes_builtin_function(self, custom_ip): assert bytes(custom_ip) == custom_ip.raw # test of hexdump property def test_should_return_correct_hexadecimal_wireshark_view_when_calling_hexdump(self, custom_ip, mini_ip_hexdump): assert mini_ip_hexdump == custom_ip.hexdump # test of __eq__ method @pytest.mark.parametrize('value', [b'E\x00\x14\x00\x01@\x00', 4]) def test_should_raise_error_when_comparing_packet_to_a_non_packet_object(self, custom_ip, value): with pytest.raises(NotImplementedError): custom_ip.__eq__(value) @pytest.mark.parametrize(('packet_1', 'packet_2', 'boolean'), [ (MiniIP(), MiniIP(), True), (MiniIP(version=5), MiniIP(version=5), True), (MiniIP(version=5), MiniIP(), False) ]) def test_should_correctly_compare_two_packets(self, packet_1, packet_2, boolean): result = packet_1 == packet_2 assert result is boolean # test of __ne__ method @pytest.mark.parametrize('value', [b'E\x00\x14\x00\x01@\x00', 4]) def test_should_raise_error_when_comparing_difference_between_packet_and_a_non_packet(self, custom_ip, value): with pytest.raises(NotImplementedError): custom_ip.__ne__(value) @pytest.mark.parametrize(('packet_1', 'packet_2', 'boolean'), [ (MiniIP(), MiniIP(), False), (MiniIP(version=5), MiniIP(version=5), False), (MiniIP(version=5), MiniIP(), True) ]) def test_should_correctly_compare_opposition_of_two_packets(self, packet_1, packet_2, boolean): result = packet_1 != packet_2 assert result is boolean # test of clone method def test_should_return_a_copy_of_packet_when_calling_clone_method(self, custom_ip): cloned_ip = custom_ip.clone() assert isinstance(cloned_ip, MiniIP) assert cloned_ip == custom_ip assert cloned_ip is not custom_ip assert cloned_ip._fields is not custom_ip._fields assert cloned_ip._field_mapping is not custom_ip._field_mapping # test of from_bytes class method def test_should_create_packet_when_calling_from_bytes_method_with_raw_bytes(self): mini_ip = MiniIP(version=5, length=18) new_ip = MiniIP.from_bytes(mini_ip.raw) assert isinstance(new_ip, MiniIP) assert 5 == new_ip.version assert 18 == new_ip.length @pytest.mark.parametrize(('apples', 'pie', 'juice', 'data'), [ (2, 2, 1, b'\x00\x02\x00\x02'), (4, 1, 2, b'\x00\x04\x00\x02') ]) def test_should_create_packet_taking_in_account_conditional_field(self, apples, pie, juice, data): fruit = Fruit.from_bytes(data) assert fruit.apples == apples assert fruit.pie == pie assert fruit.juice == juice # test of random_packet class method def test_should_create_a_packet_with_random_values_when_calling_random_packet_method(self): mini_ip = MiniIP.random_packet() assert isinstance(mini_ip, MiniIP) assert 0 <= mini_ip.version <= 2 ** 4 - 1 assert 0 <= mini_ip.ihl <= 2 ** 4 - 1 assert 0 <= mini_ip.length <= RIGHT_SHORT assert 0 <= mini_ip.identification <= RIGHT_SHORT assert 0 <= mini_ip.flags <= 2 ** 3 - 1 assert 0 <= mini_ip.offset <= 2 ** 13 - 1 # test of evolve method def test_should_raise_error_when_calling_evolve_method_with_unknown_attributes(self, custom_ip): with pytest.raises(AttributeError) as exc_info: custom_ip.evolve(ihl=7, foo='bar') assert 'there is no attribute with name foo' == str(exc_info.value) def test_should_raise_error_when_calling_evolve_with_bad_value_for_an_attribute(self, custom_ip): with pytest.raises(ValueError) as exc_info: custom_ip.evolve(ihl='foo') assert 'ihl has no value represented by foo' == str(exc_info.value) def test_should_return_a_new_packet_with_updated_attributes_when_calling_evolve_method(self, custom_ip): new_ip = custom_ip.evolve(ihl=6) assert isinstance(new_ip, MiniIP) assert new_ip is not custom_ip assert 4 == new_ip.version assert 6 == new_ip.ihl assert 5 == custom_ip.ihl # test of __repr__ method @pytest.mark.parametrize(('value', 'hexadecimal'), [ (20, False), ('0x14', True) ]) def test_should_return_correct_packet_representation_when_calling_repr_function( self, custom_ip, value, hexadecimal ): custom_ip._fields[1]._hex = hexadecimal representation = f'<MiniIP: version=4, ihl=5, length={value}, identification=1, flags=0x2, offset=0x0>' assert representation == repr(custom_ip) @pytest.mark.parametrize(('apples', 'representation'), [ (2, '<Fruit: apples=2, pie=1>'), (4, '<Fruit: apples=4, juice=1>') ]) def test_should_represent_packet_taking_in_account_conditional_fields(self, apples, representation): fruit = Fruit(apples=apples) assert representation == repr(fruit) # test of show method @pytest.mark.parametrize(('value_1', 'value_2', 'hexadecimal'), [ (18, 20, False), ('0x12', '0x14', True) ]) def test_should_print_correct_packet_representation_when_calling_show_method( self, capsys, custom_ip, value_1, value_2, hexadecimal ): custom_ip._fields[1]._hex = hexadecimal custom_ip.length = 18 custom_ip.ihl = 6 custom_ip.show() captured = capsys.readouterr() output = ( 'version : FieldPart of ByteBitsField = 4 (4)\n' 'ihl : FieldPart of ByteBitsField = 6 (5)\n' f'length : ShortField = {value_1} ({value_2})\n' 'identification : ShortEnumField = 1 (1)\n' 'flags : FieldPart of ShortBitsField = 0x2 (0x2)\n' 'offset : FieldPart of ShortBitsField = 0x0 (0x0)\n' ) assert captured.out == output @pytest.mark.parametrize(('apples', 'representation'), [ (2, 'apples : ShortField = 2 (2)\npie : ShortField = 1 (1)\n'), (4, 'apples : ShortField = 4 (2)\njuice : ShortField = 1 (1)\n') ]) def test_should_print_packet_representation_taking_in_account_conditional_field( self, capsys, apples, representation ): fruit = Fruit(apples=apples) fruit.show() captured = capsys.readouterr() assert captured.out == representation