diff --git a/ntcore/src/main/native/cpp/net/ServerImpl.cpp b/ntcore/src/main/native/cpp/net/ServerImpl.cpp index 404038b6c3..832cff0986 100644 --- a/ntcore/src/main/native/cpp/net/ServerImpl.cpp +++ b/ntcore/src/main/native/cpp/net/ServerImpl.cpp @@ -1265,6 +1265,7 @@ void ServerImpl::RemoveClient(int clientId) { if (tcdIt != topic->clients.end()) { pubChanged = !tcdIt->second.publishers.empty(); subChanged = !tcdIt->second.subscribers.empty(); + topic->publisherCount -= tcdIt->second.publishers.size(); topic->clients.erase(tcdIt); } diff --git a/ntcore/src/test/native/cpp/net/ServerImplTest.cpp b/ntcore/src/test/native/cpp/net/ServerImplTest.cpp index b1fac0518c..13488acac3 100644 --- a/ntcore/src/test/native/cpp/net/ServerImplTest.cpp +++ b/ntcore/src/test/native/cpp/net/ServerImplTest.cpp @@ -305,6 +305,77 @@ TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) { server.SendOutgoing(id, 200); } +TEST_F(ServerImplTest, ClientDisconnectUnpublish) { + server.SetLocal(&local); + NT_Publisher pubLocalHandle = nt::Handle{0, 1, nt::Handle::kPublisher}; + NT_Topic topicLocalHandle = nt::Handle{0, 1, nt::Handle::kTopic}; + NT_Publisher subHandle = nt::Handle{0, 1, nt::Handle::kSubscriber}; + { + ::testing::InSequence seq; + EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test2"}, + std::string_view{"double"}, + wpi::json::object(), pubLocalHandle)); + EXPECT_CALL(local, NetworkAnnounce(std::string_view{"test"}, + std::string_view{"double"}, + wpi::json::object(), 0)); + EXPECT_CALL(local, NetworkUnannounce(std::string_view{"test"})); + } + + { + std::vector msgs; + msgs.emplace_back(net::ClientMessage{net::PublishMsg{pubLocalHandle, + topicLocalHandle, + "test2", + "double", + wpi::json::object(), + {}}}); + msgs.emplace_back(net::ClientMessage{ + net::ClientValueMsg{pubLocalHandle, Value::MakeDouble(1.0, 10)}}); + server.HandleLocal(msgs); + } + + { + std::vector msgs; + msgs.emplace_back( + net::ClientMessage{net::SubscribeMsg{subHandle, {"test"}, {}}}); + server.HandleLocal(msgs); + } + + ::testing::StrictMock wire; + EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401)); + MockSetPeriodicFunc setPeriodic; + { + ::testing::InSequence seq; + EXPECT_CALL(wire, GetLastReceivedTime()).WillOnce(Return(0)); + EXPECT_CALL(wire, SendPing(100)); + EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendValues() + EXPECT_CALL( + wire, DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test", 8, "double", 1, wpi::json::object()}})))) + .WillOnce(Return(0)); + EXPECT_CALL(wire, Flush()); // SendValues() + } + + // connect client + auto [name, id] = server.AddClient("test", "connInfo", false, wire, + setPeriodic.AsStdFunction()); + + // publish topic + { + NT_Publisher pubHandle = nt::Handle{0, 1, nt::Handle::kPublisher}; + NT_Topic topicHandle = nt::Handle{0, 1, nt::Handle::kTopic}; + std::vector msgs; + msgs.emplace_back(net::ClientMessage{net::PublishMsg{ + pubHandle, topicHandle, "test", "double", wpi::json::object(), {}}}); + server.ProcessIncomingText(id, EncodeText(msgs)); + } + + server.SendOutgoing(id, 100); + + // disconnect client + server.RemoveClient(id); +} + TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { // publish before client connect server.SetLocal(&local);