Repository navigation
fix(const): answer IANA's transport-less port spans with the registry's transport (#2048) #2052
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """Every in-range port answers with the asking registry's own transport. | ||
|
|
||
| GitHub issue #2048. IANA assigned port 8011 on UDP (``bier-echo``) and marked it | ||
| Reserved on TCP, and its CSV then dropped 8011 from the unassigned span | ||
| ``8011``-``8014`` while listing no DCCP or SCTP entry for it, so DCCP and SCTP | ||
| answer ``get``'s ``unknown`` fallback, which the owner kept. The spans in | ||
| ``AppType._missing_`` that name no transport answer with the asking registry's | ||
| transport rather than ``TransportProtocol.undefined``, as the fallback already | ||
| did. Lookups still mint nothing (#860), and a port outside the range still | ||
| raises (#758). | ||
|
|
||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import unittest | ||
|
|
||
| #: Test tier (#1958): pure-Python lookups over the generated AppType registries, | ||
| #: which behave the same on every interpreter. | ||
| __tier__ = 'LATEST' | ||
|
|
||
| #: ``(port, {registry: (svc)})`` for each registry, in TCP, UDP, SCTP, DCCP order. | ||
| EXPECTED = ( | ||
| # IANA's rows on TCP and UDP only; nothing on SCTP or DCCP. | ||
| (8011, ('reserved', 'bier-echo', 'unknown', 'unknown')), | ||
| (8009, ('nvme-disc', 'reserved', 'unknown', 'unknown')), | ||
| # An unassigned span naming no transport. | ||
| (8012, ('unassigned', 'unassigned', 'unassigned', 'unassigned')), | ||
| # A reserved span naming no transport. | ||
| (226, ('reserved', 'reserved', 'reserved', 'reserved')), | ||
| # Spans naming TCP and UDP (#760); nothing on SCTP or DCCP. | ||
| (6010, ('x11', 'x11', 'unknown', 'unknown')), | ||
| # No row and no span on any transport. | ||
| (54321, ('unknown', 'unknown', 'unknown', 'unknown')), | ||
| ) | ||
|
|
||
|
|
||
| def _registries() -> 'tuple[type, ...]': | ||
| from pcapkit.const.reg.apptype import DCCP, SCTP, TCP, UDP | ||
|
|
||
| return TCP, UDP, SCTP, DCCP | ||
|
|
||
|
|
||
| def _sizes(registry: 'type') -> 'tuple[int, int, int]': | ||
| """What a minting lookup would grow on ``registry``.""" | ||
| return (len(registry.__members__), # type: ignore[attr-defined] | ||
| len(registry._value2member_map_), # type: ignore[attr-defined] | ||
| len(list(registry.__registry__.items()))) # type: ignore[attr-defined] | ||
|
|
||
|
|
||
| class AppTypeUnassigned2048Tests(unittest.TestCase): | ||
| """Every in-range port answers with the asking registry's transport.""" | ||
|
|
||
| def test_each_registry_answers_with_its_own_transport(self) -> None: | ||
| from pcapkit.const.reg.apptype import AppType | ||
|
|
||
| for port, answers in EXPECTED: | ||
| for registry, svc in zip(_registries(), answers): | ||
| with self.subTest(registry=registry.__name__, port=port): | ||
| member = registry.get(port) | ||
| self.assertIsInstance(member, registry) | ||
| self.assertEqual(member.svc, svc) | ||
| self.assertEqual(member.port, port) | ||
| self.assertIs(member.proto, registry.__transport__) | ||
| self.assertEqual(AppType.get(port, proto=registry.__transport__), member) | ||
|
|
||
| def test_the_issues_own_port(self) -> None: | ||
| from pcapkit.const.reg.apptype import DCCP, SCTP, TCP, UDP | ||
|
|
||
| self.assertEqual(repr(TCP.get(8011)), '<TCP.reserved: 8011 [tcp]>') | ||
| self.assertEqual(repr(UDP.get(8011)), '<UDP.bier-echo: 8011 [udp]>') | ||
| self.assertEqual(repr(SCTP.get(8011)), '<SCTP.unknown: 8011 [sctp]>') | ||
| self.assertEqual(repr(DCCP.get(8011)), '<DCCP.unknown: 8011 [dccp]>') | ||
|
|
||
| def test_lookups_mint_nothing(self) -> None: | ||
| """#860: span and fallback members alike stay unregistered.""" | ||
| for registry in _registries(): | ||
| before = _sizes(registry) | ||
| for port, _ in EXPECTED: | ||
| registry.get(port) | ||
| registry.get_all(port) | ||
| with self.subTest(registry=registry.__name__): | ||
| self.assertEqual(_sizes(registry), before) | ||
| self.assertNotIn('unassigned', registry.__members__) # type: ignore[attr-defined] | ||
| self.assertNotIn('unknown', registry.__members__) # type: ignore[attr-defined] | ||
|
|
||
| def test_out_of_range_still_raises(self) -> None: | ||
| """#758: the rule covers ``0``-``65535`` only.""" | ||
| from pcapkit.const.reg.apptype import AppType | ||
|
|
||
| for registry in _registries(): | ||
| for port in (-1, 65536): | ||
| with self.subTest(registry=registry.__name__, port=port): | ||
| with self.assertRaises(ValueError): | ||
| registry.get(port) | ||
| with self.assertRaises(ValueError): | ||
| AppType.get(port, proto=registry.__transport__) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
97 changes: 97 additions & 0 deletions
97
tests/vendor/test_vendor_apptype_get_template_agreement_2048_unit.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """The AppType generator and its committed base module agree on #2048's rule. | ||
|
|
||
| GitHub issue #2048. Every in-range port answers with the asking registry's own | ||
| transport. That lives in two places: ``get``'s ``unknown`` fallback for a port | ||
| ``_missing_`` holds no span for, and the span branches the generator renders | ||
| into ``_missing_``. Regenerating :mod:`pcapkit.const.reg.apptype.apptype` needs | ||
| IANA's live CSV, so an edit to the template in | ||
| :mod:`pcapkit.vendor.reg.apptype.apptype` is made to the committed module by | ||
| hand. These render the template offline and check the two agree. | ||
|
|
||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import importlib.util | ||
| import pathlib | ||
| import unittest | ||
|
|
||
| #: Test tier (#1958): checks the AppType generator's template against the | ||
| #: committed module, and imports the crawlers, whose extra is on ``latest`` alone. | ||
| __tier__ = 'LATEST' | ||
|
|
||
| #: Repository root, i.e. the grandparent of the directory holding this file. | ||
| ROOT = pathlib.Path(__file__).resolve().parents[2] | ||
|
|
||
| #: Every distribution importing :mod:`pcapkit.vendor` needs -- see | ||
| #: :mod:`tests.vendor.test_ipx_packet_unit` for why all three are checked. | ||
| VENDOR_DEPS = ('requests', 'bs4', 'html5lib') | ||
|
|
||
| #: Whether the crawlers are importable at all. | ||
| HAS_VENDOR_DEPS = all(importlib.util.find_spec(name) is not None for name in VENDOR_DEPS) | ||
|
|
||
| #: Where ``get`` starts and where the method after it starts, in both texts. | ||
| GET_START = " @classmethod\n def get(cls, key: 'int', *," | ||
| GET_END = ' @classmethod\n def get_all(' | ||
|
|
||
|
|
||
| def _get_method(text: 'str') -> 'str': | ||
| """The source text of ``get`` within a rendering of the base module.""" | ||
| start = text.index(GET_START) | ||
| return text[start:text.index(GET_END, start)] | ||
|
|
||
|
|
||
| @unittest.skipUnless(HAS_VENDOR_DEPS, f'vendor extra not installed ({", ".join(VENDOR_DEPS)})') | ||
| class AppTypeGetTemplateAgreement2048Tests(unittest.TestCase): | ||
| """The template's ``get`` renders to the committed one.""" | ||
|
|
||
| def test_rendered_get_matches_committed_get(self) -> None: | ||
| from pcapkit.vendor.reg.apptype.apptype import BASE, AppType | ||
|
|
||
| rendered = BASE('AppType', AppType.__doc__, AppType.FLAG, '', '', | ||
| 'pcapkit.vendor.reg.apptype.apptype') | ||
| committed = (ROOT / 'pcapkit' / 'const' / 'reg' / 'apptype' / 'apptype.py').read_text( | ||
| encoding='utf-8') | ||
|
|
||
| self.assertEqual(_get_method(rendered), _get_method(committed)) | ||
|
|
||
| def test_rendered_get_falls_back_to_unknown_on_the_registrys_transport(self) -> None: | ||
| from pcapkit.vendor.reg.apptype.apptype import BASE, AppType | ||
|
|
||
| method = _get_method(BASE('AppType', AppType.__doc__, AppType.FLAG, '', '', | ||
| 'pcapkit.vendor.reg.apptype.apptype')) | ||
|
|
||
| self.assertIn("_unregistered_member(key, 'unknown', owner.__transport__)", method) | ||
| self.assertNotIn("'unassigned'", method) | ||
|
|
||
| def test_a_span_naming_no_transport_answers_with_the_registrys(self) -> None: | ||
| """A span IANA lists for one transport keeps its claim and its transport.""" | ||
| from pcapkit.vendor.reg.apptype.apptype import AppType | ||
|
|
||
| _, miss = AppType.__new__(AppType).records([ | ||
| 'Service Name,Port Number,Transport Protocol,Description,Assignee,Contact,' | ||
| 'Registration Date,Modification Date,Reference,Service Code,' | ||
| 'Unauthorized Use Reported,Assignment Notes', | ||
| ',8012-8014,,Unassigned,,,,,,,,', | ||
| 'x11,6000-6063,tcp,X Window System,,,,,,,,', | ||
| ]) | ||
|
|
||
| self.assertEqual(miss[0], 'if 8012 <= value <= 8014:') | ||
| self.assertEqual(miss[2], " return cls._unregistered_member(value, 'unassigned', " | ||
| 'cls.__transport__)') | ||
| self.assertEqual(miss[3], 'if 6000 <= value <= 6063 and cls.__transport__ is ' | ||
| 'TransportProtocol.tcp:') | ||
| self.assertEqual(miss[5], " return cls._unregistered_member(value, 'x11', " | ||
| 'TransportProtocol.tcp)') | ||
|
|
||
| def test_committed_spans_naming_no_transport_answer_with_the_registrys(self) -> None: | ||
| source = (ROOT / 'pcapkit' / 'const' / 'reg' / 'apptype' / 'apptype.py').read_text( | ||
| encoding='utf-8') | ||
| method = source[source.index(' def _missing_(cls, value'):] | ||
|
|
||
| self.assertNotIn('TransportProtocol.undefined)', method) | ||
| self.assertIn("_unregistered_member(value, 'unassigned', cls.__transport__)", method) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.