TEST(PanicListener, Basic) { InterlinkedTestNodesWithSysClock nodes; uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<uavcan::protocol::Panic> _reg1; uavcan::PanicListener<PanicHandler::Binder> pl(nodes.a); uavcan::PanicBroadcaster pbr(nodes.b); PanicHandler handler; ASSERT_LE(0, pl.start(handler.bind())); pbr.panic("PANIC!!!"); ASSERT_TRUE(handler.msg == uavcan::protocol::Panic()); // One message published, panic is not registered yet pbr.dontPanic(); ASSERT_FALSE(pbr.isPanicking()); std::cout << "Not panicking" << std::endl; nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000)); // Will reset ASSERT_TRUE(handler.msg == uavcan::protocol::Panic()); pbr.panic("PANIC2!!!"); // Message text doesn't matter ASSERT_TRUE(pbr.isPanicking()); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(1000)); ASSERT_STREQ("PANIC2!", handler.msg.reason_text.c_str()); // Registered, only 7 chars preserved }
TEST(Logger, Cpp11Formatting) { InterlinkedTestNodesWithSysClock nodes; uavcan::Logger logger(nodes.a); logger.setLevel(uavcan::protocol::debug::LogLevel::DEBUG); SubscriberWithCollector<uavcan::protocol::debug::LogMessage> log_sub(nodes.b); ASSERT_LE(0, log_sub.start()); ASSERT_LE(0, logger.logWarning("foo", "char='%*', %* is %*", '$', "double", 12.34)); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_TRUE(log_sub.collector.msg.get()); ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::WARNING); ASSERT_EQ(log_sub.collector.msg->source, "foo"); ASSERT_EQ(log_sub.collector.msg->text, "char='$', double is 12.34"); }
TEST(ServiceClient, Rejection) { InterlinkedTestNodesWithSysClock nodes; // Type registration uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator; // Server uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a); ASSERT_EQ(0, server.start(rejectingStringServiceServerCallback)); // Caller typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType; typedef uavcan::ServiceClient<root_ns_a::StringService, typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType; ServiceCallResultHandler<root_ns_a::StringService> handler; ClientType client1(nodes.b); client1.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100)); client1.setCallback(handler.bind()); root_ns_a::StringService::Request request; request.string_request = "Hello world"; ASSERT_LT(0, client1.call(1, request)); ASSERT_EQ(uavcan::NodeID(1), client1.getCallIDByIndex(0).server_node_id); ASSERT_EQ(uavcan::NodeID(), client1.getCallIDByIndex(1).server_node_id); ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); ASSERT_TRUE(client1.hasPendingCalls()); ASSERT_TRUE(client1.hasPendingCallToServer(1)); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(200)); ASSERT_FALSE(client1.hasPendingCalls()); ASSERT_FALSE(client1.hasPendingCallToServer(1)); ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Timed out ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 1, root_ns_a::StringService::Response())); }
TEST(Logger, Basic) { InterlinkedTestNodesWithSysClock nodes; uavcan::Logger logger(nodes.a); ASSERT_EQ(uavcan::protocol::debug::LogLevel::ERROR, logger.getLevel()); LogSink sink; // Will fail - types are not registered uavcan::GlobalDataTypeRegistry::instance().reset(); ASSERT_GT(0, logger.logError("foo", "Error (fail - type is not registered)")); ASSERT_EQ(0, logger.logDebug("foo", "Debug (ignored - low logging level)")); ASSERT_FALSE(logger.getExternalSink()); logger.setExternalSink(&sink); ASSERT_EQ(&sink, logger.getExternalSink()); uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<uavcan::protocol::debug::LogMessage> _reg1; SubscriberWithCollector<uavcan::protocol::debug::LogMessage> log_sub(nodes.b); ASSERT_LE(0, log_sub.start()); // Sink test ASSERT_EQ(0, logger.logDebug("foo", "Debug (ignored due to low logging level)")); ASSERT_TRUE(sink.msgs.empty()); sink.level = uavcan::protocol::debug::LogLevel::DEBUG; ASSERT_EQ(0, logger.logDebug("foo", "Debug (sink only)")); ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::DEBUG, "foo", "Debug (sink only)")); ASSERT_LE(0, logger.logError("foo", "Error")); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_TRUE(log_sub.collector.msg.get()); ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::ERROR); ASSERT_EQ(log_sub.collector.msg->source, "foo"); ASSERT_EQ(log_sub.collector.msg->text, "Error"); logger.setLevel(uavcan::protocol::debug::LogLevel::DEBUG); ASSERT_LE(0, logger.logWarning("foo", "Warning")); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::WARNING); ASSERT_EQ(log_sub.collector.msg->source, "foo"); ASSERT_EQ(log_sub.collector.msg->text, "Warning"); ASSERT_LE(0, logger.logInfo("foo", "Info")); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::INFO); ASSERT_EQ(log_sub.collector.msg->source, "foo"); ASSERT_EQ(log_sub.collector.msg->text, "Info"); ASSERT_LE(0, logger.logDebug("foo", "Debug")); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10)); ASSERT_EQ(log_sub.collector.msg->level.value, uavcan::protocol::debug::LogLevel::DEBUG); ASSERT_EQ(log_sub.collector.msg->source, "foo"); ASSERT_EQ(log_sub.collector.msg->text, "Debug"); ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::ERROR, "foo", "Error")); ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::WARNING, "foo", "Warning")); ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::INFO, "foo", "Info")); ASSERT_TRUE(sink.popMatchByLevelAndText(uavcan::protocol::debug::LogLevel::DEBUG, "foo", "Debug")); }
TEST(DataTypeInfoProvider, Basic) { InterlinkedTestNodesWithSysClock nodes; DataTypeInfoProvider dtip(nodes.a); GlobalDataTypeRegistry::instance().reset(); DefaultDataTypeRegistrator<GetDataTypeInfo> _reg1; DefaultDataTypeRegistrator<ComputeAggregateTypeSignature> _reg2; DefaultDataTypeRegistrator<NodeStatus> _reg3; ASSERT_LE(0, dtip.start()); ServiceClientWithCollector<GetDataTypeInfo> gdti_cln(nodes.b); ServiceClientWithCollector<ComputeAggregateTypeSignature> cats_cln(nodes.b); /* * GetDataTypeInfo request for GetDataTypeInfo */ GetDataTypeInfo::Request gdti_request; gdti_request.id = GetDataTypeInfo::DefaultDataTypeID; gdti_request.kind.value = DataTypeKind::SERVICE; ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(validateDataTypeInfoResponse<GetDataTypeInfo>(gdti_cln.collector.result, GetDataTypeInfo::Response::MASK_KNOWN | GetDataTypeInfo::Response::MASK_SERVING)); ASSERT_EQ(1, gdti_cln.collector.result->getCallID().server_node_id.get()); /* * GetDataTypeInfo request for GetDataTypeInfo by name */ gdti_request = GetDataTypeInfo::Request(); gdti_request.id = 999; // Intentionally wrong gdti_request.kind.value = DataTypeKind::MESSAGE; // Intentionally wrong gdti_request.name = "uavcan.protocol.GetDataTypeInfo"; ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(validateDataTypeInfoResponse<GetDataTypeInfo>(gdti_cln.collector.result, GetDataTypeInfo::Response::MASK_KNOWN | GetDataTypeInfo::Response::MASK_SERVING)); ASSERT_EQ(1, gdti_cln.collector.result->getCallID().server_node_id.get()); /* * GetDataTypeInfo request for NodeStatus - not used yet */ gdti_request = GetDataTypeInfo::Request(); gdti_request.id = NodeStatus::DefaultDataTypeID; gdti_request.kind.value = DataTypeKind::MESSAGE; ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(validateDataTypeInfoResponse<NodeStatus>(gdti_cln.collector.result, GetDataTypeInfo::Response::MASK_KNOWN)); /* * Starting publisher and subscriber for NodeStatus, requesting info again */ uavcan::Publisher<NodeStatus> ns_pub(nodes.a); SubscriberWithCollector<NodeStatus> ns_sub(nodes.a); ASSERT_LE(0, ns_pub.broadcast(NodeStatus())); ASSERT_LE(0, ns_sub.start()); // Request again ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(validateDataTypeInfoResponse<NodeStatus>(gdti_cln.collector.result, GetDataTypeInfo::Response::MASK_KNOWN | GetDataTypeInfo::Response::MASK_PUBLISHING | GetDataTypeInfo::Response::MASK_SUBSCRIBED)); /* * Requesting a non-existent type */ gdti_request = GetDataTypeInfo::Request(); gdti_request.id = ComputeAggregateTypeSignature::DefaultDataTypeID; gdti_request.kind.value = 3; // INVALID VALUE ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(gdti_cln.collector.result.get()); ASSERT_TRUE(gdti_cln.collector.result->isSuccessful()); ASSERT_EQ(1, gdti_cln.collector.result->getCallID().server_node_id.get()); ASSERT_EQ(0, gdti_cln.collector.result->getResponse().mask); ASSERT_TRUE(gdti_cln.collector.result->getResponse().name.empty()); // Empty name ASSERT_EQ(gdti_request.id, gdti_cln.collector.result->getResponse().id); ASSERT_EQ(gdti_request.kind.value, gdti_cln.collector.result->getResponse().kind.value); /* * Requesting a non-existent type by name */ gdti_request = GetDataTypeInfo::Request(); gdti_request.id = 999; // Intentionally wrong gdti_request.kind.value = 3; // Intentionally wrong gdti_request.name = "uavcan.equipment.gnss.Fix"; ASSERT_LE(0, gdti_cln.call(1, gdti_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(gdti_cln.collector.result.get()); ASSERT_TRUE(gdti_cln.collector.result->isSuccessful()); ASSERT_EQ(1, gdti_cln.collector.result->getCallID().server_node_id.get()); ASSERT_EQ(0, gdti_cln.collector.result->getResponse().mask); ASSERT_EQ("uavcan.equipment.gnss.Fix", gdti_cln.collector.result->getResponse().name); ASSERT_EQ(0, gdti_cln.collector.result->getResponse().id); ASSERT_EQ(0, gdti_cln.collector.result->getResponse().kind.value); /* * ComputeAggregateTypeSignature test for messages */ ComputeAggregateTypeSignature::Request cats_request; cats_request.kind.value = DataTypeKind::MESSAGE; cats_request.known_ids.resize(2000); // not 2048 cats_request.known_ids.set(); // Assuming we have all 2000 types ASSERT_LE(0, cats_cln.call(1, cats_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(cats_cln.collector.result.get()); ASSERT_TRUE(cats_cln.collector.result->isSuccessful()); ASSERT_EQ(1, cats_cln.collector.result->getCallID().server_node_id.get()); ASSERT_EQ(NodeStatus::getDataTypeSignature().get(), cats_cln.collector.result->getResponse().aggregate_signature); ASSERT_EQ(2048, cats_cln.collector.result->getResponse().mutually_known_ids.size()); ASSERT_TRUE(cats_cln.collector.result->getResponse().mutually_known_ids[NodeStatus::DefaultDataTypeID]); cats_cln.collector.result->getResponse().mutually_known_ids[NodeStatus::DefaultDataTypeID] = false; ASSERT_FALSE(cats_cln.collector.result->getResponse().mutually_known_ids.any()); /* * ComputeAggregateTypeSignature test for services */ cats_request = ComputeAggregateTypeSignature::Request(); cats_request.kind.value = DataTypeKind::SERVICE; cats_request.known_ids.resize(500); // not 512 cats_request.known_ids.set(); // Assuming we have all 500 types ASSERT_LE(0, cats_cln.call(1, cats_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(cats_cln.collector.result.get()); ASSERT_TRUE(cats_cln.collector.result->isSuccessful()); ASSERT_EQ(1, cats_cln.collector.result->getCallID().server_node_id.get()); ASSERT_EQ(512, cats_cln.collector.result->getResponse().mutually_known_ids.size()); ASSERT_TRUE(cats_cln.collector.result->getResponse().mutually_known_ids[GetDataTypeInfo::DefaultDataTypeID]); ASSERT_TRUE(cats_cln.collector.result->getResponse().mutually_known_ids[ComputeAggregateTypeSignature::DefaultDataTypeID]); cats_cln.collector.result->getResponse().mutually_known_ids[GetDataTypeInfo::DefaultDataTypeID] = false; cats_cln.collector.result->getResponse().mutually_known_ids[ComputeAggregateTypeSignature::DefaultDataTypeID] = false; ASSERT_FALSE(cats_cln.collector.result->getResponse().mutually_known_ids.any()); /* * ComputeAggregateTypeSignature test for a non-existent type */ cats_request.kind.value = 0xFF; // INVALID cats_request.known_ids.set(); // Assuming we have all 2048 types ASSERT_LE(0, cats_cln.call(1, cats_request)); nodes.spinBoth(MonotonicDuration::fromMSec(10)); ASSERT_TRUE(cats_cln.collector.result.get()); ASSERT_TRUE(cats_cln.collector.result->isSuccessful()); ASSERT_EQ(0, cats_cln.collector.result->getResponse().aggregate_signature); ASSERT_FALSE(cats_cln.collector.result->getResponse().mutually_known_ids.any()); }
TEST(TransportStatsProvider, Basic) { InterlinkedTestNodesWithSysClock nodes; uavcan::TransportStatsProvider tsp(nodes.a); uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<uavcan::protocol::GetTransportStats> _reg1; ASSERT_LE(0, tsp.start()); ServiceClientWithCollector<uavcan::protocol::GetTransportStats> tsp_cln(nodes.b); /* * First request */ ASSERT_LE(0, tsp_cln.call(1, uavcan::protocol::GetTransportStats::Request())); ASSERT_LE(0, nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10))); ASSERT_TRUE(tsp_cln.collector.result.get()); ASSERT_TRUE(tsp_cln.collector.result->isSuccessful()); ASSERT_EQ(0, tsp_cln.collector.result->response.transfer_errors); ASSERT_EQ(1, tsp_cln.collector.result->response.transfers_rx); ASSERT_EQ(0, tsp_cln.collector.result->response.transfers_tx); ASSERT_EQ(1, tsp_cln.collector.result->response.can_iface_stats.size()); ASSERT_EQ(0, tsp_cln.collector.result->response.can_iface_stats[0].errors); ASSERT_EQ(1, tsp_cln.collector.result->response.can_iface_stats[0].frames_rx); ASSERT_EQ(0, tsp_cln.collector.result->response.can_iface_stats[0].frames_tx); /* * Second request */ ASSERT_LE(0, tsp_cln.call(1, uavcan::protocol::GetTransportStats::Request())); ASSERT_LE(0, nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10))); ASSERT_TRUE(tsp_cln.collector.result.get()); ASSERT_EQ(0, tsp_cln.collector.result->response.transfer_errors); ASSERT_EQ(2, tsp_cln.collector.result->response.transfers_rx); ASSERT_EQ(1, tsp_cln.collector.result->response.transfers_tx); ASSERT_EQ(1, tsp_cln.collector.result->response.can_iface_stats.size()); ASSERT_EQ(0, tsp_cln.collector.result->response.can_iface_stats[0].errors); ASSERT_EQ(2, tsp_cln.collector.result->response.can_iface_stats[0].frames_rx); ASSERT_EQ(6, tsp_cln.collector.result->response.can_iface_stats[0].frames_tx); /* * Sending a malformed frame, it must be registered as tranfer error */ uavcan::Frame frame(uavcan::protocol::GetTransportStats::DefaultDataTypeID, uavcan::TransferTypeServiceRequest, 2, 1, 0, 0, true); uavcan::CanFrame can_frame; ASSERT_TRUE(frame.compile(can_frame)); nodes.can_a.read_queue.push(can_frame); ASSERT_LE(0, nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10))); /* * Introducing a CAN driver error */ nodes.can_a.error_count = 72; /* * Third request */ ASSERT_LE(0, tsp_cln.call(1, uavcan::protocol::GetTransportStats::Request())); ASSERT_LE(0, nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(10))); ASSERT_TRUE(tsp_cln.collector.result.get()); ASSERT_EQ(1, tsp_cln.collector.result->response.transfer_errors); // That broken frame ASSERT_EQ(3, tsp_cln.collector.result->response.transfers_rx); ASSERT_EQ(2, tsp_cln.collector.result->response.transfers_tx); ASSERT_EQ(1, tsp_cln.collector.result->response.can_iface_stats.size()); ASSERT_EQ(72, tsp_cln.collector.result->response.can_iface_stats[0].errors); ASSERT_EQ(4, tsp_cln.collector.result->response.can_iface_stats[0].frames_rx); // Same here ASSERT_EQ(12, tsp_cln.collector.result->response.can_iface_stats[0].frames_tx); }
TEST(ServiceClient, Basic) { InterlinkedTestNodesWithSysClock nodes; // Type registration uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator; // Server uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a); ASSERT_EQ(0, server.start(stringServiceServerCallback)); { // Caller typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType; typedef uavcan::ServiceClient<root_ns_a::StringService, typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType; ServiceCallResultHandler<root_ns_a::StringService> handler; ClientType client1(nodes.b); ClientType client2(nodes.b); ClientType client3(nodes.b); ASSERT_EQ(0, client1.getNumPendingCalls()); ASSERT_EQ(0, client2.getNumPendingCalls()); ASSERT_EQ(0, client3.getNumPendingCalls()); ASSERT_FALSE(client1.hasPendingCallToServer(1)); client1.setCallback(handler.bind()); client2.setCallback(client1.getCallback()); client3.setCallback(client1.getCallback()); client3.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100)); ASSERT_EQ(1, nodes.a.getDispatcher().getNumServiceRequestListeners()); ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // NOT listening! root_ns_a::StringService::Request request; request.string_request = "Hello world"; std::cout << "!!! Calling!" << std::endl; ASSERT_LT(0, client1.call(1, request)); // OK ASSERT_LT(0, client1.call(1, request)); // OK - second request ASSERT_LT(0, client2.call(1, request)); // OK ASSERT_LT(0, client3.call(99, request)); // Will timeout! ASSERT_LT(0, client3.call(1, request)); // OK - second request ASSERT_TRUE(client1.hasPendingCallToServer(1)); ASSERT_TRUE(client2.hasPendingCallToServer(1)); ASSERT_TRUE(client3.hasPendingCallToServer(99)); ASSERT_TRUE(client3.hasPendingCallToServer(1)); std::cout << "!!! Spinning!" << std::endl; ASSERT_EQ(3, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Listening now! ASSERT_TRUE(client1.hasPendingCalls()); ASSERT_TRUE(client2.hasPendingCalls()); ASSERT_TRUE(client3.hasPendingCalls()); ASSERT_EQ(2, client1.getNumPendingCalls()); ASSERT_EQ(1, client2.getNumPendingCalls()); ASSERT_EQ(2, client3.getNumPendingCalls()); ASSERT_EQ(uavcan::NodeID(1), client2.getCallIDByIndex(0).server_node_id); ASSERT_EQ(uavcan::NodeID(), client2.getCallIDByIndex(1).server_node_id); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(20)); std::cout << "!!! Spin finished!" << std::endl; ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Third is still listening! ASSERT_FALSE(client1.hasPendingCalls()); ASSERT_FALSE(client2.hasPendingCalls()); ASSERT_TRUE(client3.hasPendingCalls()); ASSERT_EQ(0, client1.getNumPendingCalls()); ASSERT_EQ(0, client2.getNumPendingCalls()); ASSERT_EQ(1, client3.getNumPendingCalls()); // The one addressed to 99 is still waiting // Validating root_ns_a::StringService::Response expected_response; expected_response.string_response = "Request string: Hello world"; ASSERT_TRUE(handler.match(ResultType::Success, 1, expected_response)); nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(200)); ASSERT_FALSE(client1.hasPendingCalls()); ASSERT_FALSE(client2.hasPendingCalls()); ASSERT_FALSE(client3.hasPendingCalls()); ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Third has timed out :( // Validating ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response())); // Stray request ASSERT_LT(0, client3.call(99, request)); // Will timeout! ASSERT_TRUE(client3.hasPendingCalls()); ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); } // All destroyed - nobody listening ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); }
TEST(ServiceClient, ConcurrentCalls) { InterlinkedTestNodesWithSysClock nodes; // Type registration uavcan::GlobalDataTypeRegistry::instance().reset(); uavcan::DefaultDataTypeRegistrator<root_ns_a::StringService> _registrator; // Server uavcan::ServiceServer<root_ns_a::StringService> server(nodes.a); ASSERT_EQ(0, server.start(stringServiceServerCallback)); // Caller typedef uavcan::ServiceCallResult<root_ns_a::StringService> ResultType; typedef uavcan::ServiceClient<root_ns_a::StringService, typename ServiceCallResultHandler<root_ns_a::StringService>::Binder > ClientType; ServiceCallResultHandler<root_ns_a::StringService> handler; /* * Initializing */ ClientType client(nodes.b); ASSERT_EQ(0, client.getNumPendingCalls()); client.setCallback(handler.bind()); ASSERT_EQ(1, nodes.a.getDispatcher().getNumServiceRequestListeners()); ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // NOT listening! ASSERT_FALSE(client.hasPendingCalls()); ASSERT_EQ(0, client.getNumPendingCalls()); /* * Calling ten requests, the last one will be cancelled * Note that there will be non-unique transfer ID values; the client must handle that correctly */ uavcan::ServiceCallID last_call_id; for (int i = 0; i < 10; i++) { std::ostringstream os; os << i; root_ns_a::StringService::Request request; request.string_request = os.str().c_str(); ASSERT_LT(0, client.call(1, request, last_call_id)); } ASSERT_LT(0, client.call(99, root_ns_a::StringService::Request())); // Will timeout in 500 ms client.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(100)); ASSERT_LT(0, client.call(88, root_ns_a::StringService::Request())); // Will timeout in 100 ms ASSERT_TRUE(client.hasPendingCalls()); ASSERT_EQ(12, client.getNumPendingCalls()); ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Listening /* * Cancelling one */ client.cancelCall(last_call_id); ASSERT_TRUE(client.hasPendingCalls()); ASSERT_EQ(11, client.getNumPendingCalls()); ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening /* * Spinning */ nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(20)); ASSERT_TRUE(client.hasPendingCalls()); ASSERT_EQ(2, client.getNumPendingCalls()); // Two still pending ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening /* * Validating the ones that didn't timeout */ root_ns_a::StringService::Response last_response; for (int i = 0; i < 9; i++) { std::ostringstream os; os << "Request string: " << i; last_response.string_response = os.str().c_str(); ASSERT_FALSE(handler.responses.empty()); ASSERT_STREQ(last_response.string_response.c_str(), handler.responses.front().string_response.c_str()); handler.responses.pop(); } ASSERT_TRUE(handler.responses.empty()); /* * Validating the 100 ms timeout */ nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(100)); ASSERT_TRUE(client.hasPendingCalls()); ASSERT_EQ(1, client.getNumPendingCalls()); // One dropped ASSERT_EQ(1, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Still listening ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 88, root_ns_a::StringService::Response())); /* * Validating the 500 ms timeout */ nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(500)); ASSERT_FALSE(client.hasPendingCalls()); ASSERT_EQ(0, client.getNumPendingCalls()); // All finished ASSERT_EQ(0, nodes.b.getDispatcher().getNumServiceResponseListeners()); // Not listening ASSERT_TRUE(handler.match(ResultType::ErrorTimeout, 99, root_ns_a::StringService::Response())); }