1212import sys
1313import textwrap
1414from pathlib import Path
15- from typing import Any , Dict , Tuple
1615from unittest .mock import patch
1716
1817import pytest
1918
19+ from meshtastic .protobuf import (
20+ atak_pb2 ,
21+ config_pb2 ,
22+ mesh_pb2 ,
23+ nanopb_pb2 ,
24+ telemetry_pb2 ,
25+ )
26+
2027# ---------------------------------------------------------------------------
2128# Load bin/inject_nanopb_options.py as a module without adding it to the
2229# package. __main__ guard means no side-effects on import.
@@ -55,26 +62,31 @@ def _load_inject_module():
5562
5663@pytest .mark .unit
5764def test_parse_value_integer ():
65+ """parse_value converts a decimal string to int."""
5866 assert parse_value ("40" ) == 40
5967
6068
6169@pytest .mark .unit
6270def test_parse_value_negative_integer ():
71+ """parse_value handles negative integer strings."""
6372 assert parse_value ("-1" ) == - 1
6473
6574
6675@pytest .mark .unit
6776def test_parse_value_true ():
77+ """parse_value converts 'true' to Python True."""
6878 assert parse_value ("true" ) is True
6979
7080
7181@pytest .mark .unit
7282def test_parse_value_false ():
83+ """parse_value converts 'false' to Python False."""
7384 assert parse_value ("false" ) is False
7485
7586
7687@pytest .mark .unit
7788def test_parse_value_string ():
89+ """parse_value returns non-numeric, non-boolean strings as-is."""
7890 assert parse_value ("IS_8" ) == "IS_8"
7991
8092
@@ -113,7 +125,7 @@ def test_parse_specific(tmp_path):
113125def test_parse_multilevel (tmp_path ):
114126 """Three-part pattern (Route.Link.uid) produces a 3-tuple key."""
115127 f = _write_options (tmp_path , "*Route.Link.uid max_size:48\n " )
116- specific , wildcard = parse_options_file (f )
128+ specific , _ = parse_options_file (f )
117129 assert ("Route" , "Link" , "uid" ) in specific
118130 assert specific [("Route" , "Link" , "uid" )] == {"max_size" : 48 }
119131
@@ -181,12 +193,14 @@ def test_parse_int_and_bool_values(tmp_path):
181193
182194@pytest .mark .unit
183195def test_message_path_matches_simple ():
196+ """A single-element path matches the current message on the stack."""
184197 stack = [("message" , "User" )]
185198 assert message_path_matches (stack , ("User" ,))
186199
187200
188201@pytest .mark .unit
189202def test_message_path_matches_nested ():
203+ """Both a 1-element and 2-element path match correctly against a nested stack."""
190204 stack = [("message" , "Config" ), ("message" , "DeviceConfig" )]
191205 assert message_path_matches (stack , ("DeviceConfig" ,))
192206 assert message_path_matches (stack , ("Config" , "DeviceConfig" ))
@@ -201,6 +215,7 @@ def test_message_path_matches_with_oneof_in_stack():
201215
202216@pytest .mark .unit
203217def test_message_path_no_match ():
218+ """A path with the wrong message name does not match."""
204219 stack = [("message" , "User" )]
205220 assert not message_path_matches (stack , ("Route" ,))
206221
@@ -220,11 +235,13 @@ def test_message_path_multilevel_partial_match():
220235
221236@pytest .mark .unit
222237def test_format_max_size ():
238+ """max_size is rendered as an integer literal."""
223239 assert format_nanopb_opts ({"max_size" : 40 }) == "(nanopb).max_size = 40"
224240
225241
226242@pytest .mark .unit
227243def test_format_int_size_as_enum ():
244+ """int_size numeric values are rendered as IS_8/IS_16/IS_32/IS_64 enum names."""
228245 assert format_nanopb_opts ({"int_size" : 8 }) == "(nanopb).int_size = IS_8"
229246 assert format_nanopb_opts ({"int_size" : 16 }) == "(nanopb).int_size = IS_16"
230247 assert format_nanopb_opts ({"int_size" : 32 }) == "(nanopb).int_size = IS_32"
@@ -233,11 +250,13 @@ def test_format_int_size_as_enum():
233250
234251@pytest .mark .unit
235252def test_format_bool_true ():
253+ """True is rendered as the proto literal 'true'."""
236254 assert format_nanopb_opts ({"fixed_length" : True }) == "(nanopb).fixed_length = true"
237255
238256
239257@pytest .mark .unit
240258def test_format_bool_false ():
259+ """False is rendered as the proto literal 'false'."""
241260 assert format_nanopb_opts ({"fixed_length" : False }) == "(nanopb).fixed_length = false"
242261
243262
@@ -293,6 +312,7 @@ def test_inject_merges_with_existing_options():
293312
294313@pytest .mark .unit
295314def test_inject_int_size_uses_enum_name ():
315+ """int_size values are written as IS_N enum names, not raw integers."""
296316 proto = """\
297317 syntax = "proto3";
298318 import "meshtastic/protobuf/mesh.proto";
@@ -335,8 +355,6 @@ def test_inject_specific_not_leaking_to_other_messages():
335355 }
336356 """
337357 result = _inject (proto , specific = {("User" , "id" ): {"max_size" : 16 }})
338- lines = result .splitlines ()
339- user_line = next (l for l in lines if "User" not in l and "id = 1" in l and "Other" not in l .split ("message" )[0 ] if "message" not in l )
340358 # Easier: count annotations — should be exactly one
341359 assert result .count ("(nanopb).max_size = 16" ) == 1
342360
@@ -400,6 +418,7 @@ def test_inject_optional_qualifier_preserved():
400418
401419@pytest .mark .unit
402420def test_inject_repeated_qualifier_preserved ():
421+ """The 'repeated' qualifier is kept when a field gets an annotation."""
403422 proto = """\
404423 syntax = "proto3";
405424 import "meshtastic/protobuf/mesh.proto";
@@ -413,6 +432,7 @@ def test_inject_repeated_qualifier_preserved():
413432
414433@pytest .mark .unit
415434def test_inject_multiple_options_on_one_field ():
435+ """Multiple options from the same pattern are all injected on one field."""
416436 proto = """\
417437 syntax = "proto3";
418438 import "meshtastic/protobuf/mesh.proto";
@@ -510,14 +530,6 @@ def test_inject_noop_when_no_options():
510530# embedded in the serialized descriptors.
511531# ===========================================================================
512532
513- from meshtastic .protobuf import ( # noqa: E402 (after local helpers)
514- atak_pb2 ,
515- config_pb2 ,
516- mesh_pb2 ,
517- nanopb_pb2 ,
518- telemetry_pb2 ,
519- )
520-
521533
522534def _field_opts (descriptor , * path ):
523535 """Walk a descriptor by field/nested-type path and return its nanopb opts.
@@ -534,12 +546,14 @@ def _field_opts(descriptor, *path):
534546
535547@pytest .mark .unit
536548def test_descriptor_user_long_name ():
549+ """User.long_name has max_size = 40 from mesh.options."""
537550 opts = _field_opts (mesh_pb2 .DESCRIPTOR .message_types_by_name ["User" ], "long_name" )
538551 assert opts .max_size == 40
539552
540553
541554@pytest .mark .unit
542555def test_descriptor_user_short_name ():
556+ """User.short_name has max_size = 5 from mesh.options."""
543557 opts = _field_opts (mesh_pb2 .DESCRIPTOR .message_types_by_name ["User" ], "short_name" )
544558 assert opts .max_size == 5
545559
@@ -554,12 +568,14 @@ def test_descriptor_wildcard_macaddr():
554568
555569@pytest .mark .unit
556570def test_descriptor_meshpacket_hop_limit ():
571+ """MeshPacket.hop_limit has int_size = IS_8 from mesh.options."""
557572 opts = _field_opts (mesh_pb2 .DESCRIPTOR .message_types_by_name ["MeshPacket" ], "hop_limit" )
558573 assert opts .int_size == nanopb_pb2 .IS_8
559574
560575
561576@pytest .mark .unit
562577def test_descriptor_routediscovery_snr_towards ():
578+ """RouteDiscovery.snr_towards has max_count = 8 and int_size = IS_8 from mesh.options."""
563579 opts = _field_opts (
564580 mesh_pb2 .DESCRIPTOR .message_types_by_name ["RouteDiscovery" ], "snr_towards"
565581 )
@@ -569,6 +585,7 @@ def test_descriptor_routediscovery_snr_towards():
569585
570586@pytest .mark .unit
571587def test_descriptor_data_payload ():
588+ """Data.payload has max_size = 233 from mesh.options."""
572589 opts = _field_opts (mesh_pb2 .DESCRIPTOR .message_types_by_name ["Data" ], "payload" )
573590 assert opts .max_size == 233
574591
@@ -600,6 +617,7 @@ def test_descriptor_multilevel_nested_route_link_uid():
600617
601618@pytest .mark .unit
602619def test_descriptor_telemetry_environment_one_wire_temperature ():
620+ """EnvironmentMetrics.one_wire_temperature has max_count = 8 from telemetry.options."""
603621 env = telemetry_pb2 .DESCRIPTOR .message_types_by_name ["EnvironmentMetrics" ]
604622 opts = _field_opts (env , "one_wire_temperature" )
605623 assert opts .max_count == 8
0 commit comments