|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +"""HIP's ``alias`` is readable while the header is still being decoded. |
| 3 | +
|
| 4 | +GitHub issue #1184: :attr:`HIP.alias <pcapkit.protocols.internet.hip.HIP.alias>` |
| 5 | +read ``self._info.version``. Outside an IPv6 extension chain, |
| 6 | +:meth:`HIP.read <pcapkit.protocols.internet.hip.HIP.read>` ends in |
| 7 | +``_decode_next_layer``, which reads ``alias`` for the protocol chain, but |
| 8 | +``_info`` is assigned only once ``read`` has returned. So parsing or |
| 9 | +constructing a standalone HIP header, and |
| 10 | +:meth:`~pcapkit.protocols.protocol.ProtocolBase.from_data` over a parsed one, |
| 11 | +raised ``AttributeError: 'HIP' object has no attribute '_info'``. |
| 12 | +
|
| 13 | +Every case builds its own octets in memory and reads no capture. :class:`HIP` |
| 14 | +is imported inside each test, after |
| 15 | +:func:`~tests._support.reimport_once_per_class`, not at module load. |
| 16 | +
|
| 17 | +""" |
| 18 | + |
| 19 | +import importlib.util |
| 20 | +import unittest |
| 21 | + |
| 22 | +from tests._support import reimport_once_per_class |
| 23 | + |
| 24 | +RUNTIME_DEPS = ('tbtrim', 'aenum', 'chardet', 'dictdumper') |
| 25 | +HAS_RUNTIME = all(importlib.util.find_spec(name) is not None for name in RUNTIME_DEPS) |
| 26 | + |
| 27 | +#: Header fields shared by every HIP packet built here, as in |
| 28 | +#: :data:`examples.generators.options.HIP_BASE`. |
| 29 | +HIP_BASE = { |
| 30 | + 'next': 59, 'packet': 1, 'checksum': b'\x00\x00', |
| 31 | + 'controls_anonymous': False, 'shit': 0, 'rhit': 0, 'payload': b'', |
| 32 | +} |
| 33 | + |
| 34 | + |
| 35 | +@unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed') |
| 36 | +class TestHIPFromDataAlias(unittest.TestCase): |
| 37 | + """Pin ``alias`` on the paths that decode a next layer.""" |
| 38 | + |
| 39 | + def setUp(self) -> None: |
| 40 | + reimport_once_per_class(self) |
| 41 | + |
| 42 | + @staticmethod |
| 43 | + def _parameters() -> 'list': |
| 44 | + from pcapkit.const.hip.parameter import Parameter |
| 45 | + |
| 46 | + return [(Parameter.R1_COUNTER, {'counter': 0x0123456789abcdef})] |
| 47 | + |
| 48 | + def _octets(self, version: 'int') -> 'bytes': |
| 49 | + from pcapkit.protocols.internet.hip import HIP |
| 50 | + |
| 51 | + return HIP(parameters=self._parameters(), extension=True, |
| 52 | + version=version, **HIP_BASE).data |
| 53 | + |
| 54 | + def test_construct_standalone_header(self) -> None: |
| 55 | + from pcapkit.protocols.internet.hip import HIP |
| 56 | + |
| 57 | + for version in (1, 2): |
| 58 | + with self.subTest(version=version): |
| 59 | + proto = HIP(parameters=self._parameters(), version=version, **HIP_BASE) |
| 60 | + self.assertEqual(proto.alias, f'HIPv{version}') |
| 61 | + self.assertEqual(proto.data, self._octets(version)) |
| 62 | + |
| 63 | + def test_parse_standalone_header(self) -> None: |
| 64 | + from pcapkit.protocols.internet.hip import HIP |
| 65 | + |
| 66 | + for version in (1, 2): |
| 67 | + with self.subTest(version=version): |
| 68 | + octets = self._octets(version) |
| 69 | + proto = HIP(octets, len(octets)) |
| 70 | + self.assertEqual(proto.alias, f'HIPv{version}') |
| 71 | + self.assertEqual(str(proto.protochain).split(':')[0], f'HIPv{version}') |
| 72 | + |
| 73 | + def test_from_data_rebuilds_extension_header_byte_for_byte(self) -> None: |
| 74 | + from pcapkit.protocols.internet.hip import HIP |
| 75 | + |
| 76 | + for version in (1, 2): |
| 77 | + with self.subTest(version=version): |
| 78 | + octets = self._octets(version) |
| 79 | + parsed = HIP(octets, len(octets), extension=True) |
| 80 | + rebuilt = HIP.from_data(parsed.info) |
| 81 | + self.assertEqual(rebuilt.data, octets) |
| 82 | + self.assertEqual(rebuilt.alias, f'HIPv{version}') |
| 83 | + self.assertEqual(rebuilt.info.version, version) |
| 84 | + |
| 85 | + |
| 86 | +if __name__ == '__main__': |
| 87 | + unittest.main() |
0 commit comments