async def _unittest_slow_plug_and_play_allocatee( compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], caplog: typing.Any ) -> None: from pyuavcan.presentation import Presentation from pyuavcan.application.plug_and_play import Allocatee, NodeIDAllocationData_2, ID assert compiled asyncio.get_running_loop().slow_callback_duration = 5.0 peers: typing.Set[MockMedia] = set() pres_client = Presentation(CANTransport(MockMedia(peers, 64, 1), None)) pres_server = Presentation(CANTransport(MockMedia(peers, 64, 1), 123)) allocatee = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) pub = pres_server.make_publisher_with_fixed_subject_id(NodeIDAllocationData_2) await pub.publish(NodeIDAllocationData_2(ID(10), unique_id=_uid("aabbccddeeff00112233445566778899"))) # Mismatch. await asyncio.sleep(1.0) assert allocatee.get_result() is None with caplog.at_level(logging.CRITICAL, logger=pyuavcan.application.plug_and_play.__name__): # Bad NID. await pub.publish(NodeIDAllocationData_2(ID(999), unique_id=_uid("00112233445566778899aabbccddeeff"))) await asyncio.sleep(1.0) assert allocatee.get_result() is None await pub.publish(NodeIDAllocationData_2(ID(0), unique_id=_uid("00112233445566778899aabbccddeeff"))) # Correct. await asyncio.sleep(1.0) assert allocatee.get_result() == 0 allocatee.close() pub.close() pres_client.close() pres_server.close() await asyncio.sleep(1.0) # Let the tasks finalize properly.
async def _unittest_slow_diagnostic(generated_packages: typing.List[ pyuavcan.dsdl.GeneratedPackageInfo], caplog: typing.Any) -> None: from pyuavcan.application import diagnostic from uavcan.time import SynchronizedTimestamp_1_0 assert generated_packages pres = Presentation(LoopbackTransport(2222)) pub = pres.make_publisher_with_fixed_subject_id(diagnostic.Record) diag = diagnostic.DiagnosticSubscriber(pres) diag.start() caplog.clear() await pub.publish( diagnostic.Record( timestamp=SynchronizedTimestamp_1_0(123456789), severity=diagnostic.Severity(diagnostic.Severity.INFO), text="Hello world!", )) await asyncio.sleep(1.0) print("Captured log records:") for lr in caplog.records: print(" ", lr) assert isinstance(lr, logging.LogRecord) pat = r"uavcan\.diagnostic\.Record: node=2222 severity=2 ts_sync=123\.456789 ts_local=\S+:\nHello world!" if lr.levelno == logging.INFO and re.match(pat, lr.message): break else: assert False, "Expected log message not captured" diag.close() pub.close() pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.
async def _unittest_spoofer(caplog: pytest.LogCaptureFixture) -> None: dcs_pres = Presentation(LoopbackTransport(1234)) dcs_pub_spoof = dcs_pres.make_publisher(Spoof, 1) spoofer = Spoofer(dcs_pres.make_subscriber(Spoof, 1)) # No target transports configured -- spoofing will do nothing except incrementing the transfer-ID counter. assert await dcs_pub_spoof.publish( Spoof( timeout=uavcan.si.unit.duration.Scalar_1_0(1.0), priority=org_uavcan_yukon.io.transfer.Priority_1_0(3), session=org_uavcan_yukon.io.transfer.Session_0_1( subject=org_uavcan_yukon.io.transfer.SubjectSession_0_1( subject_id=uavcan.node.port.SubjectID_1_0(6666), source=[uavcan.node.ID_1_0(1234)])), transfer_id=[], iface_id=[], payload=org_uavcan_yukon.io.transfer.Payload_1_0(b"Hello world!"), )) await asyncio.sleep(0.5) # Validate the transfer-ID map. assert len(spoofer._transfer_id_map) == 1 assert list(spoofer._transfer_id_map.keys())[0].source_node_id == 1234 assert list(spoofer._transfer_id_map.values())[0]._value == 1 # Configure transports. cap_a: typing.List[pyuavcan.transport.Capture] = [] cap_b: typing.List[pyuavcan.transport.Capture] = [] target_tr_a = LoopbackTransport(None) target_tr_b = LoopbackTransport(None) target_tr_a.begin_capture(cap_a.append) target_tr_b.begin_capture(cap_b.append) spoofer.add_iface(111, target_tr_a) spoofer.add_iface(222, target_tr_b) # Spoof on both, successfully. spoof = Spoof( timeout=uavcan.si.unit.duration.Scalar_1_0(1.0), priority=org_uavcan_yukon.io.transfer.Priority_1_0(3), session=org_uavcan_yukon.io.transfer.Session_0_1( subject=org_uavcan_yukon.io.transfer.SubjectSession_0_1( subject_id=uavcan.node.port.SubjectID_1_0(6666), source=[uavcan.node.ID_1_0(1234)])), transfer_id=[9876543210], # This transfer will not touch the TID map. iface_id=[], # All ifaces. payload=org_uavcan_yukon.io.transfer.Payload_1_0(b"abcd"), ) assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(0.5) assert len(cap_a) == len(cap_b) (cap, ) = cap_a cap_a.clear() cap_b.clear() assert isinstance(cap, LoopbackCapture) assert cap.transfer.metadata.transfer_id == 9876543210 assert len(spoofer._transfer_id_map) == 1 # New entry was not created. assert spoofer.status == { 111: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), 222: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), } # Make one time out, the other raise an error, third one is closed. target_tr_a.spoof_result = False target_tr_b.spoof_result = RuntimeError("Intended exception") target_tr_c = LoopbackTransport(None) target_tr_c.close() spoofer.add_iface(0, target_tr_c) with caplog.at_level(logging.CRITICAL): assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(2.0) assert not cap_a assert not cap_b old_status = spoofer.status assert old_status == { 0: IfaceStatus(num_bytes=0, num_errors=1, num_timeouts=0, num_transfers=0, backlog=0, backlog_peak=0), 111: IfaceStatus(num_bytes=4, num_errors=0, num_timeouts=1, num_transfers=1, backlog=0, backlog_peak=0), 222: IfaceStatus(num_bytes=4, num_errors=1, num_timeouts=0, num_transfers=1, backlog=0, backlog_peak=0), } # Force only one iface out of three. Check that the backlog counter goes up. spoof.iface_id = [0] assert await dcs_pub_spoof.publish(spoof) assert await dcs_pub_spoof.publish(spoof) assert await dcs_pub_spoof.publish(spoof) await asyncio.sleep(2.0) assert spoofer.status[0].backlog > 0 assert spoofer.status[0].backlog_peak > 0 assert spoofer.status[111] == old_status[111] assert spoofer.status[222] == old_status[222] # Finalize. spoofer.close() target_tr_a.close() target_tr_b.close() target_tr_c.close() dcs_pres.close() await asyncio.sleep(1.0)
async def _unittest_slow_node( compiled: typing.List[pyuavcan.dsdl.GeneratedPackageInfo]) -> None: from pyuavcan.application import make_node, make_registry import uavcan.primitive from uavcan.node import Version_1_0, Heartbeat_1_0, GetInfo_1_0, Mode_1_0, Health_1_0 asyncio.get_running_loop().slow_callback_duration = 3.0 assert compiled remote_pres = Presentation(UDPTransport("127.1.1.1")) remote_hb_sub = remote_pres.make_subscriber_with_fixed_subject_id( Heartbeat_1_0) remote_info_cln = remote_pres.make_client_with_fixed_service_id( GetInfo_1_0, 258) trans = RedundantTransport() try: info = GetInfo_1_0.Response( protocol_version=Version_1_0( *pyuavcan.UAVCAN_SPECIFICATION_VERSION), software_version=Version_1_0(*pyuavcan.__version_info__[:2]), name="org.uavcan.pyuavcan.test.node", ) node = make_node(info, make_registry(None, typing.cast(Dict[str, bytes], {})), transport=trans) print("node:", node) assert node.presentation.transport is trans node.start() node.start() # Idempotency # Check port instantiation API for non-fixed-port-ID types. assert "uavcan.pub.optional.id" not in node.registry # Nothing yet. with pytest.raises(KeyError, match=r".*uavcan\.pub\.optional\.id.*"): node.make_publisher(uavcan.primitive.Empty_1_0, "optional") assert 0xFFFF == int( node.registry["uavcan.pub.optional.id"]) # Created automatically! with pytest.raises(TypeError): node.make_publisher(uavcan.primitive.Empty_1_0) # Same but for fixed port-ID types. assert "uavcan.pub.atypical_heartbeat.id" not in node.registry # Nothing yet. port = node.make_publisher(uavcan.node.Heartbeat_1_0, "atypical_heartbeat") assert port.port_id == pyuavcan.dsdl.get_model( uavcan.node.Heartbeat_1_0).fixed_port_id port.close() assert 0xFFFF == int(node.registry["uavcan.pub.atypical_heartbeat.id"] ) # Created automatically! node.registry[ "uavcan.pub.atypical_heartbeat.id"] = 111 # Override the default. port = node.make_publisher(uavcan.node.Heartbeat_1_0, "atypical_heartbeat") assert port.port_id == 111 port.close() node.heartbeat_publisher.priority = pyuavcan.transport.Priority.FAST node.heartbeat_publisher.period = 0.5 node.heartbeat_publisher.mode = Mode_1_0.MAINTENANCE # type: ignore node.heartbeat_publisher.health = Health_1_0.ADVISORY # type: ignore node.heartbeat_publisher.vendor_specific_status_code = 93 with pytest.raises(ValueError): node.heartbeat_publisher.period = 99.0 with pytest.raises(ValueError): node.heartbeat_publisher.vendor_specific_status_code = -299 assert node.heartbeat_publisher.priority == pyuavcan.transport.Priority.FAST assert node.heartbeat_publisher.period == pytest.approx(0.5) assert node.heartbeat_publisher.mode == Mode_1_0.MAINTENANCE assert node.heartbeat_publisher.health == Health_1_0.ADVISORY assert node.heartbeat_publisher.vendor_specific_status_code == 93 assert None is await remote_hb_sub.receive_for(2.0) assert trans.local_node_id is None trans.attach_inferior(UDPTransport("127.1.1.2")) assert trans.local_node_id == 258 for _ in range(2): hb_transfer = await remote_hb_sub.receive_for(2.0) assert hb_transfer is not None hb, transfer = hb_transfer assert transfer.source_node_id == 258 assert transfer.priority == pyuavcan.transport.Priority.FAST assert 1 <= hb.uptime <= 9 assert hb.mode.value == Mode_1_0.MAINTENANCE assert hb.health.value == Health_1_0.ADVISORY assert hb.vendor_specific_status_code == 93 info_transfer = await remote_info_cln.call(GetInfo_1_0.Request()) assert info_transfer is not None resp, transfer = info_transfer assert transfer.source_node_id == 258 assert isinstance(resp, GetInfo_1_0.Response) assert resp.name.tobytes().decode() == "org.uavcan.pyuavcan.test.node" assert resp.protocol_version.major == pyuavcan.UAVCAN_SPECIFICATION_VERSION[ 0] assert resp.software_version.major == pyuavcan.__version_info__[0] trans.detach_inferior(trans.inferiors[0]) assert trans.local_node_id is None assert None is await remote_hb_sub.receive_for(2.0) node.close() node.close() # Idempotency finally: trans.close() remote_pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.
async def _unittest_slow_plug_and_play_centralized( generated_packages: typing.List[pyuavcan.dsdl.GeneratedPackageInfo], mtu: int) -> None: from pyuavcan.application.plug_and_play import CentralizedAllocator, Allocatee assert generated_packages asyncio.get_running_loop().slow_callback_duration = 5.0 peers: typing.Set[MockMedia] = set() pres_client = Presentation(CANTransport(MockMedia(peers, mtu, 1), None)) pres_server = Presentation(CANTransport(MockMedia(peers, mtu, 1), 123)) cln_a = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) assert cln_a.get_result() is None cln_a.start() await asyncio.sleep(2.0) assert cln_a.get_result() is None # Nope, no response. try: _TABLE.unlink() except FileNotFoundError: pass with pytest.raises(ValueError, match=".*anonymous.*"): CentralizedAllocator(pres_client, _uid("deadbeefdeadbeefdeadbeefdeadbeef"), _TABLE) with pytest.raises(ValueError): CentralizedAllocator(pres_client, b"123", _TABLE) allocator = CentralizedAllocator(pres_server, _uid("deadbeefdeadbeefdeadbeefdeadbeef"), _TABLE) allocator.start() allocator.register_node(41, None) allocator.register_node( 41, _uid("00000000000000000000000000000001")) # Overwrites allocator.register_node(42, _uid("00000000000000000000000000000002")) allocator.register_node(42, None) # Does not overwrite allocator.register_node(43, _uid("0000000000000000000000000000000F")) allocator.register_node( 43, _uid("00000000000000000000000000000003")) # Overwrites allocator.register_node(43, None) # Does not overwrite use_v2 = mtu > cln_a._MTU_THRESHOLD # pylint: disable=protected-access await asyncio.sleep(2.0) assert cln_a.get_result() == (44 if use_v2 else 125) # Another request. cln_b = Allocatee(pres_client, _uid("aabbccddeeff00112233445566778899")) assert cln_b.get_result() is None cln_b.start() await asyncio.sleep(2.0) assert cln_b.get_result() == (125 if use_v2 else 124) # Re-request A and make sure we get the same response. cln_a = Allocatee(pres_client, _uid("00112233445566778899aabbccddeeff"), 42) assert cln_a.get_result() is None cln_a.start() await asyncio.sleep(2.0) assert cln_a.get_result() == (44 if use_v2 else 125) # C should be served from the manually added entries above. cln_c = Allocatee(pres_client, _uid("00000000000000000000000000000003")) assert cln_c.get_result() is None cln_c.start() await asyncio.sleep(2.0) assert cln_c.get_result() == 43 # This one requires no allocation because the transport is not anonymous. cln_d = Allocatee(pres_server, _uid("00000000000000000000000000000009"), 100) assert cln_d.get_result() == 123 cln_d.start() await asyncio.sleep(2.0) assert cln_d.get_result() == 123 # No change. # More test coverage needed. # Finalization. cln_a.close() cln_b.close() cln_c.close() cln_d.close() allocator.close() pres_client.close() pres_server.close() await asyncio.sleep(1.0) # Let the tasks finalize properly.
async def _unittest_slow_node( generated_packages: typing.List[pyuavcan.dsdl.GeneratedPackageInfo] ) -> None: from pyuavcan.application import Node from uavcan.node import Version_1_0, Heartbeat_1_0, GetInfo_1_0, Mode_1_0, Health_1_0 asyncio.get_running_loop().slow_callback_duration = 3.0 assert generated_packages remote_pres = Presentation(UDPTransport("127.1.1.1")) remote_hb_sub = remote_pres.make_subscriber_with_fixed_subject_id( Heartbeat_1_0) remote_info_cln = remote_pres.make_client_with_fixed_service_id( GetInfo_1_0, 258) trans = RedundantTransport() pres = Presentation(trans) try: info = GetInfo_1_0.Response( protocol_version=Version_1_0( *pyuavcan.UAVCAN_SPECIFICATION_VERSION), software_version=Version_1_0(*pyuavcan.__version_info__[:2]), name="org.uavcan.pyuavcan.test.node", ) node = Node(pres, info, with_diagnostic_subscriber=True) print("node:", node) assert node.presentation is pres node.start() node.start() # Idempotency node.heartbeat_publisher.priority = pyuavcan.transport.Priority.FAST node.heartbeat_publisher.period = 0.5 node.heartbeat_publisher.mode = Mode_1_0.MAINTENANCE # type: ignore node.heartbeat_publisher.health = Health_1_0.ADVISORY # type: ignore node.heartbeat_publisher.vendor_specific_status_code = 93 with pytest.raises(ValueError): node.heartbeat_publisher.period = 99.0 with pytest.raises(ValueError): node.heartbeat_publisher.vendor_specific_status_code = -299 assert node.heartbeat_publisher.priority == pyuavcan.transport.Priority.FAST assert node.heartbeat_publisher.period == pytest.approx(0.5) assert node.heartbeat_publisher.mode == Mode_1_0.MAINTENANCE assert node.heartbeat_publisher.health == Health_1_0.ADVISORY assert node.heartbeat_publisher.vendor_specific_status_code == 93 assert None is await remote_hb_sub.receive_for(2.0) assert trans.local_node_id is None trans.attach_inferior(UDPTransport("127.1.1.2")) assert trans.local_node_id == 258 for _ in range(2): hb_transfer = await remote_hb_sub.receive_for(2.0) assert hb_transfer is not None hb, transfer = hb_transfer assert transfer.source_node_id == 258 assert transfer.priority == pyuavcan.transport.Priority.FAST assert 1 <= hb.uptime <= 9 assert hb.mode.value == Mode_1_0.MAINTENANCE assert hb.health.value == Health_1_0.ADVISORY assert hb.vendor_specific_status_code == 93 info_transfer = await remote_info_cln.call(GetInfo_1_0.Request()) assert info_transfer is not None resp, transfer = info_transfer assert transfer.source_node_id == 258 assert isinstance(resp, GetInfo_1_0.Response) assert resp.name.tobytes().decode() == "org.uavcan.pyuavcan.test.node" assert resp.protocol_version.major == pyuavcan.UAVCAN_SPECIFICATION_VERSION[ 0] assert resp.software_version.major == pyuavcan.__version_info__[0] trans.detach_inferior(trans.inferiors[0]) assert trans.local_node_id is None assert None is await remote_hb_sub.receive_for(2.0) node.close() node.close() # Idempotency finally: pres.close() remote_pres.close() await asyncio.sleep(1.0) # Let the background tasks terminate.