diff --git a/communication/inc/protocol.h b/communication/inc/protocol.h index 72f9ca8456..72c1d355d1 100644 --- a/communication/inc/protocol.h +++ b/communication/inc/protocol.h @@ -628,7 +628,7 @@ class Protocol void notify_message_complete(message_id_t msg_id, CoAPCode::Enum responseCode); - void notify_client_messages_processed(); + virtual void notify_client_messages_processed(); // Declared as virtual for mocking in unit tests /** * Retrieves the next token. diff --git a/communication/src/description.cpp b/communication/src/description.cpp index 0882a358b0..93b5f3abff 100644 --- a/communication/src/description.cpp +++ b/communication/src/description.cpp @@ -392,7 +392,8 @@ ProtocolError Description::receiveAckOrRst(const Message& msg, int* descFlags) { activeReq_.reset(); if (!reqQueue_.isEmpty()) { CHECK_PROTOCOL(sendNextRequest(reqQueue_.takeFirst())); - } else { + } + if (!activeReq_.has_value() && reqQueue_.isEmpty()) { proto_->notify_client_messages_processed(); } *descFlags = flags; diff --git a/test/unit_tests/communication/description.cpp b/test/unit_tests/communication/description.cpp index 54801d7a46..aa6b0194a2 100644 --- a/test/unit_tests/communication/description.cpp +++ b/test/unit_tests/communication/description.cpp @@ -541,4 +541,39 @@ TEST_CASE("Description") { CHECK(m.option(CoapOption::BLOCK2).toUInt() == BlockOption().index(0).more(true)); d.sendMessage(CoapMessage().type(CoapType::ACK).code(CoapCode::EMPTY).id(m.id())); } + + SECTION("notifies the protocol layer when all client requests have been processed") { + Mock proto(*d.protocol()); + When(Method(proto, notify_client_messages_processed)).AlwaysReturn(); + When(Method(cb, appendSystemInfo)).Do([](appender_fn append, void* arg, void* reserved) { + auto s = std::string(PROTOCOL_BUFFER_SIZE, 'a'); + append(arg, (const uint8_t*)s.data(), s.size()); + return true; + }); + When(Method(cb, appendAppInfo)).Do([](appender_fn append, void* arg, void* reserved) { + auto s = std::string(BLOCK_SIZE, 'b'); + append(arg, (const uint8_t*)s.data(), s.size()); + return true; + }); + CHECK(!d.get()->hasPendingClientRequests()); + // Send a blockwise request to the server + d.get()->sendRequest(DescriptionType::DESCRIBE_SYSTEM); + CHECK(d.get()->hasPendingClientRequests()); + // Receive and acknowledge the first block + auto m = d.receiveMessage(); + d.sendMessage(CoapMessage().type(CoapType::ACK).code(CoapCode::EMPTY).id(m.id())); + // Send another request to the server (a regular one) + d.get()->sendRequest(DescriptionType::DESCRIBE_APPLICATION); + // Receive the second block of the first request + m = d.receiveMessage(); + CHECK(d.get()->hasPendingClientRequests()); + Verify(Method(proto, notify_client_messages_processed)).Never(); + // Acknowledge the second block + d.sendMessage(CoapMessage().type(CoapType::ACK).code(CoapCode::EMPTY).id(m.id())); + CHECK(!d.get()->hasPendingClientRequests()); + Verify(Method(proto, notify_client_messages_processed)).Once(); + // Receive and acknowledge the second request + m = d.receiveMessage(); + d.sendMessage(CoapMessage().type(CoapType::ACK).code(CoapCode::EMPTY).id(m.id())); + } }