diff --git a/ntcore/BUILD.bazel b/ntcore/BUILD.bazel index 935fc3a05d..2591b12cec 100644 --- a/ntcore/BUILD.bazel +++ b/ntcore/BUILD.bazel @@ -190,7 +190,7 @@ cc_test( ], deps = [ ":ntcore", - "//thirdparty/googletest", + "//thirdparty/catch2", "//wpiutil:wpiutil-testlib", ], ) diff --git a/ntcore/CMakeLists.txt b/ntcore/CMakeLists.txt index b578d40be5..d14b69a104 100644 --- a/ntcore/CMakeLists.txt +++ b/ntcore/CMakeLists.txt @@ -44,7 +44,7 @@ wpilib_target_warnings(ntcoredev) target_link_libraries(ntcoredev ntcore) if(WPILIB_WITH_TESTS) - wpilib_add_test(ntcore src/test/native/cpp) + wpilib_add_test_catch2(ntcore src/test/native/cpp) target_include_directories(ntcore_test PRIVATE src/main/native/cpp) - target_link_libraries(ntcore_test ntcore googletest wpiutil_testlib) + target_link_libraries(ntcore_test ntcore wpiutil_testlib) endif() diff --git a/ntcore/build.gradle b/ntcore/build.gradle index 49a1d4a57a..b4556ce7d9 100644 --- a/ntcore/build.gradle +++ b/ntcore/build.gradle @@ -11,6 +11,7 @@ ext { nativeName = 'ntcore' devMain = 'org.wpilib.networktables.DevMain' + nativeTestSuiteName = "${nativeName}Catch2Test" generatedSources = "$projectDir/src/generated/main/native/cpp" generatedHeaders = "$projectDir/src/generated/main/native/include" jniSplitSetup = { diff --git a/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp b/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp index cab82c9012..8b0ed7afd3 100644 --- a/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp +++ b/ntcore/src/test/native/cpp/ConnectionListenerTest.cpp @@ -6,20 +6,21 @@ #include #include -#include +#include +#include #include "TestPrinters.hpp" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/Synchronization.hpp" #include "wpi/util/mutex.hpp" -class ConnectionListenerTest : public ::testing::Test { +class ConnectionListenerTest { public: ConnectionListenerTest() : server_inst(wpi::nt::CreateInstance()), client_inst(wpi::nt::CreateInstance()) {} - ~ConnectionListenerTest() override { + ~ConnectionListenerTest() { wpi::nt::DestroyInstance(server_inst); wpi::nt::DestroyInstance(client_inst); } @@ -42,32 +43,33 @@ void ConnectionListenerTest::Connect(const char* address, unsigned int port4) { while (!wpi::nt::IsConnected(client_inst)) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (++count > 30) { - FAIL() << "timed out waiting for client to start"; + FAIL("timed out waiting for client to start"); } } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } -TEST_F(ConnectionListenerTest, Polled) { +TEST_CASE_METHOD(ConnectionListenerTest, "ConnectionListenerTest Polled", + "[ntcore][connection-listener]") { // set up the poller NT_ListenerPoller poller = wpi::nt::CreateListenerPoller(server_inst); - ASSERT_NE(poller, 0); + REQUIRE(poller != 0); NT_Listener handle = wpi::nt::AddPolledListener( poller, server_inst, wpi::nt::EventFlags::CONNECTION); - ASSERT_NE(handle, 0); + REQUIRE(handle != 0); // trigger a connect event Connect("127.0.0.1", 10020); // get the event bool timed_out = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timed_out)); - ASSERT_FALSE(timed_out); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timed_out)); + REQUIRE_FALSE(timed_out); auto result = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(result.size(), 1u); - EXPECT_EQ(handle, result[0].listener); - EXPECT_TRUE(result[0].GetConnectionInfo()); - EXPECT_EQ(result[0].flags, wpi::nt::EventFlags::CONNECTED); + REQUIRE(result.size() == 1u); + CHECK(handle == result[0].listener); + CHECK(result[0].GetConnectionInfo()); + CHECK(result[0].flags == wpi::nt::EventFlags::CONNECTED); // trigger a disconnect event wpi::nt::StopClient(client_inst); @@ -75,20 +77,19 @@ TEST_F(ConnectionListenerTest, Polled) { // get the event timed_out = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timed_out)); - ASSERT_FALSE(timed_out); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timed_out)); + REQUIRE_FALSE(timed_out); result = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(result.size(), 1u); - EXPECT_EQ(handle, result[0].listener); - EXPECT_TRUE(result[0].GetConnectionInfo()); - EXPECT_EQ(result[0].flags, wpi::nt::EventFlags::DISCONNECTED); + REQUIRE(result.size() == 1u); + CHECK(handle == result[0].listener); + CHECK(result[0].GetConnectionInfo()); + CHECK(result[0].flags == wpi::nt::EventFlags::DISCONNECTED); } -class ConnectionListenerVariantTest - : public ConnectionListenerTest, - public ::testing::WithParamInterface> {}; - -TEST_P(ConnectionListenerVariantTest, Threaded) { +TEST_CASE_METHOD(ConnectionListenerTest, "ConnectionListenerTest Threaded", + "[ntcore][connection-listener]") { + auto param = GENERATE(std::pair{"127.0.0.1", 0}, std::pair{"127.0.0.1 ", 1}, + std::pair{" 127.0.0.1 ", 2}); wpi::util::mutex m; std::vector result; auto handle = wpi::nt::AddListener( @@ -98,19 +99,19 @@ TEST_P(ConnectionListenerVariantTest, Threaded) { }); // trigger a connect event - Connect(GetParam().first, 20001 + GetParam().second); + Connect(param.first, 20001 + param.second); bool timed_out = false; - ASSERT_TRUE(wpi::util::WaitForObject(handle, 1.0, &timed_out)); - ASSERT_FALSE(timed_out); + REQUIRE(wpi::util::WaitForObject(handle, 1.0, &timed_out)); + REQUIRE_FALSE(timed_out); // get the event { std::scoped_lock lock{m}; - ASSERT_EQ(result.size(), 1u); - EXPECT_EQ(handle, result[0].listener); - EXPECT_TRUE(result[0].GetConnectionInfo()); - EXPECT_EQ(result[0].flags, wpi::nt::EventFlags::CONNECTED); + REQUIRE(result.size() == 1u); + CHECK(handle == result[0].listener); + CHECK(result[0].GetConnectionInfo()); + CHECK(result[0].flags == wpi::nt::EventFlags::CONNECTED); result.clear(); } @@ -124,15 +125,9 @@ TEST_P(ConnectionListenerVariantTest, Threaded) { // get the event { std::scoped_lock lock{m}; - ASSERT_EQ(result.size(), 1u); - EXPECT_EQ(handle, result[0].listener); - EXPECT_TRUE(result[0].GetConnectionInfo()); - EXPECT_EQ(result[0].flags, wpi::nt::EventFlags::DISCONNECTED); + REQUIRE(result.size() == 1u); + CHECK(handle == result[0].listener); + CHECK(result[0].GetConnectionInfo()); + CHECK(result[0].flags == wpi::nt::EventFlags::DISCONNECTED); } } - -INSTANTIATE_TEST_SUITE_P(ConnectionListenerVariantTests, - ConnectionListenerVariantTest, - testing::Values(std::pair{"127.0.0.1", 0}, - std::pair{"127.0.0.1 ", 1}, - std::pair{" 127.0.0.1 ", 2})); diff --git a/ntcore/src/test/native/cpp/LocalStorageTest.cpp b/ntcore/src/test/native/cpp/LocalStorageTest.cpp index 5b1f6a49b9..81b7259b22 100644 --- a/ntcore/src/test/native/cpp/LocalStorageTest.cpp +++ b/ntcore/src/test/native/cpp/LocalStorageTest.cpp @@ -4,56 +4,31 @@ #include "LocalStorage.hpp" +#include +#include #include +#include +#include #include -#include +#include +#include "MockAssertions.hpp" #include "MockListenerStorage.hpp" #include "MockLogger.hpp" -#include "PubSubOptionsMatcher.hpp" #include "TestPrinters.hpp" -#include "ValueMatcher.hpp" -#include "gmock/gmock.h" #include "net/MockMessageHandler.hpp" #include "net/MockNetworkInterface.hpp" #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp.hpp" -#include "wpi/util/SpanMatcher.hpp" -#include "wpi/util/Synchronization.hpp" - -using ::testing::_; -using ::testing::AllOf; -using ::testing::ElementsAre; -using ::testing::Field; -using ::testing::IsEmpty; -using ::testing::Property; -using ::testing::Return; namespace wpi::nt { -::testing::Matcher IsPubSubOptions( - const PubSubOptionsImpl& good) { - return AllOf( - Field("periodic", &PubSubOptionsImpl::periodicMs, good.periodicMs), - Field("pollStorage", &PubSubOptionsImpl::pollStorage, good.pollStorage), - Field("sendAll", &PubSubOptionsImpl::sendAll, good.sendAll), - Field("keepDuplicates", &PubSubOptionsImpl::keepDuplicates, - good.keepDuplicates), - Field("disableSignal", &PubSubOptionsImpl::disableSignal, - good.disableSignal)); -} - -::testing::Matcher IsDefaultPubSubOptions() { - static constexpr PubSubOptionsImpl kDefaultPubSubOptionsImpl; - return IsPubSubOptions(kDefaultPubSubOptionsImpl); -} - -class LocalStorageTest : public ::testing::Test { +class LocalStorageTest { public: LocalStorageTest() { storage.StartNetwork(&network); } - ::testing::StrictMock network; + net::MockClientMessageHandler network; wpi::MockLogger logger; MockListenerStorage listenerStorage; LocalStorage storage{0, listenerStorage, logger}; @@ -62,539 +37,548 @@ class LocalStorageTest : public ::testing::Test { NT_Topic bazTopic{storage.GetTopic("baz")}; }; -TEST_F(LocalStorageTest, GetTopicsUnpublished) { - EXPECT_TRUE(storage.GetTopics("", 0).empty()); - EXPECT_TRUE(storage.GetTopics("", {}).empty()); - EXPECT_TRUE(storage.GetTopicInfo("", 0).empty()); - EXPECT_TRUE(storage.GetTopicInfo("", {}).empty()); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetTopicsUnpublished", + "[ntcore][local-storage]") { + CHECK(storage.GetTopics("", 0).empty()); + CHECK(storage.GetTopics("", {}).empty()); + CHECK(storage.GetTopicInfo("", 0).empty()); + CHECK(storage.GetTopicInfo("", {}).empty()); } -TEST_F(LocalStorageTest, GetTopic2) { +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetTopic2", + "[ntcore][local-storage]") { auto foo2 = storage.GetTopic("foo"); - EXPECT_EQ(fooTopic, foo2); - EXPECT_NE(fooTopic, barTopic); + CHECK(fooTopic == foo2); + CHECK(fooTopic != barTopic); } -TEST_F(LocalStorageTest, GetTopicEmptyName) { - EXPECT_EQ(storage.GetTopic(""), 0); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetTopicEmptyName", + "[ntcore][local-storage]") { + CHECK(storage.GetTopic("") == 0); } -TEST_F(LocalStorageTest, GetEntryEmptyName) { - EXPECT_EQ(storage.GetEntry(""), 0); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetEntryEmptyName", + "[ntcore][local-storage]") { + CHECK(storage.GetEntry("") == 0); } -TEST_F(LocalStorageTest, GetEntryCached) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"tocache"}}), - IsDefaultPubSubOptions())); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetEntryCached", + "[ntcore][local-storage]") { auto entry1 = storage.GetEntry("tocache"); - EXPECT_EQ(entry1, storage.GetEntry("tocache")); + CHECK(entry1 == storage.GetEntry("tocache")); + + CheckNetworkCounts(network, 0, 1, 0); + CheckSubscribe(network.subscribeCalls[0], {"tocache"}); } -TEST_F(LocalStorageTest, GetTopicName) { - EXPECT_EQ(storage.GetTopicName(fooTopic), "foo"); - EXPECT_EQ(storage.GetTopicName(barTopic), "bar"); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetTopicName", + "[ntcore][local-storage]") { + CHECK(storage.GetTopicName(fooTopic) == "foo"); + CHECK(storage.GetTopicName(barTopic) == "bar"); } -TEST_F(LocalStorageTest, GetTopicInfoUnpublished) { +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest GetTopicInfoUnpublished", + "[ntcore][local-storage]") { auto info = storage.GetTopicInfo(fooTopic); - EXPECT_EQ(info.topic, fooTopic); - EXPECT_EQ(info.name, "foo"); - EXPECT_EQ(info.type, NT_UNASSIGNED); - EXPECT_TRUE(info.type_str.empty()); - EXPECT_EQ(info.properties, "{}"); + CHECK(info.topic == fooTopic); + CHECK(info.name == "foo"); + CHECK(info.type == NT_UNASSIGNED); + CHECK(info.type_str.empty()); + CHECK(info.properties == "{}"); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_UNASSIGNED); - EXPECT_TRUE(storage.GetTopicTypeString(fooTopic).empty()); - EXPECT_FALSE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_UNASSIGNED); + CHECK(storage.GetTopicTypeString(fooTopic).empty()); + CHECK_FALSE(storage.GetTopicExists(fooTopic)); } -TEST_F(LocalStorageTest, DefaultProps) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest DefaultProps", + "[ntcore][local-storage]") { storage.Publish(fooTopic, NT_BOOLEAN, "boolean", wpi::util::json::object(), {}); - EXPECT_FALSE(storage.GetTopicPersistent(fooTopic)); - EXPECT_FALSE(storage.GetTopicRetained(fooTopic)); - EXPECT_TRUE(storage.GetTopicCached(fooTopic)); + CHECK_FALSE(storage.GetTopicPersistent(fooTopic)); + CHECK_FALSE(storage.GetTopicRetained(fooTopic)); + CHECK(storage.GetTopicCached(fooTopic)); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); } -TEST_F(LocalStorageTest, PublishNewNoProps) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest PublishNewNoProps", + "[ntcore][local-storage]") { storage.Publish(fooTopic, NT_BOOLEAN, "boolean", wpi::util::json::object(), {}); auto info = storage.GetTopicInfo(fooTopic); - EXPECT_EQ(info.properties, "{}"); + CHECK(info.properties == "{}"); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); } -TEST_F(LocalStorageTest, PublishNewNoPropsNull) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest PublishNewNoPropsNull", + "[ntcore][local-storage]") { storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); auto info = storage.GetTopicInfo(fooTopic); - EXPECT_EQ(info.properties, "{}"); + CHECK(info.properties == "{}"); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); } -TEST_F(LocalStorageTest, PublishNew) { +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest PublishNew", + "[ntcore][local-storage]") { auto properties = wpi::util::json::object("persistent", true); - EXPECT_CALL(network, ClientPublish(_, std::string_view{"foo"}, - std::string_view{"boolean"}, properties, - IsDefaultPubSubOptions())); storage.Publish(fooTopic, NT_BOOLEAN, "boolean", wpi::util::json::object("persistent", true), {}); auto info = storage.GetTopicInfo(fooTopic); - EXPECT_EQ(info.topic, fooTopic); - EXPECT_EQ(info.name, "foo"); - EXPECT_EQ(info.type, NT_BOOLEAN); - EXPECT_EQ(info.type_str, "boolean"); - EXPECT_EQ(info.properties, "{\"persistent\":true}"); + CHECK(info.topic == fooTopic); + CHECK(info.name == "foo"); + CHECK(info.type == NT_BOOLEAN); + CHECK(info.type_str == "boolean"); + CHECK(info.properties == "{\"persistent\":true}"); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", properties); } -TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPost) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, + "LocalStorageTest SubscribeNoTypeLocalPubPost", + "[ntcore][local-storage]") { auto sub = storage.Subscribe(fooTopic, NT_UNASSIGNED, "", {}); - - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); auto val = Value::MakeBoolean(true, 5); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); storage.SetEntryValue(pub, val); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); auto value = storage.GetEntryValue(sub); - ASSERT_TRUE(value.IsBoolean()); - EXPECT_EQ(value.GetBoolean(), true); - EXPECT_EQ(value.time(), 5); + REQUIRE(value.IsBoolean()); + CHECK(value.GetBoolean() == true); + CHECK(value.time() == 5); auto vals = storage.ReadQueue(sub); - ASSERT_EQ(vals.size(), 1u); - EXPECT_EQ(vals[0].value, true); - EXPECT_EQ(vals[0].time, 5); + REQUIRE(vals.size() == 1u); + CHECK(vals[0].value == true); + CHECK(vals[0].time == 5); val = Value::MakeBoolean(false, 6); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); storage.SetEntryValue(pub, val); auto vals2 = storage.ReadQueue(sub); // mismatched type - ASSERT_TRUE(vals2.empty()); + REQUIRE(vals2.empty()); + + CheckNetworkCounts(network, 1, 1, 2); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeBoolean(true, 5)); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), + Value::MakeBoolean(false, 6)); } -TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPre) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, + "LocalStorageTest SubscribeNoTypeLocalPubPre", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); auto val = Value::MakeBoolean(true, 5); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); storage.SetEntryValue(pub, val); - - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); auto sub = storage.Subscribe(fooTopic, NT_UNASSIGNED, "", {}); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); auto value = storage.GetEntryValue(sub); - ASSERT_TRUE(value.IsBoolean()); - EXPECT_EQ(value.GetBoolean(), true); - EXPECT_EQ(value.time(), 5); + REQUIRE(value.IsBoolean()); + CHECK(value.GetBoolean() == true); + CHECK(value.time() == 5); + + CheckNetworkCounts(network, 1, 1, 1); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); } -TEST_F(LocalStorageTest, EntryNoTypeLocalSet) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest EntryNoTypeLocalSet", + "[ntcore][local-storage]") { auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {}); // results in a publish and value set auto val = Value::MakeBoolean(true, 5); - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); - EXPECT_CALL(network, ClientSetValue(_, val)); - EXPECT_TRUE(storage.SetEntryValue(entry, val)); + CHECK(storage.SetEntryValue(entry, val)); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); auto value = storage.GetEntryValue(entry); - ASSERT_TRUE(value.IsBoolean()); - EXPECT_EQ(value.GetBoolean(), true); - EXPECT_EQ(value.time(), 5); + REQUIRE(value.IsBoolean()); + CHECK(value.GetBoolean() == true); + CHECK(value.time() == 5); auto vals = storage.ReadQueue(entry); - ASSERT_EQ(vals.size(), 1u); - EXPECT_EQ(vals[0].value, true); - EXPECT_EQ(vals[0].time, 5); + REQUIRE(vals.size() == 1u); + CHECK(vals[0].value == true); + CHECK(vals[0].time == 5); // normal set with same type val = Value::MakeBoolean(false, 6); - EXPECT_CALL(network, ClientSetValue(_, val)); - EXPECT_TRUE(storage.SetEntryValue(entry, val)); + CHECK(storage.SetEntryValue(entry, val)); auto vals2 = storage.ReadQueue(entry); // mismatched type - ASSERT_TRUE(vals2.empty()); + REQUIRE(vals2.empty()); // cannot change type; won't generate network message - EXPECT_FALSE(storage.SetEntryValue(entry, Value::MakeInteger(5, 7))); + CHECK_FALSE(storage.SetEntryValue(entry, Value::MakeInteger(5, 7))); // should not change type or generate queue items - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); auto vals3 = storage.ReadQueue(entry); // mismatched type - ASSERT_TRUE(vals3.empty()); + REQUIRE(vals3.empty()); + + CheckNetworkCounts(network, 1, 1, 2); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], network.publishCalls[0].pubuid, + Value::MakeBoolean(true, 5)); + CheckSetValue(network.setValueCalls[1], network.publishCalls[0].pubuid, + Value::MakeBoolean(false, 6)); } -TEST_F(LocalStorageTest, PubUnpubPub) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest PubUnpubPub", + "[ntcore][local-storage]") { auto sub = storage.Subscribe(fooTopic, NT_INTEGER, "int", {}); - - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); - EXPECT_CALL(logger, - Call(NT_LOG_INFO, _, _, - std::string_view{ - "local subscribe to 'foo' disabled due to type " - "mismatch (wanted 'int', published as 'boolean')"})); auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); auto val = Value::MakeBoolean(true, 5); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); - EXPECT_TRUE(storage.SetEntryValue(pub, val)); + CHECK(storage.SetEntryValue(pub, val)); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); - EXPECT_TRUE(storage.ReadQueue(sub).empty()); - - EXPECT_CALL(network, ClientUnpublish(Handle{pub}.GetIndex())); + CHECK(storage.ReadQueue(sub).empty()); storage.Unpublish(pub); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_UNASSIGNED); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), ""); - EXPECT_FALSE(storage.GetTopicExists(fooTopic)); - - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"int"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); + CHECK(storage.GetTopicType(fooTopic) == NT_UNASSIGNED); + CHECK(storage.GetTopicTypeString(fooTopic) == ""); + CHECK_FALSE(storage.GetTopicExists(fooTopic)); pub = storage.Publish(fooTopic, NT_INTEGER, "int", {}, {}); val = Value::MakeInteger(3, 5); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); - EXPECT_TRUE(storage.SetEntryValue(pub, val)); + CHECK(storage.SetEntryValue(pub, val)); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_INTEGER); + CHECK(storage.GetTopicTypeString(fooTopic) == "int"); + CHECK(storage.GetTopicExists(fooTopic)); - EXPECT_EQ(storage.ReadQueue(sub).size(), 1u); + CHECK(storage.ReadQueue(sub).size() == 1u); + + CheckNetworkCounts(network, 2, 1, 2, 1); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckPublish(network.publishCalls[1], "foo", "int", + wpi::util::json::object()); + CHECK(network.unpublishCalls[0] == network.publishCalls[0].pubuid); + CheckSetValue(network.setValueCalls[0], network.publishCalls[0].pubuid, + Value::MakeBoolean(true, 5)); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), + Value::MakeInteger(3, 5)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted 'int', " + "published as 'boolean')"); } -TEST_F(LocalStorageTest, LocalPubConflict) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest LocalPubConflict", + "[ntcore][local-storage]") { auto pub1 = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); - - EXPECT_CALL( - logger, - Call(NT_LOG_INFO, _, _, - std::string_view{"local publish to 'foo' disabled due to type " - "mismatch (wanted 'int', currently 'boolean')"})); auto pub2 = storage.Publish(fooTopic, NT_INTEGER, "int", {}, {}); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); - EXPECT_CALL(network, ClientSetValue(Handle{pub1}.GetIndex(), _)); - - EXPECT_TRUE(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5))); - EXPECT_FALSE(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5))); + CHECK(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5))); + CHECK_FALSE(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5))); // unpublishing pub1 will publish pub2 to the network - EXPECT_CALL(network, ClientUnpublish(Handle{pub1}.GetIndex())); - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"int"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); storage.Unpublish(pub1); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_INTEGER); + CHECK(storage.GetTopicTypeString(fooTopic) == "int"); + CHECK(storage.GetTopicExists(fooTopic)); - EXPECT_CALL(network, ClientSetValue(Handle{pub2}.GetIndex(), _)); + CHECK_FALSE(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5))); + CHECK(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5))); - EXPECT_FALSE(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5))); - EXPECT_TRUE(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5))); + CheckNetworkCounts(network, 2, 0, 2, 1); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckPublish(network.publishCalls[1], "foo", "int", + wpi::util::json::object()); + CHECK(network.unpublishCalls[0] == static_cast(Handle{pub1}.GetIndex())); + CheckSetValue(network.setValueCalls[0], Handle{pub1}.GetIndex(), + Value::MakeBoolean(true, 5)); + CheckSetValue(network.setValueCalls[1], Handle{pub2}.GetIndex(), + Value::MakeInteger(3, 5)); + logger.CheckMessage( + NT_LOG_INFO, + "local publish to 'foo' disabled due to type mismatch (wanted 'int', " + "currently 'boolean')"); } -TEST_F(LocalStorageTest, LocalSubConflict) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest LocalSubConflict", + "[ntcore][local-storage]") { storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); - - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); - EXPECT_CALL(logger, - Call(NT_LOG_INFO, _, _, - std::string_view{ - "local subscribe to 'foo' disabled due to type " - "mismatch (wanted 'int', published as 'boolean')"})); storage.Subscribe(fooTopic, NT_INTEGER, "int", {}); + + CheckNetworkCounts(network, 1, 1, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted 'int', " + "published as 'boolean')"); } -TEST_F(LocalStorageTest, RemotePubConflict) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest RemotePubConflict", + "[ntcore][local-storage]") { storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); - EXPECT_CALL(logger, - Call(NT_LOG_INFO, _, _, - std::string_view{ - "network announce of 'foo' overriding local publish " - "(was 'boolean', now 'int')"})); - auto id = storage.ServerAnnounce("foo", 0, "int", wpi::util::json::object(), std::nullopt); // network overrides local - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); - - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); + CHECK(storage.GetTopicType(fooTopic) == NT_INTEGER); + CHECK(storage.GetTopicTypeString(fooTopic) == "int"); + CHECK(storage.GetTopicExists(fooTopic)); storage.ServerUnannounce("foo", id); - EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); - EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean"); - EXPECT_TRUE(storage.GetTopicExists(fooTopic)); + CHECK(storage.GetTopicType(fooTopic) == NT_BOOLEAN); + CHECK(storage.GetTopicTypeString(fooTopic) == "boolean"); + CHECK(storage.GetTopicExists(fooTopic)); + + CheckNetworkCounts(network, 2, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckPublish(network.publishCalls[1], "foo", "boolean", + wpi::util::json::object()); + logger.CheckMessage( + NT_LOG_INFO, + "network announce of 'foo' overriding local publish (was 'boolean', now " + "'int')"); } -TEST_F(LocalStorageTest, SubNonExist) { +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SubNonExist", + "[ntcore][local-storage]") { // makes sure no warning is emitted - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {}); + + CheckNetworkCounts(network, 0, 1, 0); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + logger.CheckMessages({}); } -TEST_F(LocalStorageTest, SetDefaultSubscribe) { +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetDefaultSubscribe", + "[ntcore][local-storage]") { // no publish, no value on wire, this is just handled locally - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); auto sub = storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {}); - EXPECT_TRUE(storage.SetDefaultEntryValue(sub, Value::MakeBoolean(true))); + CHECK(storage.SetDefaultEntryValue(sub, Value::MakeBoolean(true))); auto val = storage.GetEntryValue(sub); - ASSERT_TRUE(val.IsBoolean()); - ASSERT_TRUE(val.GetBoolean()); - ASSERT_EQ(val.time(), 0); + REQUIRE(val.IsBoolean()); + REQUIRE(val.GetBoolean()); + REQUIRE(val.time() == 0); + + CheckNetworkCounts(network, 0, 1, 0); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); } -TEST_F(LocalStorageTest, SetDefaultPublish) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetDefaultPublish", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {}); // expect a value across the wire auto expectVal = Value::MakeBoolean(true, 0); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), expectVal)); - EXPECT_TRUE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true))); - - EXPECT_CALL(network, ClientSubscribe(_, _, IsDefaultPubSubOptions())); + CHECK(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true))); auto sub = storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {}); auto val = storage.GetEntryValue(sub); - ASSERT_TRUE(val.IsBoolean()); - ASSERT_TRUE(val.GetBoolean()); - ASSERT_EQ(val.time(), 0); + REQUIRE(val.IsBoolean()); + REQUIRE(val.GetBoolean()); + REQUIRE(val.time() == 0); + + CheckNetworkCounts(network, 1, 1, 1); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), expectVal); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); } -TEST_F(LocalStorageTest, SetDefaultEntry) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetDefaultEntry", + "[ntcore][local-storage]") { auto entry = storage.GetEntry(fooTopic, NT_BOOLEAN, "boolean", {}); // expect a publish and value - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); auto expectVal = Value::MakeBoolean(true, 0); - EXPECT_CALL(network, ClientSetValue(_, expectVal)); - EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true))); + CHECK(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true))); auto val = storage.GetEntryValue(entry); - ASSERT_TRUE(val.IsBoolean()); - ASSERT_TRUE(val.GetBoolean()); - ASSERT_EQ(val.time(), 0); + REQUIRE(val.IsBoolean()); + REQUIRE(val.GetBoolean()); + REQUIRE(val.time() == 0); + + CheckNetworkCounts(network, 1, 1, 1); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], network.publishCalls[0].pubuid, + expectVal); } -TEST_F(LocalStorageTest, SetDefaultEntryUnassigned) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetDefaultEntryUnassigned", + "[ntcore][local-storage]") { auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {}); // expect a publish and value - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"boolean"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); auto expectVal = Value::MakeBoolean(true, 0); - EXPECT_CALL(network, ClientSetValue(_, expectVal)); - EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true))); + CHECK(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true))); - ASSERT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN); + REQUIRE(storage.GetTopicType(fooTopic) == NT_BOOLEAN); auto val = storage.GetEntryValue(entry); - ASSERT_TRUE(val.IsBoolean()); - ASSERT_TRUE(val.GetBoolean()); - ASSERT_EQ(val.time(), 0); + REQUIRE(val.IsBoolean()); + REQUIRE(val.GetBoolean()); + REQUIRE(val.time() == 0); + + CheckNetworkCounts(network, 1, 1, 1); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); + CheckPublish(network.publishCalls[0], "foo", "boolean", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], network.publishCalls[0].pubuid, + expectVal); } -TEST_F(LocalStorageTest, SetDefaultEntryDiffType) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"string"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetDefaultEntryDiffType", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {}); - EXPECT_FALSE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true))); - ASSERT_EQ(storage.GetTopicType(fooTopic), NT_STRING); + CHECK_FALSE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true))); + REQUIRE(storage.GetTopicType(fooTopic) == NT_STRING); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "string", + wpi::util::json::object()); } -TEST_F(LocalStorageTest, SetValueEmptyValue) { - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"string"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetValueEmptyValue", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {}); - EXPECT_FALSE(storage.SetEntryValue(pub, {})); + CHECK_FALSE(storage.SetEntryValue(pub, {})); + + CheckNetworkCounts(network, 1, 0, 0); + CheckPublish(network.publishCalls[0], "foo", "string", + wpi::util::json::object()); } -TEST_F(LocalStorageTest, SetValueEmptyUntypedEntry) { - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsDefaultPubSubOptions())); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetValueEmptyUntypedEntry", + "[ntcore][local-storage]") { auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {}); - EXPECT_FALSE(storage.SetEntryValue(entry, {})); + CHECK_FALSE(storage.SetEntryValue(entry, {})); + + CheckNetworkCounts(network, 0, 1, 0); + CheckSubscribe(network.subscribeCalls[0], {"foo"}); } -TEST_F(LocalStorageTest, PublishUntyped) { - EXPECT_CALL(logger, - Call(NT_LOG_ERROR, _, _, - std::string_view{"cannot publish 'foo' with an unassigned " - "type or empty type string"})); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest PublishUntyped", + "[ntcore][local-storage]") { + CHECK(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}) == 0); - EXPECT_EQ(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}), 0); + CheckNoClientCalls(network); + logger.CheckMessage( + NT_LOG_ERROR, + "cannot publish 'foo' with an unassigned type or empty type string"); } -TEST_F(LocalStorageTest, SetValueInvalidHandle) { - EXPECT_FALSE(storage.SetEntryValue(0, {})); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SetValueInvalidHandle", + "[ntcore][local-storage]") { + CHECK_FALSE(storage.SetEntryValue(0, {})); + CheckNoClientCalls(network); } -TEST_F(LocalStorageTest, DisableSignalSubscriberQueuesWithoutSignaling) { +TEST_CASE_METHOD( + LocalStorageTest, + "LocalStorageTest DisableSignalSubscriberQueuesWithoutSignaling", + "[ntcore][local-storage]") { PubSubOptionsImpl subOptions; subOptions.disableSignal = true; - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsPubSubOptions(subOptions))); auto sub = storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableSignal = true}); - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"double"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); auto val = Value::MakeDouble(1.0, 50); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); storage.SetEntryValue(pub, val); bool timedOut = false; - EXPECT_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut)); - EXPECT_TRUE(timedOut); + CHECK_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut)); + CHECK(timedOut); auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 1u); - EXPECT_EQ(values[0].value, 1.0); - EXPECT_EQ(values[0].time, 50); + REQUIRE(values.size() == 1u); + CHECK(values[0].value == 1.0); + CHECK(values[0].time == 50); + + CheckNetworkCounts(network, 1, 1, 1); + CheckSubscribe(network.subscribeCalls[0], {"foo"}, subOptions); + CheckPublish(network.publishCalls[0], "foo", "double", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val); } -TEST_F(LocalStorageTest, DisableSignalMultiSubscriberDoesNotSignalHandle) { +TEST_CASE_METHOD( + LocalStorageTest, + "LocalStorageTest DisableSignalMultiSubscriberDoesNotSignalHandle", + "[ntcore][local-storage]") { PubSubOptionsImpl subOptions; subOptions.disableSignal = true; - EXPECT_CALL(network, ClientSubscribe(_, _, IsPubSubOptions(subOptions))); + subOptions.prefixMatch = true; auto sub = storage.SubscribeMultiple({{""}}, {.disableSignal = true}); - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"double"}, - wpi::util::json::object(), IsDefaultPubSubOptions())); auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); auto val = Value::MakeDouble(1.0, 50); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val)); storage.SetEntryValue(pub, val); bool timedOut = false; - EXPECT_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut)); - EXPECT_TRUE(timedOut); + CHECK_FALSE(wpi::util::WaitForObject(sub, 0, &timedOut)); + CHECK(timedOut); + + CheckNetworkCounts(network, 1, 1, 1); + CheckSubscribe(network.subscribeCalls[0], {""}, subOptions); + CheckPublish(network.publishCalls[0], "foo", "double", + wpi::util::json::object()); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val); } class LocalStorageDuplicatesTest : public LocalStorageTest { @@ -610,91 +594,119 @@ class LocalStorageDuplicatesTest : public LocalStorageTest { }; void LocalStorageDuplicatesTest::SetupPubSub(bool keepPub, bool keepSub) { + auto publishCount = network.publishCalls.size(); + auto unpublishCount = network.unpublishCalls.size(); + auto setPropertiesCount = network.setPropertiesCalls.size(); + auto subscribeCount = network.subscribeCalls.size(); + auto unsubscribeCount = network.unsubscribeCalls.size(); + auto setValueCount = network.setValueCalls.size(); + PubSubOptionsImpl pubOptions; pubOptions.keepDuplicates = keepPub; - EXPECT_CALL( - network, - ClientPublish(_, std::string_view{"foo"}, std::string_view{"double"}, - wpi::util::json::object(), IsPubSubOptions(pubOptions))); pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {.keepDuplicates = keepPub}); PubSubOptionsImpl subOptions; subOptions.pollStorage = 10; subOptions.keepDuplicates = keepSub; - EXPECT_CALL(network, - ClientSubscribe(_, wpi::util::SpanEq({std::string{"foo"}}), - IsPubSubOptions(subOptions))); sub = storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.pollStorage = 10, .keepDuplicates = keepSub}); + + CheckClientMessageCounts(network, { + .publish = publishCount + 1, + .unpublish = unpublishCount, + .setProperties = setPropertiesCount, + .subscribe = subscribeCount + 1, + .unsubscribe = unsubscribeCount, + .setValue = setValueCount, + }); + CheckPublish(network.publishCalls[publishCount], "foo", "double", + wpi::util::json::object(), pubOptions); + CheckSubscribe(network.subscribeCalls[subscribeCount], {"foo"}, subOptions); } void LocalStorageDuplicatesTest::SetValues(bool expectDuplicates) { storage.SetEntryValue(pub, val1); storage.SetEntryValue(pub, val2); // verify the timestamp was updated (or not) - EXPECT_EQ(storage.GetEntryLastChange(sub), - expectDuplicates ? val2.time() : val1.time()); + CHECK(storage.GetEntryLastChange(sub) == + (expectDuplicates ? val2.time() : val1.time())); storage.SetEntryValue(pub, val3); } -TEST_F(LocalStorageDuplicatesTest, Defaults) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest Defaults", + "[ntcore][local-storage]") { SetupPubSub(false, false); - - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val1)); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val3)); SetValues(false); // verify 2nd update was dropped locally auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 2u); - ASSERT_EQ(values[0].value, val1.GetDouble()); - ASSERT_EQ(values[0].time, val1.time()); - ASSERT_EQ(values[1].value, val3.GetDouble()); - ASSERT_EQ(values[1].time, val3.time()); + REQUIRE(values.size() == 2u); + REQUIRE(values[0].value == val1.GetDouble()); + REQUIRE(values[0].time == val1.time()); + REQUIRE(values[1].value == val3.GetDouble()); + REQUIRE(values[1].time == val3.time()); + + CheckNetworkCounts(network, 1, 1, 2); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val1); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), val3); } -TEST_F(LocalStorageDuplicatesTest, KeepPub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest KeepPub", + "[ntcore][local-storage]") { SetupPubSub(true, false); - - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val1)).Times(2); - // EXPECT_CALL(network, SetValue(pub, val2)); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val3)); SetValues(true); // verify only 2 updates were received locally auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 2u); + REQUIRE(values.size() == 2u); + + CheckNetworkCounts(network, 1, 1, 3); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val1); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), val2); + CheckSetValue(network.setValueCalls[2], Handle{pub}.GetIndex(), val3); } -TEST_F(LocalStorageDuplicatesTest, KeepSub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest KeepSub", + "[ntcore][local-storage]") { SetupPubSub(false, true); // second update should NOT go to the network - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val1)); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val3)); SetValues(false); // verify 2 updates were received locally auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 2u); + REQUIRE(values.size() == 2u); + + CheckNetworkCounts(network, 1, 1, 2); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val1); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), val3); } -TEST_F(LocalStorageDuplicatesTest, KeepPubSub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest KeepPubSub", + "[ntcore][local-storage]") { SetupPubSub(true, true); // second update SHOULD go to the network - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val1)).Times(2); - // EXPECT_CALL(network, SetValue(pub, val2)); - EXPECT_CALL(network, ClientSetValue(Handle{pub}.GetIndex(), val3)); SetValues(true); // verify all 3 updates were received locally auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 3u); + REQUIRE(values.size() == 3u); + + CheckNetworkCounts(network, 1, 1, 3); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), val1); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), val2); + CheckSetValue(network.setValueCalls[2], Handle{pub}.GetIndex(), val3); } -TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest FromNetworkDefault", + "[ntcore][local-storage]") { SetupPubSub(false, false); // incoming from the network are treated like a normal local publish @@ -703,19 +715,23 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) { storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated - EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time()); + CHECK(storage.GetEntryLastChange(sub) == val2.time()); storage.ServerSetValue(topic, val3); // verify 2nd update was dropped for local subscriber auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 2u); - ASSERT_EQ(values[0].value, val1.GetDouble()); - ASSERT_EQ(values[0].time, val1.time()); - ASSERT_EQ(values[1].value, val3.GetDouble()); - ASSERT_EQ(values[1].time, val3.time()); + REQUIRE(values.size() == 2u); + REQUIRE(values[0].value == val1.GetDouble()); + REQUIRE(values[0].time == val1.time()); + REQUIRE(values[1].value == val3.GetDouble()); + REQUIRE(values[1].time == val3.time()); + + CheckNetworkCounts(network, 1, 1, 0); } -TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest FromNetworkKeepPub", + "[ntcore][local-storage]") { SetupPubSub(true, false); // incoming from the network are treated like a normal local publish @@ -724,18 +740,22 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) { storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated - EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time()); + CHECK(storage.GetEntryLastChange(sub) == val2.time()); storage.ServerSetValue(topic, val3); // verify 2nd update was dropped for local subscriber auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 2u); - ASSERT_EQ(values[0].value, val1.GetDouble()); - ASSERT_EQ(values[0].time, val1.time()); - ASSERT_EQ(values[1].value, val3.GetDouble()); - ASSERT_EQ(values[1].time, val3.time()); + REQUIRE(values.size() == 2u); + REQUIRE(values[0].value == val1.GetDouble()); + REQUIRE(values[0].time == val1.time()); + REQUIRE(values[1].value == val3.GetDouble()); + REQUIRE(values[1].time == val3.time()); + + CheckNetworkCounts(network, 1, 1, 0); } -TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest FromNetworkKeepSub", + "[ntcore][local-storage]") { SetupPubSub(false, true); // incoming from the network are treated like a normal local publish @@ -744,21 +764,25 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) { storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated - EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time()); + CHECK(storage.GetEntryLastChange(sub) == val2.time()); storage.ServerSetValue(topic, val3); // verify 2nd update was received by local subscriber auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 3u); - ASSERT_EQ(values[0].value, val1.GetDouble()); - ASSERT_EQ(values[0].time, val1.time()); - ASSERT_EQ(values[1].value, val2.GetDouble()); - ASSERT_EQ(values[1].time, val2.time()); - ASSERT_EQ(values[2].value, val3.GetDouble()); - ASSERT_EQ(values[2].time, val3.time()); + REQUIRE(values.size() == 3u); + REQUIRE(values[0].value == val1.GetDouble()); + REQUIRE(values[0].time == val1.time()); + REQUIRE(values[1].value == val2.GetDouble()); + REQUIRE(values[1].time == val2.time()); + REQUIRE(values[2].value == val3.GetDouble()); + REQUIRE(values[2].time == val3.time()); + + CheckNetworkCounts(network, 1, 1, 0); } -TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) { +TEST_CASE_METHOD(LocalStorageDuplicatesTest, + "LocalStorageDuplicatesTest FromNetworkKeepPubSub", + "[ntcore][local-storage]") { SetupPubSub(true, true); // incoming from the network are treated like a normal local publish @@ -767,18 +791,20 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) { storage.ServerSetValue(topic, val1); storage.ServerSetValue(topic, val2); // verify the timestamp was updated - EXPECT_EQ(storage.GetEntryLastChange(sub), val2.time()); + CHECK(storage.GetEntryLastChange(sub) == val2.time()); storage.ServerSetValue(topic, val3); // verify 2nd update was received by local subscriber auto values = storage.ReadQueue(sub); - ASSERT_EQ(values.size(), 3u); - ASSERT_EQ(values[0].value, val1.GetDouble()); - ASSERT_EQ(values[0].time, val1.time()); - ASSERT_EQ(values[1].value, val2.GetDouble()); - ASSERT_EQ(values[1].time, val2.time()); - ASSERT_EQ(values[2].value, val3.GetDouble()); - ASSERT_EQ(values[2].time, val3.time()); + REQUIRE(values.size() == 3u); + REQUIRE(values[0].value == val1.GetDouble()); + REQUIRE(values[0].time == val1.time()); + REQUIRE(values[1].value == val2.GetDouble()); + REQUIRE(values[1].time == val2.time()); + REQUIRE(values[2].value == val3.GetDouble()); + REQUIRE(values[2].time == val3.time()); + + CheckNetworkCounts(network, 1, 1, 0); } class LocalStorageNumberVariantsTest : public LocalStorageTest { @@ -809,11 +835,6 @@ void LocalStorageNumberVariantsTest::CreateSubscriber( } void LocalStorageNumberVariantsTest::CreateSubscribers() { - EXPECT_CALL(logger, - Call(NT_LOG_INFO, _, _, - std::string_view{ - "local subscribe to 'foo' disabled due to type " - "mismatch (wanted 'boolean', published as 'double')"})); CreateSubscriber(&sub1, "subDouble", NT_DOUBLE, "double"); CreateSubscriber(&sub2, "subInteger", NT_INTEGER, "int"); CreateSubscriber(&sub3, "subFloat", NT_FLOAT, "float"); @@ -823,12 +844,6 @@ void LocalStorageNumberVariantsTest::CreateSubscribers() { } void LocalStorageNumberVariantsTest::CreateSubscribersArray() { - EXPECT_CALL( - logger, - Call(NT_LOG_INFO, _, _, - std::string_view{ - "local subscribe to 'foo' disabled due to type " - "mismatch (wanted 'boolean[]', published as 'double[]')"})); CreateSubscriber(&sub1, "subDouble", NT_DOUBLE_ARRAY, "double[]"); CreateSubscriber(&sub2, "subInteger", NT_INTEGER_ARRAY, "int[]"); CreateSubscriber(&sub3, "subFloat", NT_FLOAT_ARRAY, "float[]"); @@ -837,160 +852,213 @@ void LocalStorageNumberVariantsTest::CreateSubscribersArray() { subentries.emplace_back(entry, NT_UNASSIGNED, "entry"); } -TEST_F(LocalStorageNumberVariantsTest, GetEntryPubAfter) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(5); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(1); +TEST_CASE_METHOD(LocalStorageNumberVariantsTest, + "LocalStorageNumberVariantsTest GetEntryPubAfter", + "[ntcore][local-storage]") { CreateSubscribers(); auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); // all subscribers get the actual type and time for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); - EXPECT_EQ(storage.GetEntryType(subentry.subentry), NT_DOUBLE); - EXPECT_EQ(storage.GetEntryLastChange(subentry.subentry), 50); + INFO(subentry.name); + CHECK(storage.GetEntryType(subentry.subentry) == NT_DOUBLE); + CHECK(storage.GetEntryLastChange(subentry.subentry) == 50); } // for subscribers, they get a converted value or nothing on mismatch - EXPECT_EQ(storage.GetEntryValue(sub1), Value::MakeDouble(1.0, 50)); - EXPECT_EQ(storage.GetEntryValue(sub2), Value::MakeInteger(1, 50)); - EXPECT_EQ(storage.GetEntryValue(sub3), Value::MakeFloat(1.0, 50)); - EXPECT_EQ(storage.GetEntryValue(sub4), Value{}); + CHECK(storage.GetEntryValue(sub1) == Value::MakeDouble(1.0, 50)); + CHECK(storage.GetEntryValue(sub2) == Value::MakeInteger(1, 50)); + CHECK(storage.GetEntryValue(sub3) == Value::MakeFloat(1.0, 50)); + CHECK(storage.GetEntryValue(sub4) == Value{}); // entries just get whatever the value is - EXPECT_EQ(storage.GetEntryValue(entry), Value::MakeDouble(1.0, 50)); + CHECK(storage.GetEntryValue(entry) == Value::MakeDouble(1.0, 50)); + + CheckNetworkCounts(network, 1, 5, 1); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeDouble(1.0, 50)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted " + "'boolean', published as 'double')"); } -TEST_F(LocalStorageNumberVariantsTest, GetEntryPubBefore) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(5); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(1); +TEST_CASE_METHOD(LocalStorageNumberVariantsTest, + "LocalStorageNumberVariantsTest GetEntryPubBefore", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); CreateSubscribers(); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); // all subscribers get the actual type and time for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); - EXPECT_EQ(storage.GetEntryType(subentry.subentry), NT_DOUBLE); - EXPECT_EQ(storage.GetEntryLastChange(subentry.subentry), 50); + INFO(subentry.name); + CHECK(storage.GetEntryType(subentry.subentry) == NT_DOUBLE); + CHECK(storage.GetEntryLastChange(subentry.subentry) == 50); } // for subscribers, they get a converted value or nothing on mismatch - EXPECT_EQ(storage.GetEntryValue(sub1), Value::MakeDouble(1.0, 50)); - EXPECT_EQ(storage.GetEntryValue(sub2), Value::MakeInteger(1, 50)); - EXPECT_EQ(storage.GetEntryValue(sub3), Value::MakeFloat(1.0, 50)); - EXPECT_EQ(storage.GetEntryValue(sub4), Value{}); + CHECK(storage.GetEntryValue(sub1) == Value::MakeDouble(1.0, 50)); + CHECK(storage.GetEntryValue(sub2) == Value::MakeInteger(1, 50)); + CHECK(storage.GetEntryValue(sub3) == Value::MakeFloat(1.0, 50)); + CHECK(storage.GetEntryValue(sub4) == Value{}); // entries just get whatever the value is - EXPECT_EQ(storage.GetEntryValue(entry), Value::MakeDouble(1.0, 50)); -} + CHECK(storage.GetEntryValue(entry) == Value::MakeDouble(1.0, 50)); -template -::testing::Matcher TSEq(auto value, int64_t time) { - return AllOf(Field("value", &T::value, value), Field("time", &T::time, time)); -} - -TEST_F(LocalStorageNumberVariantsTest, GetAtomic) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(5); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(1); - auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); - CreateSubscribers(); - storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); - - for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); - EXPECT_THAT(storage.GetAtomic(subentry.subentry, 0), - TSEq(1.0, 50)); - EXPECT_THAT(storage.GetAtomic(subentry.subentry, 0), - TSEq(1, 50)); - EXPECT_THAT(storage.GetAtomic(subentry.subentry, 0), - TSEq(1.0, 50)); - EXPECT_THAT(storage.GetAtomic(subentry.subentry, false), - TSEq(false, 0)); - } + CheckNetworkCounts(network, 1, 5, 1); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeDouble(1.0, 50)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted " + "'boolean', published as 'double')"); } template -::testing::Matcher TSSpanEq(std::span value, int64_t time) { - return AllOf( - Field("value", &T::value, wpi::util::SpanEq(std::span(value))), - Field("time", &T::time, time)); +bool TimestampedEq(const T& actual, U value, int64_t time) { + return actual.value == value && actual.time == time; } -TEST_F(LocalStorageNumberVariantsTest, GetAtomicArray) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(5); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(1); +TEST_CASE_METHOD(LocalStorageNumberVariantsTest, + "LocalStorageNumberVariantsTest GetAtomic", + "[ntcore][local-storage]") { + auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); + CreateSubscribers(); + storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); + + for (auto&& subentry : subentries) { + INFO(subentry.name); + CHECK(TimestampedEq(storage.GetAtomic(subentry.subentry, 0), 1.0, + 50)); + CHECK( + TimestampedEq(storage.GetAtomic(subentry.subentry, 0), 1, 50)); + CHECK(TimestampedEq(storage.GetAtomic(subentry.subentry, 0), 1.0f, + 50)); + CHECK(TimestampedEq(storage.GetAtomic(subentry.subentry, false), + false, 0)); + } + + CheckNetworkCounts(network, 1, 5, 1); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeDouble(1.0, 50)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted " + "'boolean', published as 'double')"); +} + +template +bool TimestampedSpanEq(const T& actual, std::span value, int64_t time) { + if (actual.time != time || actual.value.size() != value.size()) { + return false; + } + + using ActualValue = std::remove_cvref_t; + using ExpectedValue = std::remove_cvref_t; + using CommonValue = std::common_type_t; + for (size_t i = 0; i < value.size(); ++i) { + if (static_cast(actual.value[i]) != + static_cast(value[i])) { + return false; + } + } + return true; +} + +template +bool SingleTimestampedEq(const T& actual, U value, int64_t time) { + return actual.size() == 1 && TimestampedEq(actual[0], value, time); +} + +TEST_CASE_METHOD(LocalStorageNumberVariantsTest, + "LocalStorageNumberVariantsTest GetAtomicArray", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE_ARRAY, "double[]", {}, {}); CreateSubscribersArray(); storage.SetEntryValue(pub, Value::MakeDoubleArray({1.0}, 50)); for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); + INFO(subentry.name); double doubleVal = 1.0; - EXPECT_THAT(storage.GetAtomic(subentry.subentry, {}), - TSSpanEq(std::span{&doubleVal, 1}, 50)); + CHECK(TimestampedSpanEq(storage.GetAtomic(subentry.subentry, {}), + std::span{&doubleVal, 1}, 50)); int64_t intVal = 1; - EXPECT_THAT(storage.GetAtomic(subentry.subentry, {}), - TSSpanEq(std::span{&intVal, 1}, 50)); + CHECK(TimestampedSpanEq(storage.GetAtomic(subentry.subentry, {}), + std::span{&intVal, 1}, 50)); float floatVal = 1.0; - EXPECT_THAT(storage.GetAtomic(subentry.subentry, {}), - TSSpanEq(std::span{&floatVal, 1}, 50)); - EXPECT_THAT(storage.GetAtomic(subentry.subentry, {}), - TSSpanEq(std::span{}, 0)); + CHECK(TimestampedSpanEq(storage.GetAtomic(subentry.subentry, {}), + std::span{&floatVal, 1}, 50)); + CHECK(TimestampedSpanEq(storage.GetAtomic(subentry.subentry, {}), + std::span{}, 0)); } + + CheckNetworkCounts(network, 1, 5, 1); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeDoubleArray({1.0}, 50)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted " + "'boolean[]', published as 'double[]')"); } -TEST_F(LocalStorageNumberVariantsTest, ReadQueue) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(5); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(4); +TEST_CASE_METHOD(LocalStorageNumberVariantsTest, + "LocalStorageNumberVariantsTest ReadQueue", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); CreateSubscribers(); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); + INFO(subentry.name); if (subentry.type == NT_BOOLEAN) { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), IsEmpty()); + CHECK(storage.ReadQueue(subentry.subentry).empty()); } else { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), - ElementsAre(TSEq(1.0, 50))); + CHECK(SingleTimestampedEq(storage.ReadQueue(subentry.subentry), + 1.0, 50)); } } storage.SetEntryValue(pub, Value::MakeDouble(2.0, 50)); for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); + INFO(subentry.name); if (subentry.type == NT_BOOLEAN) { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), IsEmpty()); + CHECK(storage.ReadQueue(subentry.subentry).empty()); } else { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), - ElementsAre(TSEq(2, 50))); + CHECK(SingleTimestampedEq(storage.ReadQueue(subentry.subentry), + 2, 50)); } } storage.SetEntryValue(pub, Value::MakeDouble(3.0, 50)); for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); + INFO(subentry.name); if (subentry.type == NT_BOOLEAN) { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), IsEmpty()); + CHECK(storage.ReadQueue(subentry.subentry).empty()); } else { - EXPECT_THAT(storage.ReadQueue(subentry.subentry), - ElementsAre(TSEq(3.0, 50))); + CHECK(SingleTimestampedEq(storage.ReadQueue(subentry.subentry), + 3.0f, 50)); } } storage.SetEntryValue(pub, Value::MakeDouble(4.0, 50)); for (auto&& subentry : subentries) { - SCOPED_TRACE(subentry.name); - EXPECT_THAT(storage.ReadQueue(subentry.subentry), IsEmpty()); + INFO(subentry.name); + CHECK(storage.ReadQueue(subentry.subentry).empty()); } + + CheckNetworkCounts(network, 1, 5, 4); + CheckSetValue(network.setValueCalls[0], Handle{pub}.GetIndex(), + Value::MakeDouble(1.0, 50)); + CheckSetValue(network.setValueCalls[1], Handle{pub}.GetIndex(), + Value::MakeDouble(2.0, 50)); + CheckSetValue(network.setValueCalls[2], Handle{pub}.GetIndex(), + Value::MakeDouble(3.0, 50)); + CheckSetValue(network.setValueCalls[3], Handle{pub}.GetIndex(), + Value::MakeDouble(4.0, 50)); + logger.CheckMessage( + NT_LOG_INFO, + "local subscribe to 'foo' disabled due to type mismatch (wanted " + "'boolean', published as 'double')"); } -TEST_F(LocalStorageTest, MultiSubSpecial) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(2); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(2); - EXPECT_CALL(network, ClientSetValue(_, _)).Times(2); - EXPECT_CALL(listenerStorage, Activate(_, _, _)).Times(2); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest MultiSubSpecial", + "[ntcore][local-storage]") { auto subnormal = storage.SubscribeMultiple({{""}}, {}); auto subspecial = storage.SubscribeMultiple({{"", "$"}}, {}); auto pubnormal = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); @@ -998,43 +1066,37 @@ TEST_F(LocalStorageTest, MultiSubSpecial) { auto pubspecial = storage.Publish(specialTopic, NT_DOUBLE, "double", {}, {}); storage.AddListener(1, subnormal, NT_EVENT_VALUE_ALL); storage.AddListener(2, subspecial, NT_EVENT_VALUE_ALL); - - EXPECT_CALL( - listenerStorage, - Notify(wpi::util::SpanEq(std::span{{2}}), _, _, _, _)); storage.SetEntryValue(pubspecial, Value::MakeDouble(1.0, 30)); - - EXPECT_CALL( - listenerStorage, - Notify(wpi::util::SpanEq(std::span{{1}}), _, _, _, _)); - EXPECT_CALL( - listenerStorage, - Notify(wpi::util::SpanEq(std::span{{2}}), _, _, _, _)); storage.SetEntryValue(pubnormal, Value::MakeDouble(2.0, 40)); + + CheckNetworkCounts(network, 2, 2, 2); + CheckListenerStorageCounts(listenerStorage, + {.activate = 2, .valueNotify = 3}); + CheckValueNotifyHandles(listenerStorage.valueNotifyCalls[0], {2}); + CheckValueNotifyHandles(listenerStorage.valueNotifyCalls[1], {1}); + CheckValueNotifyHandles(listenerStorage.valueNotifyCalls[2], {2}); } -TEST_F(LocalStorageTest, NetworkDuplicateDetect) { - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)); +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest NetworkDuplicateDetect", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); auto remoteTopic = storage.ServerAnnounce( "foo", 0, "double", wpi::util::json::object(), std::nullopt); // local set - EXPECT_CALL(network, ClientSetValue(_, _)); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); // 2nd local set with same value - no SetValue call to network storage.SetEntryValue(pub, Value::MakeDouble(1.0, 60)); // network set with different value storage.ServerSetValue(remoteTopic, Value::MakeDouble(2.0, 70)); // 3rd local set with same value generates a SetValue call to network - EXPECT_CALL(network, ClientSetValue(_, _)); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 80)); + + CheckNetworkCounts(network, 1, 0, 2); } -TEST_F(LocalStorageTest, ReadQueueLocalRemote) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(3); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest ReadQueueLocalRemote", + "[ntcore][local-storage]") { auto subBoth = storage.Subscribe(fooTopic, NT_DOUBLE, "double", DEFAULT_PUB_SUB_OPTIONS); auto subLocal = @@ -1046,27 +1108,22 @@ TEST_F(LocalStorageTest, ReadQueueLocalRemote) { "foo", 0, "double", wpi::util::json::object(), std::nullopt); // local set - EXPECT_CALL(network, ClientSetValue(_, _)); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); - EXPECT_THAT(storage.ReadQueue(subBoth), - ElementsAre(TSEq(1.0, 50))); - EXPECT_THAT(storage.ReadQueue(subLocal), - ElementsAre(TSEq(1.0, 50))); - EXPECT_THAT(storage.ReadQueue(subRemote), IsEmpty()); + CHECK(SingleTimestampedEq(storage.ReadQueue(subBoth), 1.0, 50)); + CHECK(SingleTimestampedEq(storage.ReadQueue(subLocal), 1.0, 50)); + CHECK(storage.ReadQueue(subRemote).empty()); // network set storage.ServerSetValue(remoteTopic, Value::MakeDouble(2.0, 60)); - EXPECT_THAT(storage.ReadQueue(subBoth), - ElementsAre(TSEq(2.0, 60))); - EXPECT_THAT(storage.ReadQueue(subRemote), - ElementsAre(TSEq(2.0, 60))); - EXPECT_THAT(storage.ReadQueue(subLocal), IsEmpty()); + CHECK(SingleTimestampedEq(storage.ReadQueue(subBoth), 2.0, 60)); + CHECK(SingleTimestampedEq(storage.ReadQueue(subRemote), 2.0, 60)); + CHECK(storage.ReadQueue(subLocal).empty()); + + CheckNetworkCounts(network, 1, 3, 1); } -TEST_F(LocalStorageTest, SubExcludePub) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(2); - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)).Times(1); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest SubExcludePub", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); auto subActive = storage.Subscribe(fooTopic, NT_DOUBLE, "double", {}); auto subExclude = storage.Subscribe(fooTopic, NT_DOUBLE, "double", @@ -1075,45 +1132,38 @@ TEST_F(LocalStorageTest, SubExcludePub) { "foo", 0, "double", wpi::util::json::object(), std::nullopt); // local set - EXPECT_CALL(network, ClientSetValue(_, _)); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); - EXPECT_THAT(storage.ReadQueue(subActive), - ElementsAre(TSEq(1.0, 50))); - EXPECT_THAT(storage.ReadQueue(subExclude), IsEmpty()); + CHECK(SingleTimestampedEq(storage.ReadQueue(subActive), 1.0, 50)); + CHECK(storage.ReadQueue(subExclude).empty()); // network set storage.ServerSetValue(remoteTopic, Value::MakeDouble(2.0, 60)); - EXPECT_THAT(storage.ReadQueue(subActive), - ElementsAre(TSEq(2.0, 60))); - EXPECT_THAT(storage.ReadQueue(subExclude), - ElementsAre(TSEq(2.0, 60))); + CHECK(SingleTimestampedEq(storage.ReadQueue(subActive), 2.0, 60)); + CHECK(SingleTimestampedEq(storage.ReadQueue(subExclude), 2.0, 60)); + + CheckNetworkCounts(network, 1, 2, 1); } -TEST_F(LocalStorageTest, EntryExcludeSelf) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest EntryExcludeSelf", + "[ntcore][local-storage]") { auto entry = storage.GetEntry(fooTopic, NT_DOUBLE, "double", {.excludeSelf = true}); auto remoteTopic = storage.ServerAnnounce( "foo", 0, "double", wpi::util::json::object(), std::nullopt); // local set - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)); - EXPECT_CALL(network, ClientSetValue(_, _)); storage.SetEntryValue(entry, Value::MakeDouble(1.0, 50)); - EXPECT_THAT(storage.ReadQueue(entry), IsEmpty()); + CHECK(storage.ReadQueue(entry).empty()); // network set storage.ServerSetValue(remoteTopic, Value::MakeDouble(2.0, 60)); - EXPECT_THAT(storage.ReadQueue(entry), - ElementsAre(TSEq(2.0, 60))); + CHECK(SingleTimestampedEq(storage.ReadQueue(entry), 2.0, 60)); + + CheckNetworkCounts(network, 1, 1, 1); } -TEST_F(LocalStorageTest, ReadQueueInitialLocal) { - EXPECT_CALL(network, ClientPublish(_, _, _, _, _)); - EXPECT_CALL(network, ClientSetValue(_, _)); - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(3); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest ReadQueueInitialLocal", + "[ntcore][local-storage]") { auto pub = storage.Publish(fooTopic, NT_DOUBLE, "double", {}, {}); storage.SetEntryValue(pub, Value::MakeDouble(1.0, 50)); @@ -1124,16 +1174,15 @@ TEST_F(LocalStorageTest, ReadQueueInitialLocal) { auto subRemote = storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableLocal = true}); - EXPECT_THAT(storage.ReadQueue(subBoth), - ElementsAre(TSEq(1.0, 50))); - EXPECT_THAT(storage.ReadQueue(subLocal), - ElementsAre(TSEq(1.0, 50))); - EXPECT_THAT(storage.ReadQueue(subRemote), IsEmpty()); + CHECK(SingleTimestampedEq(storage.ReadQueue(subBoth), 1.0, 50)); + CHECK(SingleTimestampedEq(storage.ReadQueue(subLocal), 1.0, 50)); + CHECK(storage.ReadQueue(subRemote).empty()); + + CheckNetworkCounts(network, 1, 3, 1); } -TEST_F(LocalStorageTest, ReadQueueInitialRemote) { - EXPECT_CALL(network, ClientSubscribe(_, _, _)).Times(3); - +TEST_CASE_METHOD(LocalStorageTest, "LocalStorageTest ReadQueueInitialRemote", + "[ntcore][local-storage]") { auto remoteTopic = storage.ServerAnnounce( "foo", 0, "double", wpi::util::json::object(), std::nullopt); storage.ServerSetValue(remoteTopic, Value::MakeDouble(2.0, 60)); @@ -1146,11 +1195,11 @@ TEST_F(LocalStorageTest, ReadQueueInitialRemote) { storage.Subscribe(fooTopic, NT_DOUBLE, "double", {.disableLocal = true}); // network set - EXPECT_THAT(storage.ReadQueue(subBoth), - ElementsAre(TSEq(2.0, 60))); - EXPECT_THAT(storage.ReadQueue(subRemote), - ElementsAre(TSEq(2.0, 60))); - EXPECT_THAT(storage.ReadQueue(subLocal), IsEmpty()); + CHECK(SingleTimestampedEq(storage.ReadQueue(subBoth), 2.0, 60)); + CHECK(SingleTimestampedEq(storage.ReadQueue(subRemote), 2.0, 60)); + CHECK(storage.ReadQueue(subLocal).empty()); + + CheckNetworkCounts(network, 0, 3, 0); } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/LoggerTest.cpp b/ntcore/src/test/native/cpp/LoggerTest.cpp index 8b915c2e7a..327398dcbf 100644 --- a/ntcore/src/test/native/cpp/LoggerTest.cpp +++ b/ntcore/src/test/native/cpp/LoggerTest.cpp @@ -4,18 +4,18 @@ #include -#include +#include #include "Handle.hpp" #include "TestPrinters.hpp" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/Synchronization.h" -class LoggerTest : public ::testing::Test { +class LoggerTest { public: LoggerTest() : m_inst(wpi::nt::CreateInstance()) {} - ~LoggerTest() override { wpi::nt::DestroyInstance(m_inst); } + ~LoggerTest() { wpi::nt::DestroyInstance(m_inst); } void Generate(); void Check(const std::vector& events, NT_Listener handle, @@ -38,27 +38,27 @@ void LoggerTest::Generate() { void LoggerTest::Check(const std::vector& events, NT_Listener handle, bool infoMsg, bool errMsg) { size_t count = (infoMsg ? 1u : 0u) + (errMsg ? 1u : 0u); - ASSERT_EQ(events.size(), count); + REQUIRE(events.size() == count); for (size_t i = 0; i < count; ++i) { - ASSERT_EQ(events[i].listener, handle); - ASSERT_EQ(events[i].flags & wpi::nt::EventFlags::LOG_MESSAGE, - wpi::nt::EventFlags::LOG_MESSAGE); + REQUIRE(events[i].listener == handle); + REQUIRE((events[i].flags & wpi::nt::EventFlags::LOG_MESSAGE) == + wpi::nt::EventFlags::LOG_MESSAGE); auto log = events[i].GetLogMessage(); - ASSERT_TRUE(log); + REQUIRE(log); if (infoMsg) { - ASSERT_EQ(log->message, "starting network client"); - ASSERT_EQ(log->level, NT_LOG_INFO); + REQUIRE(log->message == "starting network client"); + REQUIRE(log->level == NT_LOG_INFO); infoMsg = false; } else if (errMsg) { - ASSERT_EQ(log->message, - "trying to publish invalid topic handle (386924549)"); - ASSERT_EQ(log->level, NT_LOG_ERROR); + REQUIRE(log->message == + "trying to publish invalid topic handle (386924549)"); + REQUIRE(log->level == NT_LOG_ERROR); errMsg = false; } } } -TEST_F(LoggerTest, DefaultLogRange) { +TEST_CASE_METHOD(LoggerTest, "LoggerTest DefaultLogRange", "[ntcore][logger]") { auto poller = wpi::nt::CreateListenerPoller(m_inst); auto handle = wpi::nt::AddPolledListener(poller, m_inst, wpi::nt::EventFlags::LOG_MESSAGE); @@ -66,33 +66,33 @@ TEST_F(LoggerTest, DefaultLogRange) { Generate(); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); Check(events, handle, true, true); } -TEST_F(LoggerTest, InfoOnly) { +TEST_CASE_METHOD(LoggerTest, "LoggerTest InfoOnly", "[ntcore][logger]") { auto poller = wpi::nt::CreateListenerPoller(m_inst); auto handle = wpi::nt::AddPolledLogger(poller, NT_LOG_INFO, NT_LOG_INFO); Generate(); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); Check(events, handle, true, false); } -TEST_F(LoggerTest, Error) { +TEST_CASE_METHOD(LoggerTest, "LoggerTest Error", "[ntcore][logger]") { auto poller = wpi::nt::CreateListenerPoller(m_inst); auto handle = wpi::nt::AddPolledLogger(poller, NT_LOG_ERROR, 100); Generate(); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); Check(events, handle, false, true); diff --git a/ntcore/src/test/native/cpp/MockAssertions.hpp b/ntcore/src/test/native/cpp/MockAssertions.hpp new file mode 100644 index 0000000000..b162a7a01f --- /dev/null +++ b/ntcore/src/test/native/cpp/MockAssertions.hpp @@ -0,0 +1,191 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "MockListenerStorage.hpp" +#include "PubSubOptionsMatcher.hpp" +#include "TestPrinters.hpp" +#include "net/MockMessageHandler.hpp" +#include "wpi/nt/ntcore_c.h" +#include "wpi/util/json.hpp" + +namespace wpi::nt { + +inline void CheckPublish(const net::MockClientMessageHandler::PublishCall& call, + std::string_view name, std::string_view typeStr, + const wpi::util::json& properties, + const PubSubOptionsImpl& options = {}) { + CHECK(call.name == name); + CHECK(call.typeStr == typeStr); + CHECK(call.properties == properties); + CHECK(call.options == options); +} + +inline void CheckPublish(const net::MockClientMessageHandler::PublishCall& call, + int pubuid, std::string_view name, + std::string_view typeStr, + const wpi::util::json& properties, + const PubSubOptionsImpl& options = {}) { + CHECK(call.pubuid == pubuid); + CheckPublish(call, name, typeStr, properties, options); +} + +inline void CheckSubscribe( + const net::MockClientMessageHandler::SubscribeCall& call, + std::initializer_list prefixes, + const PubSubOptionsImpl& options = {}) { + REQUIRE(call.prefixes.size() == prefixes.size()); + auto prefixIt = prefixes.begin(); + for (const auto& prefix : call.prefixes) { + CHECK(prefix == *prefixIt); + ++prefixIt; + } + CHECK(call.options == options); +} + +inline void CheckSetValue( + const net::MockClientMessageHandler::SetValueCall& call, int pubuid, + const Value& value) { + CHECK(call.pubuid == pubuid); + CHECK(call.value == value); +} + +struct ClientMessageCounts { + size_t publish = 0; + size_t unpublish = 0; + size_t setProperties = 0; + size_t subscribe = 0; + size_t unsubscribe = 0; + size_t setValue = 0; +}; + +inline void CheckClientMessageCounts( + const net::MockClientMessageHandler& handler, + const ClientMessageCounts& expected = {}) { + REQUIRE(handler.calls.size() == + expected.publish + expected.unpublish + expected.setProperties + + expected.subscribe + expected.unsubscribe + expected.setValue); + REQUIRE(handler.publishCalls.size() == expected.publish); + REQUIRE(handler.unpublishCalls.size() == expected.unpublish); + REQUIRE(handler.setPropertiesCalls.size() == expected.setProperties); + REQUIRE(handler.subscribeCalls.size() == expected.subscribe); + REQUIRE(handler.unsubscribeCalls.size() == expected.unsubscribe); + REQUIRE(handler.setValueCalls.size() == expected.setValue); +} + +inline void CheckNoClientCalls(const net::MockClientMessageHandler& handler) { + CheckClientMessageCounts(handler); +} + +struct ServerMessageCounts { + size_t announce = 0; + size_t unannounce = 0; + size_t propertiesUpdate = 0; + size_t setValue = 0; +}; + +inline void CheckServerMessageCounts( + const net::MockServerMessageHandler& handler, + const ServerMessageCounts& expected = {}) { + REQUIRE(handler.calls.size() == expected.announce + expected.unannounce + + expected.propertiesUpdate + + expected.setValue); + REQUIRE(handler.announceCalls.size() == expected.announce); + REQUIRE(handler.unannounceCalls.size() == expected.unannounce); + REQUIRE(handler.propertiesUpdateCalls.size() == expected.propertiesUpdate); + REQUIRE(handler.setValueCalls.size() == expected.setValue); +} + +inline void CheckNoServerCalls(const net::MockServerMessageHandler& handler) { + CheckServerMessageCounts(handler); +} + +struct ListenerStorageCounts { + size_t activate = 0; + size_t connectionNotify = 0; + size_t topicNotify = 0; + size_t valueNotify = 0; + size_t logNotify = 0; + size_t timeSyncNotify = 0; +}; + +inline void CheckListenerStorageCounts( + const MockListenerStorage& listenerStorage, + const ListenerStorageCounts& expected = {}) { + REQUIRE(listenerStorage.activateCalls.size() == expected.activate); + REQUIRE(listenerStorage.connectionNotifyCalls.size() == + expected.connectionNotify); + REQUIRE(listenerStorage.topicNotifyCalls.size() == expected.topicNotify); + REQUIRE(listenerStorage.valueNotifyCalls.size() == expected.valueNotify); + REQUIRE(listenerStorage.logNotifyCalls.size() == expected.logNotify); + REQUIRE(listenerStorage.timeSyncNotifyCalls.size() == + expected.timeSyncNotify); +} + +inline void CheckValueNotifyHandles( + const MockListenerStorage::ValueNotifyCall& call, + std::initializer_list expectedHandles) { + REQUIRE(call.handles.size() == expectedHandles.size()); + CHECK(std::equal(call.handles.begin(), call.handles.end(), + expectedHandles.begin(), expectedHandles.end())); +} + +inline void CheckNetworkCounts(const net::MockClientMessageHandler& network, + size_t publishCount, size_t subscribeCount, + size_t setValueCount, size_t unpublishCount = 0, + size_t setPropertiesCount = 0, + size_t unsubscribeCount = 0) { + CheckClientMessageCounts(network, { + .publish = publishCount, + .unpublish = unpublishCount, + .setProperties = setPropertiesCount, + .subscribe = subscribeCount, + .unsubscribe = unsubscribeCount, + .setValue = setValueCount, + }); + + std::vector activePubs; + std::vector activeSubs; + for (const auto& call : network.calls) { + if (auto publish = + std::get_if(&call)) { + activePubs.emplace_back(publish->pubuid); + } else if (auto unpublish = + std::get_if( + &call)) { + int pubuid = unpublish->pubuid; + CHECK(std::find(activePubs.begin(), activePubs.end(), pubuid) != + activePubs.end()); + std::erase(activePubs, pubuid); + } else if (auto subscribe = + std::get_if( + &call)) { + activeSubs.emplace_back(subscribe->subuid); + } else if (auto unsubscribe = + std::get_if( + &call)) { + int subuid = unsubscribe->subuid; + CHECK(std::find(activeSubs.begin(), activeSubs.end(), subuid) != + activeSubs.end()); + std::erase(activeSubs, subuid); + } else if (auto setValue = + std::get_if( + &call)) { + int pubuid = setValue->pubuid; + CHECK(std::find(activePubs.begin(), activePubs.end(), pubuid) != + activePubs.end()); + } + } +} + +} // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/MockConnectionList.hpp b/ntcore/src/test/native/cpp/MockConnectionList.hpp index 837526208a..e3975d889a 100644 --- a/ntcore/src/test/native/cpp/MockConnectionList.hpp +++ b/ntcore/src/test/native/cpp/MockConnectionList.hpp @@ -4,21 +4,43 @@ #pragma once +#include #include #include "IConnectionList.hpp" -#include "gmock/gmock.h" namespace wpi::nt { class MockConnectionList : public IConnectionList { public: - MOCK_METHOD(int, AddConnection, (const ConnectionInfo& info), (override)); - MOCK_METHOD(void, RemoveConnection, (int handle), (override)); - MOCK_METHOD(void, ClearConnections, (), (override)); - MOCK_METHOD(std::vector, GetConnections, (), - (const, override)); - MOCK_METHOD(bool, IsConnected, (), (const, override)); + int AddConnection(const ConnectionInfo& info) override { + addConnectionCalls.emplace_back(info); + if (!addConnectionReturns.empty()) { + int rv = addConnectionReturns.front(); + addConnectionReturns.pop_front(); + return rv; + } + return static_cast(addConnectionCalls.size()); + } + + void RemoveConnection(int handle) override { + removeConnectionCalls.emplace_back(handle); + } + + void ClearConnections() override { ++clearConnectionsCalls; } + + std::vector GetConnections() const override { + return connections; + } + + bool IsConnected() const override { return connected; } + + std::deque addConnectionReturns; + std::vector addConnectionCalls; + std::vector removeConnectionCalls; + int clearConnectionsCalls = 0; + std::vector connections; + bool connected = false; }; } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/MockListenerStorage.hpp b/ntcore/src/test/native/cpp/MockListenerStorage.hpp index 8a5e081c98..3e1ecd2f14 100644 --- a/ntcore/src/test/native/cpp/MockListenerStorage.hpp +++ b/ntcore/src/test/native/cpp/MockListenerStorage.hpp @@ -6,40 +6,108 @@ #include #include +#include #include +#include +#include #include "IListenerStorage.hpp" -#include "gmock/gmock.h" namespace wpi::nt { class MockListenerStorage : public IListenerStorage { public: - MOCK_METHOD(void, Activate, - (NT_Listener listenerHandle, unsigned int mask, - FinishEventFunc finishEvent), - (override)); - MOCK_METHOD(void, Notify, - (std::span handles, unsigned int flags, - std::span infos), - (override)); - MOCK_METHOD(void, Notify, - (std::span handles, unsigned int flags, - std::span infos), - (override)); - MOCK_METHOD(void, Notify, - (std::span handles, unsigned int flags, - NT_Topic topic, NT_Handle subentry, const Value& value), - (override)); - MOCK_METHOD(void, Notify, - (unsigned int flags, unsigned int level, - std::string_view filename, unsigned int line, - std::string_view message), - (override)); - MOCK_METHOD(void, NotifyTimeSync, - (std::span handles, unsigned int flags, - int64_t serverTimeOffset, int64_t rtt2, bool valid), - (override)); + struct ActivateCall { + NT_Listener listenerHandle; + unsigned int mask; + FinishEventFunc finishEvent; + }; + + struct ConnectionNotifyCall { + std::vector handles; + unsigned int flags; + std::vector infos; + }; + + struct TopicNotifyCall { + std::vector handles; + unsigned int flags; + std::vector infos; + }; + + struct ValueNotifyCall { + std::vector handles; + unsigned int flags; + NT_Topic topic; + NT_Handle subentry; + Value value; + }; + + struct LogNotifyCall { + unsigned int flags; + unsigned int level; + std::string filename; + unsigned int line; + std::string message; + }; + + struct TimeSyncNotifyCall { + std::vector handles; + unsigned int flags; + int64_t serverTimeOffset; + int64_t rtt2; + bool valid; + }; + + void Activate(NT_Listener listenerHandle, unsigned int mask, + FinishEventFunc finishEvent = {}) override { + activateCalls.emplace_back(listenerHandle, mask, std::move(finishEvent)); + } + + void Notify(std::span handles, unsigned int flags, + std::span infos) override { + auto& call = connectionNotifyCalls.emplace_back(); + call.handles.assign(handles.begin(), handles.end()); + call.flags = flags; + for (auto info : infos) { + call.infos.emplace_back(*info); + } + } + + void Notify(std::span handles, unsigned int flags, + std::span infos) override { + topicNotifyCalls.emplace_back( + std::vector{handles.begin(), handles.end()}, flags, + std::vector{infos.begin(), infos.end()}); + } + + void Notify(std::span handles, unsigned int flags, + NT_Topic topic, NT_Handle subentry, const Value& value) override { + valueNotifyCalls.emplace_back( + std::vector{handles.begin(), handles.end()}, flags, topic, + subentry, value); + } + + void Notify(unsigned int flags, unsigned int level, std::string_view filename, + unsigned int line, std::string_view message) override { + logNotifyCalls.emplace_back(flags, level, std::string{filename}, line, + std::string{message}); + } + + void NotifyTimeSync(std::span handles, unsigned int flags, + int64_t serverTimeOffset, int64_t rtt2, + bool valid) override { + timeSyncNotifyCalls.emplace_back( + std::vector{handles.begin(), handles.end()}, flags, + serverTimeOffset, rtt2, valid); + } + + std::vector activateCalls; + std::vector connectionNotifyCalls; + std::vector topicNotifyCalls; + std::vector valueNotifyCalls; + std::vector logNotifyCalls; + std::vector timeSyncNotifyCalls; }; } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/MockLogger.hpp b/ntcore/src/test/native/cpp/MockLogger.hpp index 9aeeee31a9..04694ec836 100644 --- a/ntcore/src/test/native/cpp/MockLogger.hpp +++ b/ntcore/src/test/native/cpp/MockLogger.hpp @@ -4,20 +4,54 @@ #pragma once -#include "gmock/gmock.h" +#include +#include +#include +#include + +#include + #include "wpi/util/Logger.hpp" namespace wpi { -class MockLogger : public wpi::util::Logger, - public ::testing::MockFunction { +class MockLogger : public wpi::util::Logger { public: + struct Message { + unsigned int level; + std::string file; + unsigned int line; + std::string msg; + }; + MockLogger() { SetLogger([this](unsigned int level, const char* file, unsigned int line, - const char* msg) { Call(level, file, line, msg); }); + const char* msg) { + messages.emplace_back(level, file, line, msg); + }); } + + struct ExpectedMessage { + unsigned int level; + std::string_view msg; + }; + + void CheckMessages(std::initializer_list expected) const { + REQUIRE(messages.size() == expected.size()); + + auto expectedIt = expected.begin(); + for (const auto& message : messages) { + CHECK(message.level == expectedIt->level); + CHECK(std::string_view{message.msg} == expectedIt->msg); + ++expectedIt; + } + } + + void CheckMessage(unsigned int level, std::string_view expected) const { + CheckMessages({{level, expected}}); + } + + std::vector messages; }; } // namespace wpi diff --git a/ntcore/src/test/native/cpp/NetworkServerTest.cpp b/ntcore/src/test/native/cpp/NetworkServerTest.cpp index 0424230532..4b53558ab1 100644 --- a/ntcore/src/test/native/cpp/NetworkServerTest.cpp +++ b/ntcore/src/test/native/cpp/NetworkServerTest.cpp @@ -8,13 +8,14 @@ #include #include -#include +#include +#include #include "wpi/nt/IntegerTopic.hpp" #include "wpi/nt/NetworkTableInstance.hpp" // Valid persistent JSON containing a single persistent integer topic. -static constexpr const char* kPersistentJson = R"([ +static constexpr const char* PERSISTENT_JSON = R"([ { "name": "/test/persistent_value", "type": "int", @@ -23,7 +24,12 @@ static constexpr const char* kPersistentJson = R"([ } ])"; -class NetworkServerPersistentTest : public ::testing::Test { +static constexpr unsigned int RESTORE_BACKUP_PORT = 10040; +static constexpr unsigned int NORMAL_LOAD_PORT = 10041; +static constexpr unsigned int ORIGINAL_OVER_BACKUP_PORT = 10042; +static constexpr unsigned int NO_FILE_PORT = 10043; + +class NetworkServerPersistentTest { public: NetworkServerPersistentTest() { // Create a unique temp directory for each test @@ -36,7 +42,7 @@ class NetworkServerPersistentTest : public ::testing::Test { m_persistFile = (m_tempDir / "test_persistent.json").string(); } - ~NetworkServerPersistentTest() override { + ~NetworkServerPersistentTest() { std::error_code ec; std::filesystem::remove_all(m_tempDir, ec); } @@ -45,7 +51,8 @@ class NetworkServerPersistentTest : public ::testing::Test { // Write content to a file. static void WriteFile(const std::string& path, const std::string& content) { std::ofstream os{path}; - ASSERT_TRUE(os.is_open()) << "Failed to create file: " << path; + UNSCOPED_INFO("Failed to create file: " << path); + REQUIRE(os.is_open()); os << content; } @@ -73,13 +80,15 @@ class NetworkServerPersistentTest : public ::testing::Test { // original persistent file is missing. This simulates SavePersistent being // interrupted after renaming the original file to .bck but before the // temporary file has been renamed to the original filename. -TEST_F(NetworkServerPersistentTest, - LoadPersistentRestoresFromBackupWhenOriginalMissing) { +TEST_CASE_METHOD(NetworkServerPersistentTest, + "NetworkServerPersistentTest " + "LoadPersistentRestoresFromBackupWhenOriginalMissing", + "[ntcore][network-server]") { // Set up "interrupted" state: only .bck file exists, no original. std::string backupFile = m_persistFile + ".bck"; - WriteFile(backupFile, kPersistentJson); - ASSERT_TRUE(std::filesystem::exists(backupFile)); - ASSERT_FALSE(std::filesystem::exists(m_persistFile)); + WriteFile(backupFile, PERSISTENT_JSON); + REQUIRE(std::filesystem::exists(backupFile)); + REQUIRE_FALSE(std::filesystem::exists(m_persistFile)); // Start a server that references the (missing) persistent file. // Subscribe BEFORE starting the server so the server's local client has a @@ -87,17 +96,17 @@ TEST_F(NetworkServerPersistentTest, auto inst = wpi::nt::NetworkTableInstance::Create(); wpi::nt::IntegerSubscriber sub = inst.GetIntegerTopic("/test/persistent_value").Subscribe(0); - inst.StartServer(m_persistFile, "127.0.0.1"); + inst.StartServer(m_persistFile, "127.0.0.1", "", RESTORE_BACKUP_PORT); // Wait for the persistent topic to appear. - EXPECT_TRUE(WaitForTopic(inst, "/test/persistent_value")) - << "LoadPersistent did not restore from the .bck backup file"; + UNSCOPED_INFO("LoadPersistent did not restore from the .bck backup file"); + CHECK(WaitForTopic(inst, "/test/persistent_value")); // Also verify the value is correct. - EXPECT_EQ(sub.Get(), 42); + CHECK(sub.Get() == 42); // The .bck should have been renamed to the original filename. - EXPECT_TRUE(std::filesystem::exists(m_persistFile)); + CHECK(std::filesystem::exists(m_persistFile)); inst.StopServer(); wpi::nt::NetworkTableInstance::Destroy(inst); @@ -105,20 +114,22 @@ TEST_F(NetworkServerPersistentTest, // Verify that LoadPersistent works normally when the original persistent file // is present (no interruption scenario). -TEST_F(NetworkServerPersistentTest, LoadPersistentNormalLoad) { +TEST_CASE_METHOD(NetworkServerPersistentTest, + "NetworkServerPersistentTest LoadPersistentNormalLoad", + "[ntcore][network-server]") { // Write the persistent file directly (no backup). - WriteFile(m_persistFile, kPersistentJson); - ASSERT_TRUE(std::filesystem::exists(m_persistFile)); + WriteFile(m_persistFile, PERSISTENT_JSON); + REQUIRE(std::filesystem::exists(m_persistFile)); auto inst = wpi::nt::NetworkTableInstance::Create(); wpi::nt::IntegerSubscriber sub = inst.GetIntegerTopic("/test/persistent_value").Subscribe(0); - inst.StartServer(m_persistFile, "127.0.0.1"); + inst.StartServer(m_persistFile, "127.0.0.1", "", NORMAL_LOAD_PORT); - EXPECT_TRUE(WaitForTopic(inst, "/test/persistent_value")) - << "LoadPersistent did not load the persistent file"; + UNSCOPED_INFO("LoadPersistent did not load the persistent file"); + CHECK(WaitForTopic(inst, "/test/persistent_value")); - EXPECT_EQ(sub.Get(), 42); + CHECK(sub.Get() == 42); inst.StopServer(); wpi::nt::NetworkTableInstance::Destroy(inst); @@ -126,9 +137,12 @@ TEST_F(NetworkServerPersistentTest, LoadPersistentNormalLoad) { // Verify that when both the original file and .bck exist, the original file // takes precedence (the backup is not used). -TEST_F(NetworkServerPersistentTest, LoadPersistentPrefersOriginalOverBackup) { +TEST_CASE_METHOD( + NetworkServerPersistentTest, + "NetworkServerPersistentTest LoadPersistentPrefersOriginalOverBackup", + "[ntcore][network-server]") { // Original file with value 100. - static constexpr const char* kOriginalJson = R"([ + static constexpr const char* ORIGINAL_JSON = R"([ { "name": "/test/persistent_value", "type": "int", @@ -138,21 +152,21 @@ TEST_F(NetworkServerPersistentTest, LoadPersistentPrefersOriginalOverBackup) { ])"; // Backup file with a different value (42). - WriteFile(m_persistFile, kOriginalJson); - WriteFile(m_persistFile + ".bck", kPersistentJson); - ASSERT_TRUE(std::filesystem::exists(m_persistFile)); - ASSERT_TRUE(std::filesystem::exists(m_persistFile + ".bck")); + WriteFile(m_persistFile, ORIGINAL_JSON); + WriteFile(m_persistFile + ".bck", PERSISTENT_JSON); + REQUIRE(std::filesystem::exists(m_persistFile)); + REQUIRE(std::filesystem::exists(m_persistFile + ".bck")); auto inst = wpi::nt::NetworkTableInstance::Create(); wpi::nt::IntegerSubscriber sub = inst.GetIntegerTopic("/test/persistent_value").Subscribe(0); - inst.StartServer(m_persistFile, "127.0.0.1"); + inst.StartServer(m_persistFile, "127.0.0.1", "", ORIGINAL_OVER_BACKUP_PORT); - EXPECT_TRUE(WaitForTopic(inst, "/test/persistent_value")) - << "LoadPersistent did not load any persistent file"; + UNSCOPED_INFO("LoadPersistent did not load any persistent file"); + CHECK(WaitForTopic(inst, "/test/persistent_value")); // The value should come from the original (100), not the backup (42). - EXPECT_EQ(sub.Get(), 100); + CHECK(sub.Get() == 100); inst.StopServer(); wpi::nt::NetworkTableInstance::Destroy(inst); @@ -160,19 +174,21 @@ TEST_F(NetworkServerPersistentTest, LoadPersistentPrefersOriginalOverBackup) { // Verify that LoadPersistent handles a missing persistent file and no backup // gracefully (no crash, no topics loaded). -TEST_F(NetworkServerPersistentTest, LoadPersistentNoFile) { - ASSERT_FALSE(std::filesystem::exists(m_persistFile)); - ASSERT_FALSE(std::filesystem::exists(m_persistFile + ".bck")); +TEST_CASE_METHOD(NetworkServerPersistentTest, + "NetworkServerPersistentTest LoadPersistentNoFile", + "[ntcore][network-server]") { + REQUIRE_FALSE(std::filesystem::exists(m_persistFile)); + REQUIRE_FALSE(std::filesystem::exists(m_persistFile + ".bck")); auto inst = wpi::nt::NetworkTableInstance::Create(); - inst.StartServer(m_persistFile, "127.0.0.1"); + inst.StartServer(m_persistFile, "127.0.0.1", "", NO_FILE_PORT); // Give the server time to initialize. std::this_thread::sleep_for(std::chrono::milliseconds{500}); // No persistent topics should exist. auto infos = inst.GetTopicInfo("/test/persistent_value"); - EXPECT_TRUE(infos.empty()); + CHECK(infos.empty()); inst.StopServer(); wpi::nt::NetworkTableInstance::Destroy(inst); diff --git a/ntcore/src/test/native/cpp/NetworkTableTest.cpp b/ntcore/src/test/native/cpp/NetworkTableTest.cpp index 95b3defb9c..fb3acf54c9 100644 --- a/ntcore/src/test/native/cpp/NetworkTableTest.cpp +++ b/ntcore/src/test/native/cpp/NetworkTableTest.cpp @@ -7,104 +7,102 @@ #include #include -#include +#include #include "TestPrinters.hpp" #include "wpi/nt/NetworkTableInstance.hpp" -class NetworkTableTest : public ::testing::Test {}; - -TEST_F(NetworkTableTest, BasenameKey) { - EXPECT_EQ("simple", wpi::nt::NetworkTable::BasenameKey("simple")); - EXPECT_EQ("simple", - wpi::nt::NetworkTable::BasenameKey("one/two/many/simple")); - EXPECT_EQ("simple", wpi::nt::NetworkTable::BasenameKey( - "//////an/////awful/key////simple")); +TEST_CASE("NetworkTableTest BasenameKey", "[ntcore][network-table]") { + CHECK("simple" == wpi::nt::NetworkTable::BasenameKey("simple")); + CHECK("simple" == wpi::nt::NetworkTable::BasenameKey("one/two/many/simple")); + CHECK("simple" == + wpi::nt::NetworkTable::BasenameKey("//////an/////awful/key////simple")); } -TEST_F(NetworkTableTest, NormalizeKeySlash) { - EXPECT_EQ("/", wpi::nt::NetworkTable::NormalizeKey("///")); - EXPECT_EQ("/no/normal/req", - wpi::nt::NetworkTable::NormalizeKey("/no/normal/req")); - EXPECT_EQ("/no/leading/slash", - wpi::nt::NetworkTable::NormalizeKey("no/leading/slash")); - EXPECT_EQ("/what/an/awful/key/", wpi::nt::NetworkTable::NormalizeKey( - "//////what////an/awful/////key///")); +TEST_CASE("NetworkTableTest NormalizeKeySlash", "[ntcore][network-table]") { + CHECK("/" == wpi::nt::NetworkTable::NormalizeKey("///")); + CHECK("/no/normal/req" == + wpi::nt::NetworkTable::NormalizeKey("/no/normal/req")); + CHECK("/no/leading/slash" == + wpi::nt::NetworkTable::NormalizeKey("no/leading/slash")); + CHECK("/what/an/awful/key/" == wpi::nt::NetworkTable::NormalizeKey( + "//////what////an/awful/////key///")); } -TEST_F(NetworkTableTest, NormalizeKeyNoSlash) { - EXPECT_EQ("a", wpi::nt::NetworkTable::NormalizeKey("a", false)); - EXPECT_EQ("a", wpi::nt::NetworkTable::NormalizeKey("///a", false)); - EXPECT_EQ("leading/slash", - wpi::nt::NetworkTable::NormalizeKey("/leading/slash", false)); - EXPECT_EQ("no/leading/slash", - wpi::nt::NetworkTable::NormalizeKey("no/leading/slash", false)); - EXPECT_EQ("what/an/awful/key/", - wpi::nt::NetworkTable::NormalizeKey( - "//////what////an/awful/////key///", false)); +TEST_CASE("NetworkTableTest NormalizeKeyNoSlash", "[ntcore][network-table]") { + CHECK("a" == wpi::nt::NetworkTable::NormalizeKey("a", false)); + CHECK("a" == wpi::nt::NetworkTable::NormalizeKey("///a", false)); + CHECK("leading/slash" == + wpi::nt::NetworkTable::NormalizeKey("/leading/slash", false)); + CHECK("no/leading/slash" == + wpi::nt::NetworkTable::NormalizeKey("no/leading/slash", false)); + CHECK("what/an/awful/key/" == + wpi::nt::NetworkTable::NormalizeKey("//////what////an/awful/////key///", + false)); } -TEST_F(NetworkTableTest, GetHierarchyEmpty) { +TEST_CASE("NetworkTableTest GetHierarchyEmpty", "[ntcore][network-table]") { std::vector expected{"/"}; - ASSERT_EQ(expected, wpi::nt::NetworkTable::GetHierarchy("")); + REQUIRE(expected == wpi::nt::NetworkTable::GetHierarchy("")); } -TEST_F(NetworkTableTest, GetHierarchyRoot) { +TEST_CASE("NetworkTableTest GetHierarchyRoot", "[ntcore][network-table]") { std::vector expected{"/"}; - ASSERT_EQ(expected, wpi::nt::NetworkTable::GetHierarchy("/")); + REQUIRE(expected == wpi::nt::NetworkTable::GetHierarchy("/")); } -TEST_F(NetworkTableTest, GetHierarchyNormal) { +TEST_CASE("NetworkTableTest GetHierarchyNormal", "[ntcore][network-table]") { std::vector expected{"/", "/foo", "/foo/bar", "/foo/bar/baz"}; - ASSERT_EQ(expected, wpi::nt::NetworkTable::GetHierarchy("/foo/bar/baz")); + REQUIRE(expected == wpi::nt::NetworkTable::GetHierarchy("/foo/bar/baz")); } -TEST_F(NetworkTableTest, GetHierarchyTrailingSlash) { +TEST_CASE("NetworkTableTest GetHierarchyTrailingSlash", + "[ntcore][network-table]") { std::vector expected{"/", "/foo", "/foo/bar", "/foo/bar/"}; - ASSERT_EQ(expected, wpi::nt::NetworkTable::GetHierarchy("/foo/bar/")); + REQUIRE(expected == wpi::nt::NetworkTable::GetHierarchy("/foo/bar/")); } -TEST_F(NetworkTableTest, ContainsKey) { +TEST_CASE("NetworkTableTest ContainsKey", "[ntcore][network-table]") { auto inst = wpi::nt::NetworkTableInstance::Create(); auto nt = inst.GetTable("containskey"); - ASSERT_FALSE(nt->ContainsKey("testkey")); + REQUIRE_FALSE(nt->ContainsKey("testkey")); nt->PutNumber("testkey", 5); - ASSERT_TRUE(nt->ContainsKey("testkey")); - ASSERT_TRUE(inst.GetEntry("/containskey/testkey").Exists()); - ASSERT_FALSE(inst.GetEntry("containskey/testkey").Exists()); + REQUIRE(nt->ContainsKey("testkey")); + REQUIRE(inst.GetEntry("/containskey/testkey").Exists()); + REQUIRE_FALSE(inst.GetEntry("containskey/testkey").Exists()); wpi::nt::NetworkTableInstance::Destroy(inst); } -TEST_F(NetworkTableTest, LeadingSlash) { +TEST_CASE("NetworkTableTest LeadingSlash", "[ntcore][network-table]") { auto inst = wpi::nt::NetworkTableInstance::Create(); auto nt = inst.GetTable("leadingslash"); auto nt2 = inst.GetTable("/leadingslash"); - ASSERT_FALSE(nt->ContainsKey("testkey")); + REQUIRE_FALSE(nt->ContainsKey("testkey")); nt2->PutNumber("testkey", 5); - ASSERT_TRUE(nt->ContainsKey("testkey")); - ASSERT_TRUE(inst.GetEntry("/leadingslash/testkey").Exists()); + REQUIRE(nt->ContainsKey("testkey")); + REQUIRE(inst.GetEntry("/leadingslash/testkey").Exists()); wpi::nt::NetworkTableInstance::Destroy(inst); } -TEST_F(NetworkTableTest, EmptyOrNoSlash) { +TEST_CASE("NetworkTableTest EmptyOrNoSlash", "[ntcore][network-table]") { auto inst = wpi::nt::NetworkTableInstance::Create(); auto nt = inst.GetTable("/"); auto nt2 = inst.GetTable(""); - ASSERT_FALSE(nt->ContainsKey("testkey")); + REQUIRE_FALSE(nt->ContainsKey("testkey")); nt2->PutNumber("testkey", 5); - ASSERT_TRUE(nt->ContainsKey("testkey")); - ASSERT_TRUE(inst.GetEntry("/testkey").Exists()); + REQUIRE(nt->ContainsKey("testkey")); + REQUIRE(inst.GetEntry("/testkey").Exists()); wpi::nt::NetworkTableInstance::Destroy(inst); } -TEST_F(NetworkTableTest, ResetInstance) { +TEST_CASE("NetworkTableTest ResetInstance", "[ntcore][network-table]") { auto inst = wpi::nt::NetworkTableInstance::Create(); auto nt = inst.GetTable("containskey"); - ASSERT_FALSE(nt->ContainsKey("testkey")); + REQUIRE_FALSE(nt->ContainsKey("testkey")); nt->PutNumber("testkey", 5); - ASSERT_TRUE(nt->ContainsKey("testkey")); - ASSERT_TRUE(inst.GetEntry("/containskey/testkey").Exists()); + REQUIRE(nt->ContainsKey("testkey")); + REQUIRE(inst.GetEntry("/containskey/testkey").Exists()); wpi::nt::ResetInstance(inst.GetHandle()); - ASSERT_FALSE(nt->ContainsKey("testkey")); + REQUIRE_FALSE(nt->ContainsKey("testkey")); wpi::nt::NetworkTableInstance::Destroy(inst); } diff --git a/ntcore/src/test/native/cpp/PubSubOptionsMatcher.cpp b/ntcore/src/test/native/cpp/PubSubOptionsMatcher.cpp deleted file mode 100644 index 365fe10974..0000000000 --- a/ntcore/src/test/native/cpp/PubSubOptionsMatcher.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "PubSubOptionsMatcher.hpp" - -#include "TestPrinters.hpp" - -namespace wpi::nt { - -bool PubSubOptionsMatcher::MatchAndExplain( - const PubSubOptionsImpl& val, - ::testing::MatchResultListener* listener) const { - bool match = true; - if (val.periodicMs != good.periodicMs) { - *listener << "periodic mismatch "; - match = false; - } - if (val.pollStorage != good.pollStorage) { - *listener << "pollStorage mismatch "; - match = false; - } - if (val.sendAll != good.sendAll) { - *listener << "sendAll mismatch "; - match = false; - } - if (val.keepDuplicates != good.keepDuplicates) { - *listener << "keepDuplicates mismatch "; - match = false; - } - if (val.disableSignal != good.disableSignal) { - *listener << "disableSignal mismatch "; - match = false; - } - return match; -} - -void PubSubOptionsMatcher::DescribeTo(::std::ostream* os) const { - PrintTo(good, os); -} - -void PubSubOptionsMatcher::DescribeNegationTo(::std::ostream* os) const { - *os << "is not equal to "; - PrintTo(good, os); -} - -} // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/PubSubOptionsMatcher.hpp b/ntcore/src/test/native/cpp/PubSubOptionsMatcher.hpp index c42ff995be..1a0ed719af 100644 --- a/ntcore/src/test/native/cpp/PubSubOptionsMatcher.hpp +++ b/ntcore/src/test/native/cpp/PubSubOptionsMatcher.hpp @@ -4,32 +4,19 @@ #pragma once -#include -#include - #include "PubSubOptions.hpp" -#include "gmock/gmock.h" +#include "TestPrinters.hpp" namespace wpi::nt { -class PubSubOptionsMatcher - : public ::testing::MatcherInterface { - public: - explicit PubSubOptionsMatcher(PubSubOptionsImpl good) - : good{std::move(good)} {} - - bool MatchAndExplain(const PubSubOptionsImpl& val, - ::testing::MatchResultListener* listener) const override; - void DescribeTo(::std::ostream* os) const override; - void DescribeNegationTo(::std::ostream* os) const override; - - private: - PubSubOptionsImpl good; -}; - -inline ::testing::Matcher PubSubOptionsEq( - PubSubOptionsImpl good) { - return ::testing::MakeMatcher(new PubSubOptionsMatcher(std::move(good))); +inline bool operator==(const PubSubOptionsImpl& lhs, + const PubSubOptionsImpl& rhs) { + return lhs.periodicMs == rhs.periodicMs && + lhs.pollStorage == rhs.pollStorage && lhs.sendAll == rhs.sendAll && + lhs.keepDuplicates == rhs.keepDuplicates && + lhs.topicsOnly == rhs.topicsOnly && + lhs.prefixMatch == rhs.prefixMatch && + lhs.disableSignal == rhs.disableSignal; } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/StorageTest.hpp b/ntcore/src/test/native/cpp/StorageTest.hpp index cb662be149..cf03ae7d86 100644 --- a/ntcore/src/test/native/cpp/StorageTest.hpp +++ b/ntcore/src/test/native/cpp/StorageTest.hpp @@ -29,7 +29,7 @@ class StorageTest { void HookOutgoing(bool server) { storage.SetDispatcher(&dispatcher, server); } wpi::util::Logger logger; - ::testing::StrictMock dispatcher; + MockDispatcher dispatcher; Storage storage; Storage::Entry tmp_entry; }; diff --git a/ntcore/src/test/native/cpp/StructTest.cpp b/ntcore/src/test/native/cpp/StructTest.cpp index 33304556a7..b4e7adb405 100644 --- a/ntcore/src/test/native/cpp/StructTest.cpp +++ b/ntcore/src/test/native/cpp/StructTest.cpp @@ -4,12 +4,12 @@ #include "wpi/util/struct/Struct.hpp" -#include +#include #include "wpi/nt/NetworkTableInstance.hpp" #include "wpi/nt/StructArrayTopic.hpp" #include "wpi/nt/StructTopic.hpp" -#include "wpi/util/SpanMatcher.hpp" +#include "wpi/util/json.hpp" namespace { struct Inner { @@ -157,189 +157,193 @@ struct wpi::util::Struct { namespace wpi::nt { -class StructTest : public ::testing::Test { +class StructTest { public: StructTest() { inst = wpi::nt::NetworkTableInstance::Create(); } - ~StructTest() override { wpi::nt::NetworkTableInstance::Destroy(inst); } + ~StructTest() { wpi::nt::NetworkTableInstance::Destroy(inst); } wpi::nt::NetworkTableInstance inst; }; -TEST_F(StructTest, InnerConstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest InnerConstexpr", "[ntcore][struct]") { wpi::nt::StructTopic topic = inst.GetStructTopic("inner"); wpi::nt::StructPublisher pub = topic.Publish(); wpi::nt::StructSubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Inner"); + REQUIRE(topic.GetTypeString() == "struct:Inner"); pub.SetDefault({0, 1}); Inner val = sub.Get(); - ASSERT_EQ(val.a, 0); - ASSERT_EQ(val.b, 1); + REQUIRE(val.a == 0); + REQUIRE(val.b == 1); pub.Set({1, 2}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.a, 1); - ASSERT_EQ(atomicVal.value.b, 2); + REQUIRE(atomicVal.value.a == 1); + REQUIRE(atomicVal.value.b == 2); Inner val2; sub.GetInto(&val2); - ASSERT_EQ(val2.a, 1); - ASSERT_EQ(val2.b, 2); + REQUIRE(val2.a == 1); + REQUIRE(val2.b == 2); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.a, 1); - ASSERT_EQ(vals[0].value.b, 2); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.a == 1); + REQUIRE(vals[0].value.b == 2); } -TEST_F(StructTest, InnerNonconstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest InnerNonconstexpr", + "[ntcore][struct]") { wpi::nt::StructTopic topic = inst.GetStructTopic("inner2"); wpi::nt::StructPublisher pub = topic.Publish(); wpi::nt::StructSubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Inner2"); + REQUIRE(topic.GetTypeString() == "struct:Inner2"); pub.SetDefault({0, 1}); Inner2 val = sub.Get(); - ASSERT_EQ(val.a, 0); - ASSERT_EQ(val.b, 1); + REQUIRE(val.a == 0); + REQUIRE(val.b == 1); pub.Set({1, 2}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.a, 1); - ASSERT_EQ(atomicVal.value.b, 2); + REQUIRE(atomicVal.value.a == 1); + REQUIRE(atomicVal.value.b == 2); Inner2 val2; sub.GetInto(&val2); - ASSERT_EQ(val2.a, 1); - ASSERT_EQ(val2.b, 2); + REQUIRE(val2.a == 1); + REQUIRE(val2.b == 2); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.a, 1); - ASSERT_EQ(vals[0].value.b, 2); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.a == 1); + REQUIRE(vals[0].value.b == 2); } -TEST_F(StructTest, OuterConstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest OuterConstexpr", "[ntcore][struct]") { wpi::nt::StructTopic topic = inst.GetStructTopic("outer"); wpi::nt::StructPublisher pub = topic.Publish(); wpi::nt::StructSubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Outer"); + REQUIRE(topic.GetTypeString() == "struct:Outer"); pub.SetDefault({{0, 1}, 2}); Outer val = sub.Get(); - ASSERT_EQ(val.inner.a, 0); - ASSERT_EQ(val.inner.b, 1); - ASSERT_EQ(val.c, 2); + REQUIRE(val.inner.a == 0); + REQUIRE(val.inner.b == 1); + REQUIRE(val.c == 2); pub.Set({{1, 2}, 3}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.inner.a, 1); - ASSERT_EQ(atomicVal.value.inner.b, 2); - ASSERT_EQ(atomicVal.value.c, 3); + REQUIRE(atomicVal.value.inner.a == 1); + REQUIRE(atomicVal.value.inner.b == 2); + REQUIRE(atomicVal.value.c == 3); Outer val2; sub.GetInto(&val2); - ASSERT_EQ(val2.inner.a, 1); - ASSERT_EQ(val2.inner.b, 2); - ASSERT_EQ(val2.c, 3); + REQUIRE(val2.inner.a == 1); + REQUIRE(val2.inner.b == 2); + REQUIRE(val2.c == 3); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.inner.a, 1); - ASSERT_EQ(vals[0].value.inner.b, 2); - ASSERT_EQ(vals[0].value.c, 3); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.inner.a == 1); + REQUIRE(vals[0].value.inner.b == 2); + REQUIRE(vals[0].value.c == 3); } -TEST_F(StructTest, OuterNonconstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest OuterNonconstexpr", + "[ntcore][struct]") { wpi::nt::StructTopic topic = inst.GetStructTopic("outer2"); wpi::nt::StructPublisher pub = topic.Publish(); wpi::nt::StructSubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Outer2"); + REQUIRE(topic.GetTypeString() == "struct:Outer2"); pub.SetDefault({{0, 1}, 2}); Outer2 val = sub.Get(); - ASSERT_EQ(val.inner.a, 0); - ASSERT_EQ(val.inner.b, 1); - ASSERT_EQ(val.c, 2); + REQUIRE(val.inner.a == 0); + REQUIRE(val.inner.b == 1); + REQUIRE(val.c == 2); pub.Set({{1, 2}, 3}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.inner.a, 1); - ASSERT_EQ(atomicVal.value.inner.b, 2); - ASSERT_EQ(atomicVal.value.c, 3); + REQUIRE(atomicVal.value.inner.a == 1); + REQUIRE(atomicVal.value.inner.b == 2); + REQUIRE(atomicVal.value.c == 3); Outer2 val2; sub.GetInto(&val2); - ASSERT_EQ(val2.inner.a, 1); - ASSERT_EQ(val2.inner.b, 2); - ASSERT_EQ(val2.c, 3); + REQUIRE(val2.inner.a == 1); + REQUIRE(val2.inner.b == 2); + REQUIRE(val2.c == 3); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.inner.a, 1); - ASSERT_EQ(vals[0].value.inner.b, 2); - ASSERT_EQ(vals[0].value.c, 3); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.inner.a == 1); + REQUIRE(vals[0].value.inner.b == 2); + REQUIRE(vals[0].value.c == 3); } -TEST_F(StructTest, InnerArrayConstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest InnerArrayConstexpr", + "[ntcore][struct]") { wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("innerA"); wpi::nt::StructArrayPublisher pub = topic.Publish(); wpi::nt::StructArraySubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Inner[]"); + REQUIRE(topic.GetTypeString() == "struct:Inner[]"); pub.SetDefault({{{0, 1}}}); auto val = sub.Get(); - ASSERT_EQ(val.size(), 1u); - ASSERT_EQ(val[0].a, 0); - ASSERT_EQ(val[0].b, 1); + REQUIRE(val.size() == 1u); + REQUIRE(val[0].a == 0); + REQUIRE(val[0].b == 1); pub.Set({{{1, 2}}}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.size(), 1u); - ASSERT_EQ(atomicVal.value[0].a, 1); - ASSERT_EQ(atomicVal.value[0].b, 2); + REQUIRE(atomicVal.value.size() == 1u); + REQUIRE(atomicVal.value[0].a == 1); + REQUIRE(atomicVal.value[0].b == 2); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.size(), 1u); - ASSERT_EQ(vals[0].value[0].a, 1); - ASSERT_EQ(vals[0].value[0].b, 2); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.size() == 1u); + REQUIRE(vals[0].value[0].a == 1); + REQUIRE(vals[0].value[0].b == 2); } -TEST_F(StructTest, InnerArrayNonconstexpr) { +TEST_CASE_METHOD(StructTest, "StructTest InnerArrayNonconstexpr", + "[ntcore][struct]") { wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("innerA2"); wpi::nt::StructArrayPublisher pub = topic.Publish(); wpi::nt::StructArraySubscriber sub = topic.Subscribe({}); - ASSERT_EQ(topic.GetTypeString(), "struct:Inner2[]"); + REQUIRE(topic.GetTypeString() == "struct:Inner2[]"); pub.SetDefault({{{0, 1}}}); auto val = sub.Get(); - ASSERT_EQ(val.size(), 1u); - ASSERT_EQ(val[0].a, 0); - ASSERT_EQ(val[0].b, 1); + REQUIRE(val.size() == 1u); + REQUIRE(val[0].a == 0); + REQUIRE(val[0].b == 1); pub.Set({{{1, 2}}}); auto atomicVal = sub.GetAtomic(); - ASSERT_EQ(atomicVal.value.size(), 1u); - ASSERT_EQ(atomicVal.value[0].a, 1); - ASSERT_EQ(atomicVal.value[0].b, 2); + REQUIRE(atomicVal.value.size() == 1u); + REQUIRE(atomicVal.value[0].a == 1); + REQUIRE(atomicVal.value[0].b == 2); auto vals = sub.ReadQueue(); - ASSERT_EQ(vals.size(), 1u); - ASSERT_EQ(vals[0].value.size(), 1u); - ASSERT_EQ(vals[0].value[0].a, 1); - ASSERT_EQ(vals[0].value[0].b, 2); + REQUIRE(vals.size() == 1u); + REQUIRE(vals[0].value.size() == 1u); + REQUIRE(vals[0].value[0].a == 1); + REQUIRE(vals[0].value[0].b == 2); } -TEST_F(StructTest, StructA) { +TEST_CASE_METHOD(StructTest, "StructTest StructA", "[ntcore][struct]") { wpi::nt::StructTopic topic = inst.GetStructTopic("a"); wpi::nt::StructPublisher pub = topic.Publish(); wpi::nt::StructPublisher pub2 = @@ -357,7 +361,7 @@ TEST_F(StructTest, StructA) { entry.Get({}); } -TEST_F(StructTest, StructArrayA) { +TEST_CASE_METHOD(StructTest, "StructTest StructArrayA", "[ntcore][struct]") { wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("a"); wpi::nt::StructArrayPublisher pub = topic.Publish(); @@ -376,7 +380,8 @@ TEST_F(StructTest, StructArrayA) { entry.Get({}); } -TEST_F(StructTest, StructFixedArrayA) { +TEST_CASE_METHOD(StructTest, "StructTest StructFixedArrayA", + "[ntcore][struct]") { wpi::nt::StructTopic> topic = inst.GetStructTopic>("a"); wpi::nt::StructPublisher> pub = topic.Publish(); @@ -396,7 +401,7 @@ TEST_F(StructTest, StructFixedArrayA) { entry.Get(arr); } -TEST_F(StructTest, StructB) { +TEST_CASE_METHOD(StructTest, "StructTest StructB", "[ntcore][struct]") { Info1 info; wpi::nt::StructTopic topic = inst.GetStructTopic("b", info); @@ -416,7 +421,7 @@ TEST_F(StructTest, StructB) { entry.Get({}); } -TEST_F(StructTest, StructArrayB) { +TEST_CASE_METHOD(StructTest, "StructTest StructArrayB", "[ntcore][struct]") { Info1 info; wpi::nt::StructArrayTopic topic = inst.GetStructArrayTopic("b", info); @@ -436,7 +441,8 @@ TEST_F(StructTest, StructArrayB) { entry.Get({}); } -TEST_F(StructTest, StructFixedArrayB) { +TEST_CASE_METHOD(StructTest, "StructTest StructFixedArrayB", + "[ntcore][struct]") { Info1 info; wpi::nt::StructTopic, Info1> topic = inst.GetStructTopic, Info1>("b", info); diff --git a/ntcore/src/test/native/cpp/TableListenerTest.cpp b/ntcore/src/test/native/cpp/TableListenerTest.cpp index 04972d1dce..d8096c52e9 100644 --- a/ntcore/src/test/native/cpp/TableListenerTest.cpp +++ b/ntcore/src/test/native/cpp/TableListenerTest.cpp @@ -3,31 +3,21 @@ // the WPILib BSD license file in the root directory of this project. #include +#include +#include -#include +#include #include "TestPrinters.hpp" -#include "gmock/gmock.h" #include "wpi/nt/DoubleTopic.hpp" #include "wpi/nt/NetworkTableInstance.hpp" #include "wpi/nt/ntcore_cpp.hpp" -using ::testing::_; - -using MockTableEventListener = testing::MockFunction; -using MockSubTableListener = testing::MockFunction table)>; - -class TableListenerTest : public ::testing::Test { +class TableListenerTest { public: TableListenerTest() : m_inst(wpi::nt::NetworkTableInstance::Create()) {} - ~TableListenerTest() override { - wpi::nt::NetworkTableInstance::Destroy(m_inst); - } + ~TableListenerTest() { wpi::nt::NetworkTableInstance::Destroy(m_inst); } void PublishTopics(); @@ -44,21 +34,44 @@ void TableListenerTest::PublishTopics() { m_bazvalue = m_inst.GetDoubleTopic("/baz/bazvalue").Publish(); } -TEST_F(TableListenerTest, AddListener) { +TEST_CASE_METHOD(TableListenerTest, "TableListenerTest AddListener", + "[ntcore][table-listener]") { auto table = m_inst.GetTable("/foo"); - MockTableEventListener listener; + struct ListenerCall { + wpi::nt::NetworkTable* table; + std::string key; + }; + std::vector listenerCalls; table->AddListener(NT_EVENT_TOPIC | NT_EVENT_IMMEDIATE, - listener.AsStdFunction()); - EXPECT_CALL(listener, Call(table.get(), std::string_view{"foovalue"}, _)); + [&](wpi::nt::NetworkTable* callbackTable, + std::string_view key, const wpi::nt::Event&) { + listenerCalls.emplace_back(callbackTable, + std::string{key}); + }); PublishTopics(); - EXPECT_TRUE(m_inst.WaitForListenerQueue(1.0)); + CHECK(m_inst.WaitForListenerQueue(1.0)); + REQUIRE(listenerCalls.size() == 1u); + CHECK(listenerCalls[0].table == table.get()); + CHECK(listenerCalls[0].key == "foovalue"); } -TEST_F(TableListenerTest, AddSubTableListener) { +TEST_CASE_METHOD(TableListenerTest, "TableListenerTest AddSubTableListener", + "[ntcore][table-listener]") { auto table = m_inst.GetTable("/foo"); - MockSubTableListener listener; - table->AddSubTableListener(listener.AsStdFunction()); - EXPECT_CALL(listener, Call(table.get(), std::string_view{"bar"}, _)); + struct ListenerCall { + wpi::nt::NetworkTable* parent; + std::string name; + std::shared_ptr table; + }; + std::vector listenerCalls; + table->AddSubTableListener( + [&](wpi::nt::NetworkTable* parent, std::string_view name, + std::shared_ptr callbackTable) { + listenerCalls.emplace_back(parent, std::string{name}, callbackTable); + }); PublishTopics(); - EXPECT_TRUE(m_inst.WaitForListenerQueue(1.0)); + CHECK(m_inst.WaitForListenerQueue(1.0)); + REQUIRE(listenerCalls.size() == 1u); + CHECK(listenerCalls[0].parent == table.get()); + CHECK(listenerCalls[0].name == "bar"); } diff --git a/ntcore/src/test/native/cpp/TestPrinters.cpp b/ntcore/src/test/native/cpp/TestPrinters.cpp index 5153b5c438..417db6e5e3 100644 --- a/ntcore/src/test/native/cpp/TestPrinters.cpp +++ b/ntcore/src/test/native/cpp/TestPrinters.cpp @@ -4,106 +4,295 @@ #include "TestPrinters.hpp" +#include +#include +#include +#include +#include + #include "Handle.hpp" #include "PubSubOptions.hpp" #include "net/Message.hpp" #include "wpi/nt/NetworkTableValue.hpp" #include "wpi/nt/ntcore_cpp.hpp" +#include "wpi/util/fmt/raw_ostream.hpp" +#include "wpi/util/json.hpp" +#include "wpi/util/raw_ostream.hpp" -namespace wpi::nt { +namespace { -void PrintTo(const Event& event, std::ostream* os) { - *os << "Event{listener="; - PrintTo(Handle{event.listener}, os); - *os << ", flags=" << event.flags; - // *os << ", name=\"" << event.name << "\", flags=" << event.flags - // << "value="; - // PrintTo(event.value, os); - *os << '}'; +template +void WriteRangeElement(const T& elem, wpi::util::raw_ostream& os) { + using Elem = std::decay_t; + if constexpr (std::is_convertible_v) { + os << elem; + } else { + wpi::util::print(os, "{}", elem); + } } -void PrintTo(const Handle& handle, std::ostream* os) { - *os << "Handle{"; +template +void WriteRange(const Range& range, wpi::util::raw_ostream& os) { + os << '{'; + bool first = true; + for (const auto& elem : range) { + if (first) { + first = false; + } else { + os << ", "; + } + WriteRangeElement(elem, os); + } + os << '}'; +} + +template +void WriteStringRange(const Range& range, wpi::util::raw_ostream& os) { + os << '{'; + bool first = true; + for (const auto& elem : range) { + if (first) { + first = false; + } else { + os << ", "; + } + os << '"' << elem << '"'; + } + os << '}'; +} + +std::string JsonToString(const wpi::util::json& value) { + std::string out; + wpi::util::raw_string_ostream os{out}; + value.marshal(os); + return os.str(); +} + +void WriteOptionalInt(std::optional value, wpi::util::raw_ostream& os) { + if (value) { + wpi::util::print(os, "{}", *value); + } else { + os << "nullopt"; + } +} + +void WritePubSubOptions(const wpi::nt::PubSubOptionsImpl& options, + wpi::util::raw_ostream& os) { + wpi::util::print(os, + "PubSubOptions{{periodicMs={}, pollStorage={}, sendAll={}, " + "keepDuplicates={}, topicsOnly={}, prefixMatch={}, " + "disableSignal={}}}", + options.periodicMs, options.pollStorage, options.sendAll, + options.keepDuplicates, options.topicsOnly, + options.prefixMatch, options.disableSignal); +} + +void WriteHandle(const wpi::nt::Handle& handle, wpi::util::raw_ostream& os) { + os << "Handle{"; switch (handle.GetType()) { - case Handle::LISTENER: - *os << "LISTENER"; + case wpi::nt::Handle::LISTENER: + os << "LISTENER"; break; - case Handle::LISTENER_POLLER: - *os << "LISTENER_POLLER"; + case wpi::nt::Handle::LISTENER_POLLER: + os << "LISTENER_POLLER"; break; - case Handle::ENTRY: - *os << "ENTRY"; + case wpi::nt::Handle::ENTRY: + os << "ENTRY"; break; - case Handle::INSTANCE: - *os << "INSTANCE"; + case wpi::nt::Handle::INSTANCE: + os << "INSTANCE"; break; - case Handle::TOPIC: - *os << "kTopic"; + case wpi::nt::Handle::TOPIC: + os << "kTopic"; break; - case Handle::SUBSCRIBER: - *os << "SUBSCRIBER"; + case wpi::nt::Handle::SUBSCRIBER: + os << "SUBSCRIBER"; break; - case Handle::PUBLISHER: - *os << "PUBLISHER"; + case wpi::nt::Handle::PUBLISHER: + os << "PUBLISHER"; break; default: - *os << "UNKNOWN"; + os << "UNKNOWN"; break; } - *os << ", " << handle.GetInst() << ", " << handle.GetIndex() << '}'; + wpi::util::print(os, ", {}, {}}}", handle.GetInst(), handle.GetIndex()); } -void PrintTo(const Value& value, std::ostream* os) { - *os << "Value{"; +void WriteValue(const wpi::nt::Value& value, wpi::util::raw_ostream& os) { + os << "Value{"; switch (value.type()) { case NT_UNASSIGNED: break; case NT_BOOLEAN: - *os << "boolean, " << (value.GetBoolean() ? "true" : "false"); + os << "boolean, " << (value.GetBoolean() ? "true" : "false"); break; case NT_DOUBLE: - *os << "double, " << value.GetDouble(); + wpi::util::print(os, "double, {}", value.GetDouble()); break; case NT_FLOAT: - *os << "float, " << value.GetFloat(); + wpi::util::print(os, "float, {}", value.GetFloat()); break; case NT_INTEGER: - *os << "int, " << value.GetInteger(); + wpi::util::print(os, "int, {}", value.GetInteger()); break; case NT_STRING: - *os << "string, \"" << value.GetString() << '"'; + os << "string, \"" << value.GetString() << '"'; break; case NT_RAW: - *os << "raw, " << ::testing::PrintToString(value.GetRaw()); + os << "raw, "; + WriteRange(value.GetRaw(), os); break; case NT_BOOLEAN_ARRAY: - *os << "boolean[], " << ::testing::PrintToString(value.GetBooleanArray()); + os << "boolean[], "; + WriteRange(value.GetBooleanArray(), os); break; case NT_DOUBLE_ARRAY: - *os << "double[], " << ::testing::PrintToString(value.GetDoubleArray()); + os << "double[], "; + WriteRange(value.GetDoubleArray(), os); break; case NT_FLOAT_ARRAY: - *os << "float[], " << ::testing::PrintToString(value.GetFloatArray()); + os << "float[], "; + WriteRange(value.GetFloatArray(), os); break; case NT_INTEGER_ARRAY: - *os << "int[], " << ::testing::PrintToString(value.GetIntegerArray()); + os << "int[], "; + WriteRange(value.GetIntegerArray(), os); break; case NT_STRING_ARRAY: - *os << "string[], " << ::testing::PrintToString(value.GetStringArray()); + os << "string[], "; + WriteRange(value.GetStringArray(), os); break; default: - *os << "UNKNOWN TYPE " << value.type(); + wpi::util::print(os, "UNKNOWN TYPE {}", static_cast(value.type())); break; } - *os << '}'; + os << '}'; } -void PrintTo(const PubSubOptionsImpl& options, std::ostream* os) { - *os << "PubSubOptions{periodicMs=" << options.periodicMs - << ", pollStorage=" << options.pollStorage - << ", sendAll=" << options.sendAll - << ", keepDuplicates=" << options.keepDuplicates - << ", disableSignal=" << options.disableSignal << '}'; +void WriteClientMessage(const wpi::nt::net::ClientMessage& msg, + wpi::util::raw_ostream& os) { + std::visit( + [&](const auto& contents) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + os << "ClientMessage{}"; + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ClientMessage{{PublishMsg{{pubuid={}, name=\"{}\", " + "typeStr=\"{}\", properties={}, options=", + contents.pubuid, contents.name, contents.typeStr, + JsonToString(contents.properties)); + WritePubSubOptions(contents.options, os); + os << "}}"; + } else if constexpr (std::is_same_v) { + wpi::util::print(os, "ClientMessage{{UnpublishMsg{{pubuid={}}}}}", + contents.pubuid); + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ClientMessage{{SetPropertiesMsg{{name=\"{}\", " + "update={}}}}}", + contents.name, JsonToString(contents.update)); + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ClientMessage{{SubscribeMsg{{subuid={}, " + "topicNames=", + contents.subuid); + WriteStringRange(contents.topicNames, os); + os << ", options="; + WritePubSubOptions(contents.options, os); + os << "}}"; + } else if constexpr (std::is_same_v) { + wpi::util::print(os, "ClientMessage{{UnsubscribeMsg{{subuid={}}}}}", + contents.subuid); + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ClientMessage{{ClientValueMsg{{pubuid={}, value=", + contents.pubuid); + WriteValue(contents.value, os); + os << "}}"; + } + }, + msg.contents); } -} // namespace wpi::nt +void WriteServerMessage(const wpi::nt::net::ServerMessage& msg, + wpi::util::raw_ostream& os) { + std::visit( + [&](const auto& contents) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + os << "ServerMessage{}"; + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ServerMessage{{AnnounceMsg{{name=\"{}\", id={}, " + "typeStr=\"{}\", pubuid=", + contents.name, contents.id, contents.typeStr); + WriteOptionalInt(contents.pubuid, os); + wpi::util::print(os, ", properties={}}}}}", + JsonToString(contents.properties)); + } else if constexpr (std::is_same_v) { + wpi::util::print( + os, "ServerMessage{{UnannounceMsg{{name=\"{}\", id={}}}}}", + contents.name, contents.id); + } else if constexpr (std::is_same_v< + T, wpi::nt::net::PropertiesUpdateMsg>) { + wpi::util::print(os, + "ServerMessage{{PropertiesUpdateMsg{{name=\"{}\", " + "update={}, ack={}}}}}", + contents.name, JsonToString(contents.update), + contents.ack); + } else if constexpr (std::is_same_v) { + wpi::util::print(os, + "ServerMessage{{ServerValueMsg{{topic={}, value=", + contents.topic); + WriteValue(contents.value, os); + os << "}}"; + } + }, + msg.contents); +} + +template +std::string ToString(const T& value, Writer writer) { + std::string out; + wpi::util::raw_string_ostream os{out}; + writer(value, os); + return os.str(); +} + +} // namespace + +std::string Catch::StringMaker::convert( + const wpi::nt::Event& event) { + return ToString(event, [](const auto& value, wpi::util::raw_ostream& os) { + os << "Event{listener="; + WriteHandle(wpi::nt::Handle{value.listener}, os); + wpi::util::print(os, ", flags={}}}", value.flags); + }); +} + +std::string Catch::StringMaker::convert( + const wpi::nt::Handle& handle) { + return ToString(handle, WriteHandle); +} + +std::string Catch::StringMaker::convert( + const wpi::nt::net::ClientMessage& msg) { + return ToString(msg, WriteClientMessage); +} + +std::string Catch::StringMaker::convert( + const wpi::nt::net::ServerMessage& msg) { + return ToString(msg, WriteServerMessage); +} + +std::string Catch::StringMaker::convert( + const wpi::nt::Value& value) { + return ToString(value, WriteValue); +} + +std::string Catch::StringMaker::convert( + const wpi::nt::PubSubOptionsImpl& options) { + return ToString(options, WritePubSubOptions); +} diff --git a/ntcore/src/test/native/cpp/TestPrinters.hpp b/ntcore/src/test/native/cpp/TestPrinters.hpp index 171ad77c48..296618b56b 100644 --- a/ntcore/src/test/native/cpp/TestPrinters.hpp +++ b/ntcore/src/test/native/cpp/TestPrinters.hpp @@ -4,21 +4,12 @@ #pragma once -#include -#include #include -#include -#include - -#include "wpi/util/TestPrinters.hpp" +#include namespace wpi::nt { -namespace net3 { -class Message3; -} // namespace net3 - namespace net { struct ClientMessage; struct ServerMessage; @@ -29,12 +20,38 @@ class Handle; class PubSubOptionsImpl; class Value; -void PrintTo(const Event& event, std::ostream* os); -void PrintTo(const Handle& handle, std::ostream* os); -void PrintTo(const net3::Message3& msg, std::ostream* os); -void PrintTo(const net::ClientMessage& msg, std::ostream* os); -void PrintTo(const net::ServerMessage& msg, std::ostream* os); -void PrintTo(const Value& value, std::ostream* os); -void PrintTo(const PubSubOptionsImpl& options, std::ostream* os); - } // namespace wpi::nt + +namespace Catch { + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::Event& event); +}; + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::Handle& handle); +}; + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::net::ClientMessage& msg); +}; + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::net::ServerMessage& msg); +}; + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::Value& value); +}; + +template <> +struct StringMaker { + static std::string convert(const wpi::nt::PubSubOptionsImpl& options); +}; + +} // namespace Catch diff --git a/ntcore/src/test/native/cpp/TimeSyncTest.cpp b/ntcore/src/test/native/cpp/TimeSyncTest.cpp index eac1c85dab..5a41d80595 100644 --- a/ntcore/src/test/native/cpp/TimeSyncTest.cpp +++ b/ntcore/src/test/native/cpp/TimeSyncTest.cpp @@ -2,60 +2,63 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include +#include #include "wpi/nt/NetworkTableInstance.hpp" #include "wpi/nt/NetworkTableListener.hpp" -class TimeSyncTest : public ::testing::Test { +class TimeSyncTest { public: TimeSyncTest() : m_inst(wpi::nt::NetworkTableInstance::Create()) {} - ~TimeSyncTest() override { wpi::nt::NetworkTableInstance::Destroy(m_inst); } + ~TimeSyncTest() { wpi::nt::NetworkTableInstance::Destroy(m_inst); } protected: wpi::nt::NetworkTableInstance m_inst; }; -TEST_F(TimeSyncTest, TestLocal) { +TEST_CASE_METHOD(TimeSyncTest, "TimeSyncTest TestLocal", + "[ntcore][time-sync]") { auto offset = m_inst.GetServerTimeOffset(); - ASSERT_FALSE(offset); + REQUIRE_FALSE(offset); } -TEST_F(TimeSyncTest, TestServer) { +TEST_CASE_METHOD(TimeSyncTest, "TimeSyncTest TestServer", + "[ntcore][time-sync]") { wpi::nt::NetworkTableListenerPoller poller{m_inst}; poller.AddTimeSyncListener(false); m_inst.StartServer("timesynctest.json", "127.0.0.1", "", 10030); auto offset = m_inst.GetServerTimeOffset(); - ASSERT_TRUE(offset); - ASSERT_EQ(0, *offset); + REQUIRE(offset); + REQUIRE(0 == *offset); auto events = poller.ReadQueue(); - ASSERT_EQ(1u, events.size()); + REQUIRE(1u == events.size()); auto data = events[0].GetTimeSyncEventData(); - ASSERT_TRUE(data); - ASSERT_TRUE(data->valid); - ASSERT_EQ(0, data->serverTimeOffset); - ASSERT_EQ(0, data->rtt2); + REQUIRE(data); + REQUIRE(data->valid); + REQUIRE(0 == data->serverTimeOffset); + REQUIRE(0 == data->rtt2); m_inst.StopServer(); offset = m_inst.GetServerTimeOffset(); - ASSERT_FALSE(offset); + REQUIRE_FALSE(offset); events = poller.ReadQueue(); - ASSERT_EQ(1u, events.size()); + REQUIRE(1u == events.size()); data = events[0].GetTimeSyncEventData(); - ASSERT_TRUE(data); - ASSERT_FALSE(data->valid); + REQUIRE(data); + REQUIRE_FALSE(data->valid); } -TEST_F(TimeSyncTest, TestClient) { +TEST_CASE_METHOD(TimeSyncTest, "TimeSyncTest TestClient", + "[ntcore][time-sync]") { m_inst.StartClient("client"); auto offset = m_inst.GetServerTimeOffset(); - ASSERT_FALSE(offset); + REQUIRE_FALSE(offset); m_inst.StopClient(); offset = m_inst.GetServerTimeOffset(); - ASSERT_FALSE(offset); + REQUIRE_FALSE(offset); } diff --git a/ntcore/src/test/native/cpp/TopicListenerTest.cpp b/ntcore/src/test/native/cpp/TopicListenerTest.cpp index b3ed4f5ec5..b745d856cf 100644 --- a/ntcore/src/test/native/cpp/TopicListenerTest.cpp +++ b/ntcore/src/test/native/cpp/TopicListenerTest.cpp @@ -7,15 +7,17 @@ #include #include -#include - #include "TestPrinters.hpp" -#include "ValueMatcher.hpp" +#undef FAIL +#undef SUCCEED +#include + #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/Synchronization.hpp" +#include "wpi/util/json.hpp" -class TopicListenerTest : public ::testing::Test { +class TopicListenerTest { public: TopicListenerTest() : m_serverInst(wpi::nt::CreateInstance()), @@ -34,7 +36,7 @@ class TopicListenerTest : public ::testing::Test { #endif } - ~TopicListenerTest() override { + ~TopicListenerTest() { wpi::nt::DestroyInstance(m_serverInst); wpi::nt::DestroyInstance(m_clientInst); } @@ -62,7 +64,7 @@ void TopicListenerTest::Connect(unsigned int port) { wpi::nt::EventFlags::CONNECTED); bool timedOut = false; if (!wpi::util::WaitForObject(poller, 1.0, &timedOut)) { - FAIL() << "client didn't connect to server"; + FAIL("client didn't connect to server"); } } @@ -75,16 +77,17 @@ void TopicListenerTest::PublishTopics(NT_Inst inst) { void TopicListenerTest::CheckEvents(const std::vector& events, NT_Listener handle, unsigned int flags, std::string_view topicName) { - ASSERT_EQ(events.size(), 1u); - ASSERT_EQ(events[0].listener, handle); - ASSERT_EQ(events[0].flags, flags); + REQUIRE(events.size() == 1u); + REQUIRE(events[0].listener == handle); + REQUIRE(events[0].flags == flags); auto topicInfo = events[0].GetTopicInfo(); - ASSERT_TRUE(topicInfo); - ASSERT_EQ(topicInfo->topic, wpi::nt::GetTopic(m_serverInst, topicName)); - ASSERT_EQ(topicInfo->name, topicName); + REQUIRE(topicInfo); + REQUIRE(topicInfo->topic == wpi::nt::GetTopic(m_serverInst, topicName)); + REQUIRE(topicInfo->name == topicName); } -TEST_F(TopicListenerTest, TopicNewLocal) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicNewLocal", + "[ntcore][topic-listener]") { auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener( poller, wpi::nt::GetTopic(m_serverInst, "/foo"), @@ -93,16 +96,14 @@ TEST_F(TopicListenerTest, TopicNewLocal) { PublishTopics(m_serverInst); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH, "/foo"); } -TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicNewRemote", + "[ntcore][topic-listener][.]") { Connect(10010); - if (HasFatalFailure()) { - return; - } auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener( poller, wpi::nt::GetTopic(m_serverInst, "/foo"), @@ -114,12 +115,13 @@ TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH, "/foo"); } -TEST_F(TopicListenerTest, TopicPublishImm) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicPublishImm", + "[ntcore][topic-listener]") { PublishTopics(m_serverInst); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); @@ -128,14 +130,15 @@ TEST_F(TopicListenerTest, TopicPublishImm) { wpi::nt::EventFlags::PUBLISH | wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH | wpi::nt::EventFlags::IMMEDIATE, "/foo"); } -TEST_F(TopicListenerTest, TopicUnpublishPropsImm) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicUnpublishPropsImm", + "[ntcore][topic-listener]") { PublishTopics(m_serverInst); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); @@ -145,12 +148,13 @@ TEST_F(TopicListenerTest, TopicUnpublishPropsImm) { wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); + REQUIRE_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); - ASSERT_TRUE(events.empty()); + REQUIRE(events.empty()); } -TEST_F(TopicListenerTest, TopicUnpublishLocal) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicUnpublishLocal", + "[ntcore][topic-listener]") { auto topic = wpi::nt::GetTopic(m_serverInst, "/foo"); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); @@ -161,16 +165,14 @@ TEST_F(TopicListenerTest, TopicUnpublishLocal) { wpi::nt::Unpublish(pub); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::UNPUBLISH, "/foo"); } -TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicUnpublishRemote", + "[ntcore][topic-listener][.]") { Connect(10010); - if (HasFatalFailure()) { - return; - } auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener( poller, wpi::nt::GetTopic(m_serverInst, "/foo"), @@ -187,31 +189,30 @@ TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::UNPUBLISH, "/foo"); } -TEST_F(TopicListenerTest, TopicPropertiesLocal) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicPropertiesLocal", + "[ntcore][topic-listener]") { auto topic = wpi::nt::GetTopic(m_serverInst, "/foo"); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener(poller, topic, wpi::nt::EventFlags::PROPERTIES); - wpi::nt::SetTopicProperty(topic, "foo", 5); + wpi::nt::SetTopicProperty(topic, "foo", wpi::util::json{5}); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PROPERTIES, "/foo"); } -TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest TopicPropertiesRemote", + "[ntcore][topic-listener][.]") { Connect(10010); - if (HasFatalFailure()) { - return; - } // the topic needs to actually exist wpi::nt::Publish(wpi::nt::GetTopic(m_serverInst, "/foo"), NT_BOOLEAN, "boolean"); @@ -222,17 +223,19 @@ TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) { wpi::nt::EventFlags::PROPERTIES); wpi::nt::FlushLocal(m_serverInst); - wpi::nt::SetTopicProperty(wpi::nt::GetTopic(m_clientInst, "/foo"), "foo", 5); + wpi::nt::SetTopicProperty(wpi::nt::GetTopic(m_clientInst, "/foo"), "foo", + wpi::util::json{5}); wpi::nt::Flush(m_clientInst); std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PROPERTIES, "/foo"); } -TEST_F(TopicListenerTest, PrefixPublishLocal) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest PrefixPublishLocal", + "[ntcore][topic-listener]") { auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener(poller, {{"/foo/"}}, wpi::nt::EventFlags::PUBLISH); @@ -240,16 +243,14 @@ TEST_F(TopicListenerTest, PrefixPublishLocal) { PublishTopics(m_serverInst); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH); } -TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest PrefixPublishRemote", + "[ntcore][topic-listener][.]") { Connect(10011); - if (HasFatalFailure()) { - return; - } auto poller = wpi::nt::CreateListenerPoller(m_serverInst); auto handle = wpi::nt::AddPolledListener(poller, {{"/foo/"}}, wpi::nt::EventFlags::PUBLISH); @@ -260,12 +261,13 @@ TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH); } -TEST_F(TopicListenerTest, PrefixPublishImm) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest PrefixPublishImm", + "[ntcore][topic-listener]") { PublishTopics(m_serverInst); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); @@ -274,13 +276,14 @@ TEST_F(TopicListenerTest, PrefixPublishImm) { wpi::nt::EventFlags::PUBLISH | wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); CheckEvents(events, handle, wpi::nt::EventFlags::PUBLISH | wpi::nt::EventFlags::IMMEDIATE); } -TEST_F(TopicListenerTest, PrefixUnpublishPropsImm) { +TEST_CASE_METHOD(TopicListenerTest, "TopicListenerTest PrefixUnpublishPropsImm", + "[ntcore][topic-listener]") { PublishTopics(m_serverInst); auto poller = wpi::nt::CreateListenerPoller(m_serverInst); @@ -290,7 +293,7 @@ TEST_F(TopicListenerTest, PrefixUnpublishPropsImm) { wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); + REQUIRE_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); auto events = wpi::nt::ReadListenerQueue(poller); - ASSERT_TRUE(events.empty()); + REQUIRE(events.empty()); } diff --git a/ntcore/src/test/native/cpp/TopicTest.cpp b/ntcore/src/test/native/cpp/TopicTest.cpp index 20911c87a0..8d725ad7ea 100644 --- a/ntcore/src/test/native/cpp/TopicTest.cpp +++ b/ntcore/src/test/native/cpp/TopicTest.cpp @@ -4,57 +4,60 @@ #include "wpi/nt/Topic.hpp" -#include +#include #include "wpi/nt/NetworkTableInstance.hpp" -class TopicTest : public ::testing::Test { +class TopicTest { public: TopicTest() : m_inst{wpi::nt::NetworkTableInstance::Create()} {} - ~TopicTest() override { wpi::nt::NetworkTableInstance::Destroy(m_inst); } + ~TopicTest() { wpi::nt::NetworkTableInstance::Destroy(m_inst); } protected: wpi::nt::NetworkTableInstance m_inst; }; -TEST_F(TopicTest, UserDataDefaultsToNull) { +TEST_CASE_METHOD(TopicTest, "TopicTest UserDataDefaultsToNull", + "[ntcore][topic]") { auto topic = m_inst.GetTopic("foo"); - EXPECT_EQ(nullptr, topic.GetUserData()); + CHECK(nullptr == topic.GetUserData()); } -TEST_F(TopicTest, UserDataRoundTrip) { +TEST_CASE_METHOD(TopicTest, "TopicTest UserDataRoundTrip", "[ntcore][topic]") { auto topic = m_inst.GetTopic("foo"); int data = 5; topic.SetUserData(&data); - EXPECT_EQ(&data, topic.GetUserData()); + CHECK(&data == topic.GetUserData()); } -TEST_F(TopicTest, UserDataCanBeReplacedAndCleared) { +TEST_CASE_METHOD(TopicTest, "TopicTest UserDataCanBeReplacedAndCleared", + "[ntcore][topic]") { auto topic = m_inst.GetTopic("foo"); auto sameTopic = m_inst.GetTopic("foo"); int data1 = 5; int data2 = 10; topic.SetUserData(&data1); - EXPECT_EQ(&data1, sameTopic.GetUserData()); + CHECK(&data1 == sameTopic.GetUserData()); sameTopic.SetUserData(&data2); - EXPECT_EQ(&data2, topic.GetUserData()); + CHECK(&data2 == topic.GetUserData()); topic.SetUserData(nullptr); - EXPECT_EQ(nullptr, sameTopic.GetUserData()); + CHECK(nullptr == sameTopic.GetUserData()); } -TEST_F(TopicTest, UserDataInvalidTopic) { +TEST_CASE_METHOD(TopicTest, "TopicTest UserDataInvalidTopic", + "[ntcore][topic]") { wpi::nt::Topic topic; int data = 5; - EXPECT_EQ(nullptr, topic.GetUserData()); + CHECK(nullptr == topic.GetUserData()); topic.SetUserData(&data); - EXPECT_EQ(nullptr, topic.GetUserData()); + CHECK(nullptr == topic.GetUserData()); } diff --git a/ntcore/src/test/native/cpp/ValueListenerTest.cpp b/ntcore/src/test/native/cpp/ValueListenerTest.cpp index 34dad0da72..3c1737f164 100644 --- a/ntcore/src/test/native/cpp/ValueListenerTest.cpp +++ b/ntcore/src/test/native/cpp/ValueListenerTest.cpp @@ -2,33 +2,30 @@ // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. -#include - #include "TestPrinters.hpp" -#include "ValueMatcher.hpp" +#undef FAIL +#undef SUCCEED +#include + #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp.hpp" #include "wpi/util/Synchronization.hpp" -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::IsNull; -using ::testing::Return; - namespace wpi::nt { // Test only local here; it's more reliable to mock the network -class ValueListenerTest : public ::testing::Test { +class ValueListenerTest { public: ValueListenerTest() : m_inst{wpi::nt::CreateInstance()} {} - ~ValueListenerTest() override { wpi::nt::DestroyInstance(m_inst); } + ~ValueListenerTest() { wpi::nt::DestroyInstance(m_inst); } protected: NT_Inst m_inst; }; -TEST_F(ValueListenerTest, MultiPollSub) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest MultiPollSub", + "[ntcore][value-listener]") { auto topic = wpi::nt::GetTopic(m_inst, "foo"); auto pub = wpi::nt::Publish(topic, NT_DOUBLE, "double"); auto sub = wpi::nt::Subscribe(topic, NT_DOUBLE, "double"); @@ -46,45 +43,46 @@ TEST_F(ValueListenerTest, MultiPollSub) { wpi::nt::SetDouble(pub, 0); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller1, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); - ASSERT_TRUE(wpi::util::WaitForObject(poller2, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); - ASSERT_TRUE(wpi::util::WaitForObject(poller3, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller1, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller2, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller3, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results1 = wpi::nt::ReadListenerQueue(poller1); auto results2 = wpi::nt::ReadListenerQueue(poller2); auto results3 = wpi::nt::ReadListenerQueue(poller3); - ASSERT_EQ(results1.size(), 1u); - EXPECT_EQ(results1[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results1[0].listener, h1); + REQUIRE(results1.size() == 1u); + CHECK(results1[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results1[0].listener == h1); auto valueData = results1[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - ASSERT_EQ(results2.size(), 1u); - EXPECT_EQ(results2[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results2[0].listener, h2); + REQUIRE(results2.size() == 1u); + CHECK(results2[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results2[0].listener == h2); valueData = results2[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - ASSERT_EQ(results3.size(), 1u); - EXPECT_EQ(results3[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results3[0].listener, h3); + REQUIRE(results3.size() == 1u); + CHECK(results3[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results3[0].listener == h3); valueData = results3[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, PollMultiSub) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollMultiSub", + "[ntcore][value-listener]") { auto topic = wpi::nt::GetTopic(m_inst, "foo"); auto pub = wpi::nt::Publish(topic, NT_DOUBLE, "double"); auto sub1 = wpi::nt::Subscribe(topic, NT_DOUBLE, "double"); @@ -99,29 +97,30 @@ TEST_F(ValueListenerTest, PollMultiSub) { wpi::nt::SetDouble(pub, 0); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 2u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h1); + REQUIRE(results.size() == 2u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h1); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub1); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub1); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - EXPECT_EQ(results[1].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[1].listener, h2); + CHECK(results[1].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[1].listener == h2); valueData = results[1].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub2); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub2); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, PollMultiSubTopic) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollMultiSubTopic", + "[ntcore][value-listener]") { auto topic1 = wpi::nt::GetTopic(m_inst, "foo"); auto topic2 = wpi::nt::GetTopic(m_inst, "bar"); auto pub1 = wpi::nt::Publish(topic1, NT_DOUBLE, "double"); @@ -139,29 +138,30 @@ TEST_F(ValueListenerTest, PollMultiSubTopic) { wpi::nt::SetDouble(pub2, 1); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 2u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h1); + REQUIRE(results.size() == 2u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h1); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub1); - EXPECT_EQ(valueData->topic, topic1); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub1); + CHECK(valueData->topic == topic1); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - EXPECT_EQ(results[1].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[1].listener, h2); + CHECK(results[1].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[1].listener == h2); valueData = results[1].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub2); - EXPECT_EQ(valueData->topic, topic2); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(1.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub2); + CHECK(valueData->topic == topic2); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(1.0)); } -TEST_F(ValueListenerTest, PollSubMultiple) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollSubMultiple", + "[ntcore][value-listener]") { auto topic1 = wpi::nt::GetTopic(m_inst, "foo/1"); auto topic2 = wpi::nt::GetTopic(m_inst, "foo/2"); auto pub1 = wpi::nt::Publish(topic1, NT_DOUBLE, "double"); @@ -176,29 +176,30 @@ TEST_F(ValueListenerTest, PollSubMultiple) { wpi::nt::SetDouble(pub2, 1); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 2u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 2u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic1); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic1); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - EXPECT_EQ(results[1].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[1].listener, h); + CHECK(results[1].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[1].listener == h); valueData = results[1].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic2); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(1.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic2); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(1.0)); } -TEST_F(ValueListenerTest, PollSubPrefixCreated) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollSubPrefixCreated", + "[ntcore][value-listener]") { auto poller = wpi::nt::CreateListenerPoller(m_inst); auto h = wpi::nt::AddPolledListener(poller, {{"foo"}}, wpi::nt::EventFlags::VALUE_LOCAL); @@ -215,53 +216,55 @@ TEST_F(ValueListenerTest, PollSubPrefixCreated) { wpi::nt::SetDouble(pub3, 1); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 2u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 2u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->topic, topic1); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->topic == topic1); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - EXPECT_EQ(results[1].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[1].listener, h); + CHECK(results[1].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[1].listener == h); valueData = results[1].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->topic, topic2); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(1.0)); + REQUIRE(valueData); + CHECK(valueData->topic == topic2); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(1.0)); } -TEST_F(ValueListenerTest, PollEntry) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollEntry", + "[ntcore][value-listener]") { auto entry = wpi::nt::GetEntry(m_inst, "foo"); auto poller = wpi::nt::CreateListenerPoller(m_inst); auto h = wpi::nt::AddPolledListener(poller, entry, wpi::nt::EventFlags::VALUE_LOCAL); - ASSERT_TRUE(wpi::nt::SetDouble(entry, 0)); + REQUIRE(wpi::nt::SetDouble(entry, 0)); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 1u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 1u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, entry); - EXPECT_EQ(valueData->topic, wpi::nt::GetTopic(m_inst, "foo")); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == entry); + CHECK(valueData->topic == wpi::nt::GetTopic(m_inst, "foo")); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, PollImmediate) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollImmediate", + "[ntcore][value-listener]") { auto entry = wpi::nt::GetEntry(m_inst, "foo"); - ASSERT_TRUE(wpi::nt::SetDouble(entry, 0)); + REQUIRE(wpi::nt::SetDouble(entry, 0)); auto poller = wpi::nt::CreateListenerPoller(m_inst); auto h = wpi::nt::AddPolledListener( @@ -269,23 +272,24 @@ TEST_F(ValueListenerTest, PollImmediate) { wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 1u); - EXPECT_EQ(results[0].flags & (wpi::nt::EventFlags::VALUE_LOCAL | - wpi::nt::EventFlags::IMMEDIATE), - wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 1u); + CHECK((results[0].flags & + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)) == + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, entry); - EXPECT_EQ(valueData->topic, wpi::nt::GetTopic(m_inst, "foo")); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == entry); + CHECK(valueData->topic == wpi::nt::GetTopic(m_inst, "foo")); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, PollImmediateNoValue) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest PollImmediateNoValue", + "[ntcore][value-listener]") { auto entry = wpi::nt::GetEntry(m_inst, "foo"); auto poller = wpi::nt::CreateListenerPoller(m_inst); @@ -294,29 +298,31 @@ TEST_F(ValueListenerTest, PollImmediateNoValue) { wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); - ASSERT_TRUE(timedOut); + REQUIRE_FALSE(wpi::util::WaitForObject(poller, 0.02, &timedOut)); + REQUIRE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_TRUE(results.empty()); + REQUIRE(results.empty()); // now set a value - ASSERT_TRUE(wpi::nt::SetDouble(entry, 0)); + REQUIRE(wpi::nt::SetDouble(entry, 0)); - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); results = wpi::nt::ReadListenerQueue(poller); - ASSERT_FALSE(timedOut); + REQUIRE_FALSE(timedOut); - ASSERT_EQ(results.size(), 1u); - EXPECT_EQ(results[0].flags, wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 1u); + CHECK(results[0].flags == wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, entry); - EXPECT_EQ(valueData->topic, wpi::nt::GetTopic(m_inst, "foo")); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == entry); + CHECK(valueData->topic == wpi::nt::GetTopic(m_inst, "foo")); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, PollImmediateSubMultiple) { +TEST_CASE_METHOD(ValueListenerTest, + "ValueListenerTest PollImmediateSubMultiple", + "[ntcore][value-listener]") { auto topic1 = wpi::nt::GetTopic(m_inst, "foo/1"); auto topic2 = wpi::nt::GetTopic(m_inst, "foo/2"); auto pub1 = wpi::nt::Publish(topic1, NT_DOUBLE, "double"); @@ -331,33 +337,34 @@ TEST_F(ValueListenerTest, PollImmediateSubMultiple) { wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 2u); - EXPECT_EQ(results[0].flags & (wpi::nt::EventFlags::VALUE_LOCAL | - wpi::nt::EventFlags::IMMEDIATE), - wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 2u); + CHECK((results[0].flags & + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)) == + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic1); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic1); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); - EXPECT_EQ(results[1].flags & (wpi::nt::EventFlags::VALUE_LOCAL | - wpi::nt::EventFlags::IMMEDIATE), - wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE); - EXPECT_EQ(results[1].listener, h); + CHECK((results[1].flags & + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)) == + (wpi::nt::EventFlags::VALUE_LOCAL | wpi::nt::EventFlags::IMMEDIATE)); + CHECK(results[1].listener == h); valueData = results[1].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub); - EXPECT_EQ(valueData->topic, topic2); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(1.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub); + CHECK(valueData->topic == topic2); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(1.0)); } -TEST_F(ValueListenerTest, TwoSubOneListener) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest TwoSubOneListener", + "[ntcore][value-listener]") { auto topic = wpi::nt::GetTopic(m_inst, "foo"); auto pub = wpi::nt::Publish(topic, NT_DOUBLE, "double"); auto sub1 = wpi::nt::Subscribe(topic, NT_DOUBLE, "double"); @@ -373,22 +380,23 @@ TEST_F(ValueListenerTest, TwoSubOneListener) { wpi::nt::SetDouble(pub, 0); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 1u); - EXPECT_EQ(results[0].flags & wpi::nt::EventFlags::VALUE_LOCAL, - wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 1u); + CHECK((results[0].flags & wpi::nt::EventFlags::VALUE_LOCAL) == + wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub1); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub1); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } -TEST_F(ValueListenerTest, TwoSubOneMultiListener) { +TEST_CASE_METHOD(ValueListenerTest, "ValueListenerTest TwoSubOneMultiListener", + "[ntcore][value-listener]") { auto topic = wpi::nt::GetTopic(m_inst, "foo"); auto pub = wpi::nt::Publish(topic, NT_DOUBLE, "double"); auto sub1 = wpi::nt::Subscribe(topic, NT_DOUBLE, "double"); @@ -404,19 +412,19 @@ TEST_F(ValueListenerTest, TwoSubOneMultiListener) { wpi::nt::SetDouble(pub, 0); bool timedOut = false; - ASSERT_TRUE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); - ASSERT_FALSE(timedOut); + REQUIRE(wpi::util::WaitForObject(poller, 1.0, &timedOut)); + REQUIRE_FALSE(timedOut); auto results = wpi::nt::ReadListenerQueue(poller); - ASSERT_EQ(results.size(), 1u); - EXPECT_EQ(results[0].flags & wpi::nt::EventFlags::VALUE_LOCAL, - wpi::nt::EventFlags::VALUE_LOCAL); - EXPECT_EQ(results[0].listener, h); + REQUIRE(results.size() == 1u); + CHECK((results[0].flags & wpi::nt::EventFlags::VALUE_LOCAL) == + wpi::nt::EventFlags::VALUE_LOCAL); + CHECK(results[0].listener == h); auto valueData = results[0].GetValueEventData(); - ASSERT_TRUE(valueData); - EXPECT_EQ(valueData->subentry, sub3); - EXPECT_EQ(valueData->topic, topic); - EXPECT_EQ(valueData->value, wpi::nt::Value::MakeDouble(0.0)); + REQUIRE(valueData); + CHECK(valueData->subentry == sub3); + CHECK(valueData->topic == topic); + CHECK(valueData->value == wpi::nt::Value::MakeDouble(0.0)); } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/ValueMatcher.cpp b/ntcore/src/test/native/cpp/ValueMatcher.cpp deleted file mode 100644 index c9b9a09a88..0000000000 --- a/ntcore/src/test/native/cpp/ValueMatcher.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#include "ValueMatcher.hpp" - -#include "TestPrinters.hpp" - -namespace wpi::nt { - -bool ValueMatcher::MatchAndExplain( - Value val, ::testing::MatchResultListener* listener) const { - if ((!val && goodval) || (val && !goodval) || - (val && goodval && val != goodval)) { - return false; - } - return true; -} - -void ValueMatcher::DescribeTo(::std::ostream* os) const { - PrintTo(goodval, os); -} - -void ValueMatcher::DescribeNegationTo(::std::ostream* os) const { - *os << "is not equal to "; - PrintTo(goodval, os); -} - -} // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/ValueMatcher.hpp b/ntcore/src/test/native/cpp/ValueMatcher.hpp deleted file mode 100644 index 0232e8f3fb..0000000000 --- a/ntcore/src/test/native/cpp/ValueMatcher.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) FIRST and other WPILib contributors. -// Open Source Software; you can modify and/or share it under the terms of -// the WPILib BSD license file in the root directory of this project. - -#pragma once - -#include -#include -#include - -#include "gmock/gmock.h" -#include "wpi/nt/NetworkTableValue.hpp" - -namespace wpi::nt { - -class ValueMatcher : public ::testing::MatcherInterface { - public: - explicit ValueMatcher(Value goodval_) : goodval(std::move(goodval_)) {} - - bool MatchAndExplain(Value msg, - ::testing::MatchResultListener* listener) const override; - void DescribeTo(::std::ostream* os) const override; - void DescribeNegationTo(::std::ostream* os) const override; - - private: - Value goodval; -}; - -inline ::testing::Matcher ValueEq(const Value& goodval) { - return ::testing::MakeMatcher(new ValueMatcher(goodval)); -} - -} // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/ValueTest.cpp b/ntcore/src/test/native/cpp/ValueTest.cpp index c8faab3578..30f5709007 100644 --- a/ntcore/src/test/native/cpp/ValueTest.cpp +++ b/ntcore/src/test/native/cpp/ValueTest.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include "TestPrinters.hpp" #include "Value_internal.hpp" @@ -29,210 +29,208 @@ inline bool operator==(std::span lhs, std::span rhs) { namespace wpi::nt { -class ValueTest : public ::testing::Test {}; - -using ValueDeathTest = ValueTest; - -TEST_F(ValueTest, ConstructEmpty) { +TEST_CASE("ValueTest ConstructEmpty", "[ntcore][value]") { Value v; - ASSERT_EQ(NT_UNASSIGNED, v.type()); + REQUIRE(NT_UNASSIGNED == v.type()); } -TEST_F(ValueTest, Boolean) { +TEST_CASE("ValueTest Boolean", "[ntcore][value]") { auto v = Value::MakeBoolean(false); - ASSERT_EQ(NT_BOOLEAN, v.type()); - ASSERT_FALSE(v.GetBoolean()); + REQUIRE(NT_BOOLEAN == v.type()); + REQUIRE_FALSE(v.GetBoolean()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_BOOLEAN, cv.type); - ASSERT_EQ(0, cv.data.v_boolean); + REQUIRE(NT_BOOLEAN == cv.type); + REQUIRE(0 == cv.data.v_boolean); v = Value::MakeBoolean(true); - ASSERT_EQ(NT_BOOLEAN, v.type()); - ASSERT_TRUE(v.GetBoolean()); + REQUIRE(NT_BOOLEAN == v.type()); + REQUIRE(v.GetBoolean()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_BOOLEAN, cv.type); - ASSERT_EQ(1, cv.data.v_boolean); + REQUIRE(NT_BOOLEAN == cv.type); + REQUIRE(1 == cv.data.v_boolean); NT_DisposeValue(&cv); } -TEST_F(ValueTest, Double) { +TEST_CASE("ValueTest Double", "[ntcore][value]") { auto v = Value::MakeDouble(0.5); - ASSERT_EQ(NT_DOUBLE, v.type()); - ASSERT_EQ(0.5, v.GetDouble()); + REQUIRE(NT_DOUBLE == v.type()); + REQUIRE(0.5 == v.GetDouble()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_DOUBLE, cv.type); - ASSERT_EQ(0.5, cv.data.v_double); + REQUIRE(NT_DOUBLE == cv.type); + REQUIRE(0.5 == cv.data.v_double); v = Value::MakeDouble(0.25); - ASSERT_EQ(NT_DOUBLE, v.type()); - ASSERT_EQ(0.25, v.GetDouble()); + REQUIRE(NT_DOUBLE == v.type()); + REQUIRE(0.25 == v.GetDouble()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_DOUBLE, cv.type); - ASSERT_EQ(0.25, cv.data.v_double); + REQUIRE(NT_DOUBLE == cv.type); + REQUIRE(0.25 == cv.data.v_double); NT_DisposeValue(&cv); } -TEST_F(ValueTest, String) { +TEST_CASE("ValueTest String", "[ntcore][value]") { auto v = Value::MakeString("hello"); - ASSERT_EQ(NT_STRING, v.type()); - ASSERT_EQ("hello", v.GetString()); + REQUIRE(NT_STRING == v.type()); + REQUIRE("hello" == v.GetString()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_STRING, cv.type); - ASSERT_EQ("hello"sv, wpi::util::to_string_view(&cv.data.v_string)); - ASSERT_EQ(5u, cv.data.v_string.len); + REQUIRE(NT_STRING == cv.type); + REQUIRE("hello"sv == wpi::util::to_string_view(&cv.data.v_string)); + REQUIRE(5u == cv.data.v_string.len); v = Value::MakeString("goodbye"); - ASSERT_EQ(NT_STRING, v.type()); - ASSERT_EQ("goodbye", v.GetString()); + REQUIRE(NT_STRING == v.type()); + REQUIRE("goodbye" == v.GetString()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_STRING, cv.type); - ASSERT_EQ("goodbye"sv, wpi::util::to_string_view(&cv.data.v_string)); - ASSERT_EQ(7u, cv.data.v_string.len); + REQUIRE(NT_STRING == cv.type); + REQUIRE("goodbye"sv == wpi::util::to_string_view(&cv.data.v_string)); + REQUIRE(7u == cv.data.v_string.len); NT_DisposeValue(&cv); } -TEST_F(ValueTest, Raw) { +TEST_CASE("ValueTest Raw", "[ntcore][value]") { std::vector arr{5, 4, 3, 2, 1}; auto v = Value::MakeRaw(arr); - ASSERT_EQ(NT_RAW, v.type()); - ASSERT_EQ(std::span(arr), v.GetRaw()); + REQUIRE(NT_RAW == v.type()); + REQUIRE(std::span(arr) == v.GetRaw()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_RAW, cv.type); - ASSERT_EQ(5u, cv.data.v_raw.size); - ASSERT_EQ(std::span(reinterpret_cast("\5\4\3\2\1"), 5), - std::span(cv.data.v_raw.data, 5)); + REQUIRE(NT_RAW == cv.type); + REQUIRE(5u == cv.data.v_raw.size); + REQUIRE(std::span(reinterpret_cast("\5\4\3\2\1"), 5) == + std::span(cv.data.v_raw.data, 5)); std::vector arr2{1, 2, 3, 4, 5, 6}; v = Value::MakeRaw(arr2); - ASSERT_EQ(NT_RAW, v.type()); - ASSERT_EQ(std::span(arr2), v.GetRaw()); + REQUIRE(NT_RAW == v.type()); + REQUIRE(std::span(arr2) == v.GetRaw()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_RAW, cv.type); - ASSERT_EQ(6u, cv.data.v_raw.size); - ASSERT_EQ(std::span(reinterpret_cast("\1\2\3\4\5\6"), 6), - std::span(cv.data.v_raw.data, 6)); + REQUIRE(NT_RAW == cv.type); + REQUIRE(6u == cv.data.v_raw.size); + REQUIRE(std::span(reinterpret_cast("\1\2\3\4\5\6"), 6) == + std::span(cv.data.v_raw.data, 6)); NT_DisposeValue(&cv); } -TEST_F(ValueTest, BooleanArray) { +TEST_CASE("ValueTest BooleanArray", "[ntcore][value]") { std::vector vec{1, 0, 1}; auto v = Value::MakeBooleanArray(vec); - ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetBooleanArray()); + REQUIRE(NT_BOOLEAN_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetBooleanArray()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_boolean.size); - ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]); - ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]); + REQUIRE(NT_BOOLEAN_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_boolean.size); + REQUIRE(vec[0] == cv.data.arr_boolean.arr[0]); + REQUIRE(vec[1] == cv.data.arr_boolean.arr[1]); + REQUIRE(vec[2] == cv.data.arr_boolean.arr[2]); // assign with same size vec = {0, 1, 0}; v = Value::MakeBooleanArray(vec); - ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetBooleanArray()); + REQUIRE(NT_BOOLEAN_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetBooleanArray()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_boolean.size); - ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]); - ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]); + REQUIRE(NT_BOOLEAN_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_boolean.size); + REQUIRE(vec[0] == cv.data.arr_boolean.arr[0]); + REQUIRE(vec[1] == cv.data.arr_boolean.arr[1]); + REQUIRE(vec[2] == cv.data.arr_boolean.arr[2]); // assign with different size vec = {1, 0}; v = Value::MakeBooleanArray(vec); - ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetBooleanArray()); + REQUIRE(NT_BOOLEAN_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetBooleanArray()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type); - ASSERT_EQ(2u, cv.data.arr_boolean.size); - ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]); + REQUIRE(NT_BOOLEAN_ARRAY == cv.type); + REQUIRE(2u == cv.data.arr_boolean.size); + REQUIRE(vec[0] == cv.data.arr_boolean.arr[0]); + REQUIRE(vec[1] == cv.data.arr_boolean.arr[1]); NT_DisposeValue(&cv); } -TEST_F(ValueTest, DoubleArray) { +TEST_CASE("ValueTest DoubleArray", "[ntcore][value]") { std::vector vec{0.5, 0.25, 0.5}; auto v = Value::MakeDoubleArray(vec); - ASSERT_EQ(NT_DOUBLE_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetDoubleArray()); + REQUIRE(NT_DOUBLE_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetDoubleArray()); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_double.size); - ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]); - ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]); + REQUIRE(NT_DOUBLE_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_double.size); + REQUIRE(vec[0] == cv.data.arr_double.arr[0]); + REQUIRE(vec[1] == cv.data.arr_double.arr[1]); + REQUIRE(vec[2] == cv.data.arr_double.arr[2]); // assign with same size vec = {0.25, 0.5, 0.25}; v = Value::MakeDoubleArray(vec); - ASSERT_EQ(NT_DOUBLE_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetDoubleArray()); + REQUIRE(NT_DOUBLE_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetDoubleArray()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_double.size); - ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]); - ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]); + REQUIRE(NT_DOUBLE_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_double.size); + REQUIRE(vec[0] == cv.data.arr_double.arr[0]); + REQUIRE(vec[1] == cv.data.arr_double.arr[1]); + REQUIRE(vec[2] == cv.data.arr_double.arr[2]); // assign with different size vec = {0.5, 0.25}; v = Value::MakeDoubleArray(vec); - ASSERT_EQ(NT_DOUBLE_ARRAY, v.type()); - ASSERT_EQ(std::span(vec), v.GetDoubleArray()); + REQUIRE(NT_DOUBLE_ARRAY == v.type()); + REQUIRE(std::span(vec) == v.GetDoubleArray()); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type); - ASSERT_EQ(2u, cv.data.arr_double.size); - ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]); - ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]); + REQUIRE(NT_DOUBLE_ARRAY == cv.type); + REQUIRE(2u == cv.data.arr_double.size); + REQUIRE(vec[0] == cv.data.arr_double.arr[0]); + REQUIRE(vec[1] == cv.data.arr_double.arr[1]); NT_DisposeValue(&cv); } -TEST_F(ValueTest, StringArray) { +TEST_CASE("ValueTest StringArray", "[ntcore][value]") { std::vector vec; vec.push_back("hello"); vec.push_back("goodbye"); vec.push_back("string"); auto v = Value::MakeStringArray(std::move(vec)); - ASSERT_EQ(NT_STRING_ARRAY, v.type()); - ASSERT_EQ(3u, v.GetStringArray().size()); - ASSERT_EQ("hello"sv, v.GetStringArray()[0]); - ASSERT_EQ("goodbye"sv, v.GetStringArray()[1]); - ASSERT_EQ("string"sv, v.GetStringArray()[2]); + REQUIRE(NT_STRING_ARRAY == v.type()); + REQUIRE(3u == v.GetStringArray().size()); + REQUIRE("hello"sv == v.GetStringArray()[0]); + REQUIRE("goodbye"sv == v.GetStringArray()[1]); + REQUIRE("string"sv == v.GetStringArray()[2]); NT_Value cv; NT_InitValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_STRING_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_string.size); - ASSERT_EQ("hello"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[0])); - ASSERT_EQ("goodbye"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[1])); - ASSERT_EQ("string"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[2])); + REQUIRE(NT_STRING_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_string.size); + REQUIRE("hello"sv == wpi::util::to_string_view(&cv.data.arr_string.arr[0])); + REQUIRE("goodbye"sv == + (wpi::util::to_string_view(&cv.data.arr_string.arr[1]))); + REQUIRE("string"sv == + (wpi::util::to_string_view(&cv.data.arr_string.arr[2]))); // assign with same size vec.clear(); @@ -240,188 +238,171 @@ TEST_F(ValueTest, StringArray) { vec.push_back("str2"); vec.push_back("string3"); v = Value::MakeStringArray(vec); - ASSERT_EQ(NT_STRING_ARRAY, v.type()); - ASSERT_EQ(3u, v.GetStringArray().size()); - ASSERT_EQ("s1"sv, v.GetStringArray()[0]); - ASSERT_EQ("str2"sv, v.GetStringArray()[1]); - ASSERT_EQ("string3"sv, v.GetStringArray()[2]); + REQUIRE(NT_STRING_ARRAY == v.type()); + REQUIRE(3u == v.GetStringArray().size()); + REQUIRE("s1"sv == v.GetStringArray()[0]); + REQUIRE("str2"sv == v.GetStringArray()[1]); + REQUIRE("string3"sv == v.GetStringArray()[2]); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_STRING_ARRAY, cv.type); - ASSERT_EQ(3u, cv.data.arr_string.size); - ASSERT_EQ("s1"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[0])); - ASSERT_EQ("str2"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[1])); - ASSERT_EQ("string3"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[2])); + REQUIRE(NT_STRING_ARRAY == cv.type); + REQUIRE(3u == cv.data.arr_string.size); + REQUIRE("s1"sv == wpi::util::to_string_view(&cv.data.arr_string.arr[0])); + REQUIRE("str2"sv == wpi::util::to_string_view(&cv.data.arr_string.arr[1])); + REQUIRE("string3"sv == + (wpi::util::to_string_view(&cv.data.arr_string.arr[2]))); // assign with different size vec.clear(); vec.push_back("short"); vec.push_back("er"); v = Value::MakeStringArray(std::move(vec)); - ASSERT_EQ(NT_STRING_ARRAY, v.type()); - ASSERT_EQ(2u, v.GetStringArray().size()); - ASSERT_EQ("short"sv, v.GetStringArray()[0]); - ASSERT_EQ("er"sv, v.GetStringArray()[1]); + REQUIRE(NT_STRING_ARRAY == v.type()); + REQUIRE(2u == v.GetStringArray().size()); + REQUIRE("short"sv == v.GetStringArray()[0]); + REQUIRE("er"sv == v.GetStringArray()[1]); NT_DisposeValue(&cv); ConvertToC(v, &cv); - ASSERT_EQ(NT_STRING_ARRAY, cv.type); - ASSERT_EQ(2u, cv.data.arr_string.size); - ASSERT_EQ("short"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[0])); - ASSERT_EQ("er"sv, wpi::util::to_string_view(&cv.data.arr_string.arr[1])); + REQUIRE(NT_STRING_ARRAY == cv.type); + REQUIRE(2u == cv.data.arr_string.size); + REQUIRE("short"sv == wpi::util::to_string_view(&cv.data.arr_string.arr[0])); + REQUIRE("er"sv == wpi::util::to_string_view(&cv.data.arr_string.arr[1])); NT_DisposeValue(&cv); } -// Google Test doesn't have ASSERT_DEATH when compiled with emscripten -#ifndef __EMSCRIPTEN__ -#ifdef NDEBUG -TEST_F(ValueDeathTest, DISABLED_GetAssertions) { -#else -TEST_F(ValueDeathTest, DISABLED_GetAssertions) { -#endif - Value v; - ASSERT_DEATH((void)v.GetBoolean(), "type == NT_BOOLEAN"); - ASSERT_DEATH((void)v.GetDouble(), "type == NT_DOUBLE"); - ASSERT_DEATH((void)v.GetString(), "type == NT_STRING"); - ASSERT_DEATH((void)v.GetRaw(), "type == NT_RAW"); - ASSERT_DEATH((void)v.GetBooleanArray(), "type == NT_BOOLEAN_ARRAY"); - ASSERT_DEATH((void)v.GetDoubleArray(), "type == NT_DOUBLE_ARRAY"); - ASSERT_DEATH((void)v.GetStringArray(), "type == NT_STRING_ARRAY"); -} -#endif - -TEST_F(ValueTest, UnassignedComparison) { +TEST_CASE("ValueTest UnassignedComparison", "[ntcore][value]") { Value v1, v2; - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } -TEST_F(ValueTest, MixedComparison) { +TEST_CASE("ValueTest MixedComparison", "[ntcore][value]") { Value v1; auto v2 = Value::MakeBoolean(true); - ASSERT_NE(v1, v2); // unassigned vs boolean + REQUIRE(v1 != v2); // unassigned vs boolean auto v3 = Value::MakeDouble(0.5); - ASSERT_NE(v2, v3); // boolean vs double + REQUIRE(v2 != v3); // boolean vs double } -TEST_F(ValueTest, BooleanComparison) { +TEST_CASE("ValueTest BooleanComparison", "[ntcore][value]") { auto v1 = Value::MakeBoolean(true); auto v2 = Value::MakeBoolean(true); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); v2 = Value::MakeBoolean(false); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); } -TEST_F(ValueTest, DoubleComparison) { +TEST_CASE("ValueTest DoubleComparison", "[ntcore][value]") { auto v1 = Value::MakeDouble(0.25); auto v2 = Value::MakeDouble(0.25); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); v2 = Value::MakeDouble(0.5); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); } -TEST_F(ValueTest, StringComparison) { +TEST_CASE("ValueTest StringComparison", "[ntcore][value]") { auto v1 = Value::MakeString("hello"); auto v2 = Value::MakeString("hello"); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); v2 = Value::MakeString("world"); // different contents - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); v2 = Value::MakeString("goodbye"); // different size - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); } -TEST_F(ValueTest, BooleanArrayComparison) { +TEST_CASE("ValueTest BooleanArrayComparison", "[ntcore][value]") { std::vector vec{1, 0, 1}; auto v1 = Value::MakeBooleanArray(vec); auto v2 = Value::MakeBooleanArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); // different contents vec = {1, 1, 1}; v2 = Value::MakeBooleanArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different size vec = {1, 0}; v2 = Value::MakeBooleanArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // empty vec = {}; v1 = Value::MakeBooleanArray(vec); v2 = Value::MakeBooleanArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } -TEST_F(ValueTest, IntegerArrayComparison) { +TEST_CASE("ValueTest IntegerArrayComparison", "[ntcore][value]") { std::vector vec{-42, 0, 1}; auto v1 = Value::MakeIntegerArray(vec); auto v2 = Value::MakeIntegerArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); // different contents vec = {-42, 1, 1}; v2 = Value::MakeIntegerArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different size vec = {-42, 0}; v2 = Value::MakeIntegerArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // empty vec = {}; v1 = Value::MakeIntegerArray(vec); v2 = Value::MakeIntegerArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } -TEST_F(ValueTest, FloatArrayComparison) { +TEST_CASE("ValueTest FloatArrayComparison", "[ntcore][value]") { std::vector vec{0.5, 0.25, 0.5}; auto v1 = Value::MakeFloatArray(vec); auto v2 = Value::MakeFloatArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); // different contents vec = {0.5, 0.5, 0.5}; v2 = Value::MakeFloatArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different size vec = {0.5, 0.25}; v2 = Value::MakeFloatArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // empty vec = {}; v1 = Value::MakeFloatArray(vec); v2 = Value::MakeFloatArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } -TEST_F(ValueTest, DoubleArrayComparison) { +TEST_CASE("ValueTest DoubleArrayComparison", "[ntcore][value]") { std::vector vec{0.5, 0.25, 0.5}; auto v1 = Value::MakeDoubleArray(vec); auto v2 = Value::MakeDoubleArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); // different contents vec = {0.5, 0.5, 0.5}; v2 = Value::MakeDoubleArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different size vec = {0.5, 0.25}; v2 = Value::MakeDoubleArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // empty vec = {}; v1 = Value::MakeDoubleArray(vec); v2 = Value::MakeDoubleArray(vec); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } -TEST_F(ValueTest, StringArrayComparison) { +TEST_CASE("ValueTest StringArrayComparison", "[ntcore][value]") { std::vector vec; vec.push_back("hello"); vec.push_back("goodbye"); @@ -432,7 +413,7 @@ TEST_F(ValueTest, StringArrayComparison) { vec.push_back("goodbye"); vec.push_back("string"); auto v2 = Value::MakeStringArray(std::move(vec)); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); // different contents vec.clear(); @@ -440,7 +421,7 @@ TEST_F(ValueTest, StringArrayComparison) { vec.push_back("goodby2"); vec.push_back("string"); v2 = Value::MakeStringArray(std::move(vec)); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different sized contents vec.clear(); @@ -448,20 +429,20 @@ TEST_F(ValueTest, StringArrayComparison) { vec.push_back("goodbye2"); vec.push_back("string"); v2 = Value::MakeStringArray(vec); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // different size vec.clear(); vec.push_back("hello"); vec.push_back("goodbye"); v2 = Value::MakeStringArray(std::move(vec)); - ASSERT_NE(v1, v2); + REQUIRE(v1 != v2); // empty vec.clear(); v1 = Value::MakeStringArray(vec); v2 = Value::MakeStringArray(std::move(vec)); - ASSERT_EQ(v1, v2); + REQUIRE(v1 == v2); } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/main.cpp b/ntcore/src/test/native/cpp/main.cpp index 3c0a5ec13d..3fb895d0ea 100644 --- a/ntcore/src/test/native/cpp/main.cpp +++ b/ntcore/src/test/native/cpp/main.cpp @@ -4,8 +4,10 @@ #include #include +#include + +#include -#include "gmock/gmock.h" #include "wpi/nt/ntcore.h" #include "wpi/util/timestamp.h" @@ -17,18 +19,19 @@ int main(int argc, char** argv) { std::fputc('\n', stderr); } }); - ::testing::InitGoogleMock(&argc, argv); - int ret = RUN_ALL_TESTS(); + int ret = Catch::Session().run(argc, argv); wpi::nt::ResetInstance(wpi::nt::GetDefaultInstance()); return ret; } extern "C" { void __ubsan_on_report(void) { - FAIL() << "Encountered an undefined behavior sanitizer error"; + std::puts("Encountered an undefined behavior sanitizer error"); + std::_Exit(EXIT_FAILURE); } void __asan_on_error(void) { - FAIL() << "Encountered an address sanitizer error"; + std::puts("Encountered an address sanitizer error"); + std::_Exit(EXIT_FAILURE); } void __tsan_on_report(void) { std::puts("Encountered a thread sanitizer error"); diff --git a/ntcore/src/test/native/cpp/net/MockMessageHandler.hpp b/ntcore/src/test/native/cpp/net/MockMessageHandler.hpp index da42575ea6..039c53d879 100644 --- a/ntcore/src/test/native/cpp/net/MockMessageHandler.hpp +++ b/ntcore/src/test/native/cpp/net/MockMessageHandler.hpp @@ -4,48 +4,190 @@ #pragma once +#include +#include #include +#include +#include #include "PubSubOptions.hpp" -#include "gmock/gmock.h" #include "net/MessageHandler.hpp" +#include "wpi/nt/NetworkTableValue.hpp" #include "wpi/util/json.hpp" namespace wpi::nt::net { class MockClientMessageHandler : public net::ClientMessageHandler { public: - MOCK_METHOD(void, ClientPublish, - (int pubuid, std::string_view name, std::string_view typeStr, - const wpi::util::json& properties, - const PubSubOptionsImpl& options), - (override)); - MOCK_METHOD(void, ClientUnpublish, (int pubuid), (override)); - MOCK_METHOD(void, ClientSetProperties, - (std::string_view name, const wpi::util::json& update), - (override)); - MOCK_METHOD(void, ClientSubscribe, - (int subuid, std::span prefixes, - const PubSubOptionsImpl& options), - (override)); - MOCK_METHOD(void, ClientUnsubscribe, (int subuid), (override)); - MOCK_METHOD(void, ClientSetValue, (int pubuid, const Value& value), - (override)); + struct PublishCall { + int pubuid; + std::string name; + std::string typeStr; + wpi::util::json properties; + PubSubOptionsImpl options; + + bool operator==(const PublishCall&) const = default; + }; + + struct UnpublishCall { + int pubuid; + + bool operator==(const UnpublishCall&) const = default; + }; + + struct SetPropertiesCall { + std::string name; + wpi::util::json update; + + bool operator==(const SetPropertiesCall&) const = default; + }; + + struct SubscribeCall { + int subuid; + std::vector prefixes; + PubSubOptionsImpl options; + + bool operator==(const SubscribeCall&) const = default; + }; + + struct UnsubscribeCall { + int subuid; + + bool operator==(const UnsubscribeCall&) const = default; + }; + + struct SetValueCall { + int pubuid; + Value value; + + bool operator==(const SetValueCall&) const = default; + }; + + using Call = std::variant; + + void ClientPublish(int pubuid, std::string_view name, + std::string_view typeStr, + const wpi::util::json& properties, + const PubSubOptionsImpl& options) override { + publishCalls.emplace_back(pubuid, std::string{name}, std::string{typeStr}, + properties, options); + calls.emplace_back(publishCalls.back()); + } + + void ClientUnpublish(int pubuid) override { + unpublishCalls.emplace_back(pubuid); + calls.emplace_back(UnpublishCall{pubuid}); + } + + void ClientSetProperties(std::string_view name, + const wpi::util::json& update) override { + setPropertiesCalls.emplace_back(std::string{name}, update); + calls.emplace_back(setPropertiesCalls.back()); + } + + void ClientSubscribe(int subuid, std::span prefixes, + const PubSubOptionsImpl& options) override { + subscribeCalls.emplace_back( + subuid, std::vector{prefixes.begin(), prefixes.end()}, + options); + calls.emplace_back(subscribeCalls.back()); + } + + void ClientUnsubscribe(int subuid) override { + unsubscribeCalls.emplace_back(subuid); + calls.emplace_back(UnsubscribeCall{subuid}); + } + + void ClientSetValue(int pubuid, const Value& value) override { + setValueCalls.emplace_back(pubuid, value); + calls.emplace_back(setValueCalls.back()); + } + + std::vector calls; + std::vector publishCalls; + std::vector unpublishCalls; + std::vector setPropertiesCalls; + std::vector subscribeCalls; + std::vector unsubscribeCalls; + std::vector setValueCalls; }; class MockServerMessageHandler : public net::ServerMessageHandler { public: - MOCK_METHOD(int, ServerAnnounce, - (std::string_view name, int id, std::string_view typeStr, - const wpi::util::json& properties, std::optional pubuid), - (override)); - MOCK_METHOD(void, ServerUnannounce, (std::string_view name, int id), - (override)); - MOCK_METHOD(void, ServerPropertiesUpdate, - (std::string_view name, const wpi::util::json& update, bool ack), - (override)); - MOCK_METHOD(void, ServerSetValue, (int topicuid, const Value& value), - (override)); + struct AnnounceCall { + std::string name; + int id; + std::string typeStr; + wpi::util::json properties; + std::optional pubuid; + + bool operator==(const AnnounceCall&) const = default; + }; + + struct UnannounceCall { + std::string name; + int id; + + bool operator==(const UnannounceCall&) const = default; + }; + + struct PropertiesUpdateCall { + std::string name; + wpi::util::json update; + bool ack; + + bool operator==(const PropertiesUpdateCall&) const = default; + }; + + struct SetValueCall { + int topicuid; + Value value; + + bool operator==(const SetValueCall&) const = default; + }; + + using Call = std::variant; + + int ServerAnnounce(std::string_view name, int id, std::string_view typeStr, + const wpi::util::json& properties, + std::optional pubuid) override { + announceCalls.emplace_back(std::string{name}, id, std::string{typeStr}, + properties, pubuid); + calls.emplace_back(announceCalls.back()); + if (!announceReturns.empty()) { + int rv = announceReturns.front(); + announceReturns.pop_front(); + return rv; + } + return defaultAnnounceReturn; + } + + void ServerUnannounce(std::string_view name, int id) override { + unannounceCalls.emplace_back(std::string{name}, id); + calls.emplace_back(unannounceCalls.back()); + } + + void ServerPropertiesUpdate(std::string_view name, + const wpi::util::json& update, + bool ack) override { + propertiesUpdateCalls.emplace_back(std::string{name}, update, ack); + calls.emplace_back(propertiesUpdateCalls.back()); + } + + void ServerSetValue(int topicuid, const Value& value) override { + setValueCalls.emplace_back(topicuid, value); + calls.emplace_back(setValueCalls.back()); + } + + int defaultAnnounceReturn = 0; + std::deque announceReturns; + std::vector calls; + std::vector announceCalls; + std::vector unannounceCalls; + std::vector propertiesUpdateCalls; + std::vector setValueCalls; }; } // namespace wpi::nt::net diff --git a/ntcore/src/test/native/cpp/net/MockNetworkInterface.hpp b/ntcore/src/test/native/cpp/net/MockNetworkInterface.hpp index 8453a4765f..4d627b45d7 100644 --- a/ntcore/src/test/native/cpp/net/MockNetworkInterface.hpp +++ b/ntcore/src/test/native/cpp/net/MockNetworkInterface.hpp @@ -4,27 +4,84 @@ #pragma once -#include "gmock/gmock.h" +#include +#include +#include +#include + #include "net/NetworkInterface.hpp" +#include "wpi/nt/NetworkTableValue.hpp" #include "wpi/util/json.hpp" namespace wpi::nt::net { class MockLocalStorage : public ILocalStorage { public: - MOCK_METHOD(int, ServerAnnounce, - (std::string_view name, int id, std::string_view typeStr, - const wpi::util::json& properties, std::optional pubuid), - (override)); - MOCK_METHOD(void, ServerUnannounce, (std::string_view name, int id), - (override)); - MOCK_METHOD(void, ServerPropertiesUpdate, - (std::string_view name, const wpi::util::json& update, bool ack), - (override)); - MOCK_METHOD(void, ServerSetValue, (int topicId, const Value& value), - (override)); - MOCK_METHOD(void, StartNetwork, (ClientMessageHandler * network), (override)); - MOCK_METHOD(void, ClearNetwork, (), (override)); + struct AnnounceCall { + std::string name; + int id; + std::string typeStr; + wpi::util::json properties; + std::optional pubuid; + }; + + struct UnannounceCall { + std::string name; + int id; + }; + + struct PropertiesUpdateCall { + std::string name; + wpi::util::json update; + bool ack; + }; + + struct SetValueCall { + int topicId; + Value value; + }; + + int ServerAnnounce(std::string_view name, int id, std::string_view typeStr, + const wpi::util::json& properties, + std::optional pubuid) override { + announceCalls.emplace_back(std::string{name}, id, std::string{typeStr}, + properties, pubuid); + if (!announceReturns.empty()) { + int rv = announceReturns.front(); + announceReturns.pop_front(); + return rv; + } + return defaultAnnounceReturn; + } + + void ServerUnannounce(std::string_view name, int id) override { + unannounceCalls.emplace_back(std::string{name}, id); + } + + void ServerPropertiesUpdate(std::string_view name, + const wpi::util::json& update, + bool ack) override { + propertiesUpdateCalls.emplace_back(std::string{name}, update, ack); + } + + void ServerSetValue(int topicId, const Value& value) override { + setValueCalls.emplace_back(topicId, value); + } + + void StartNetwork(ClientMessageHandler* network) override { + startNetworkCalls.emplace_back(network); + } + + void ClearNetwork() override { ++clearNetworkCalls; } + + int defaultAnnounceReturn = 0; + std::deque announceReturns; + std::vector announceCalls; + std::vector unannounceCalls; + std::vector propertiesUpdateCalls; + std::vector setValueCalls; + std::vector startNetworkCalls; + int clearNetworkCalls = 0; }; } // namespace wpi::nt::net diff --git a/ntcore/src/test/native/cpp/net/MockWireConnection.hpp b/ntcore/src/test/native/cpp/net/MockWireConnection.hpp index a67d59786d..95d666a305 100644 --- a/ntcore/src/test/native/cpp/net/MockWireConnection.hpp +++ b/ntcore/src/test/native/cpp/net/MockWireConnection.hpp @@ -6,12 +6,14 @@ #include +#include +#include #include #include #include +#include #include -#include "gmock/gmock.h" #include "net/WireConnection.hpp" #include "wpi/util/raw_ostream.hpp" @@ -19,11 +21,57 @@ namespace wpi::nt::net { class MockWireConnection : public WireConnection { public: - MOCK_METHOD(unsigned int, GetVersion, (), (const, override)); + struct SendPingCall { + uint64_t time; - MOCK_METHOD(void, SendPing, (uint64_t time), (override)); + bool operator==(const SendPingCall&) const = default; + }; - MOCK_METHOD(bool, Ready, (), (const, override)); + struct ReadyCall { + bool operator==(const ReadyCall&) const = default; + }; + + struct WriteTextCall { + std::string contents; + + bool operator==(const WriteTextCall&) const = default; + }; + + struct WriteBinaryCall { + std::vector contents; + + bool operator==(const WriteBinaryCall&) const = default; + }; + + struct FlushCall { + bool operator==(const FlushCall&) const = default; + }; + + struct GetLastReceivedTimeCall { + bool operator==(const GetLastReceivedTimeCall&) const = default; + }; + + using Call = + std::variant; + + unsigned int GetVersion() const override { return version; } + + void SendPing(uint64_t time) override { + sendPingCalls.emplace_back(time); + calls.emplace_back(SendPingCall{time}); + } + + bool Ready() const override { + ++readyCalls; + calls.emplace_back(ReadyCall{}); + if (!readyReturns.empty()) { + bool rv = readyReturns.front(); + readyReturns.pop_front(); + return rv; + } + return defaultReady; + } int WriteText(wpi::util::function_ref writer) override { @@ -55,21 +103,101 @@ class MockWireConnection : public WireConnection { DoSendBinary(binary); } - MOCK_METHOD(int, DoWriteText, (std::string_view contents)); - MOCK_METHOD(int, DoWriteBinary, (std::span contents)); + int DoWriteText(std::string_view contents) { + writeTextCalls.emplace_back(contents); + calls.emplace_back(WriteTextCall{writeTextCalls.back()}); + if (onWriteText) { + return onWriteText(contents); + } + if (!writeTextReturns.empty()) { + int rv = writeTextReturns.front(); + writeTextReturns.pop_front(); + return rv; + } + return defaultWriteTextReturn; + } - MOCK_METHOD(void, DoSendText, (std::string_view contents)); - MOCK_METHOD(void, DoSendBinary, (std::span contents)); + int DoWriteBinary(std::span contents) { + writeBinaryCalls.emplace_back(contents.begin(), contents.end()); + calls.emplace_back(WriteBinaryCall{writeBinaryCalls.back()}); + if (onWriteBinary) { + return onWriteBinary(contents); + } + if (!writeBinaryReturns.empty()) { + int rv = writeBinaryReturns.front(); + writeBinaryReturns.pop_front(); + return rv; + } + return defaultWriteBinaryReturn; + } - MOCK_METHOD(int, Flush, (), (override)); + void DoSendText(std::string_view contents) { + sendTextCalls.emplace_back(contents); + } - MOCK_METHOD(uint64_t, GetLastFlushTime, (), (const, override)); - MOCK_METHOD(uint64_t, GetLastReceivedTime, (), (const, override)); + void DoSendBinary(std::span contents) { + sendBinaryCalls.emplace_back(contents.begin(), contents.end()); + } - MOCK_METHOD(void, StopRead, (), (override)); - MOCK_METHOD(void, StartRead, (), (override)); + int Flush() override { + ++flushCalls; + calls.emplace_back(FlushCall{}); + if (!flushReturns.empty()) { + int rv = flushReturns.front(); + flushReturns.pop_front(); + return rv; + } + return defaultFlushReturn; + } - MOCK_METHOD(void, Disconnect, (std::string_view reason), (override)); + uint64_t GetLastFlushTime() const override { return lastFlushTime; } + + uint64_t GetLastReceivedTime() const override { + ++lastReceivedTimeCalls; + calls.emplace_back(GetLastReceivedTimeCall{}); + if (!lastReceivedTimeReturns.empty()) { + uint64_t rv = lastReceivedTimeReturns.front(); + lastReceivedTimeReturns.pop_front(); + return rv; + } + return defaultLastReceivedTime; + } + + void StopRead() override { ++stopReadCalls; } + + void StartRead() override { ++startReadCalls; } + + void Disconnect(std::string_view reason) override { + disconnectCalls.emplace_back(reason); + } + + unsigned int version = 0x0401; + mutable std::deque readyReturns; + bool defaultReady = true; + std::deque writeTextReturns; + int defaultWriteTextReturn = 0; + std::deque writeBinaryReturns; + int defaultWriteBinaryReturn = 0; + std::deque flushReturns; + int defaultFlushReturn = 0; + uint64_t lastFlushTime = 0; + mutable std::deque lastReceivedTimeReturns; + uint64_t defaultLastReceivedTime = 0; + std::function onWriteText; + std::function)> onWriteBinary; + + std::vector sendPingCalls; + mutable int readyCalls = 0; + std::vector writeTextCalls; + std::vector> writeBinaryCalls; + mutable std::vector calls; + std::vector sendTextCalls; + std::vector> sendBinaryCalls; + int flushCalls = 0; + mutable int lastReceivedTimeCalls = 0; + int stopReadCalls = 0; + int startReadCalls = 0; + std::vector disconnectCalls; }; } // namespace wpi::nt::net diff --git a/ntcore/src/test/native/cpp/net/NetworkOutgoingQueueTest.cpp b/ntcore/src/test/native/cpp/net/NetworkOutgoingQueueTest.cpp index 40f83c527d..983bc259c9 100644 --- a/ntcore/src/test/native/cpp/net/NetworkOutgoingQueueTest.cpp +++ b/ntcore/src/test/native/cpp/net/NetworkOutgoingQueueTest.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include "net/Message.hpp" #include "net/WireConnection.hpp" @@ -110,9 +110,10 @@ std::pair DecodeBinary(std::span data, int id = 0; Value value; std::string error; - EXPECT_TRUE(WireDecodeBinary(&data, &id, &value, &error, localTimeOffset)) - << error; - EXPECT_TRUE(data.empty()); + bool decoded = WireDecodeBinary(&data, &id, &value, &error, localTimeOffset); + UNSCOPED_INFO(error); + CHECK(decoded); + CHECK(data.empty()); return {id, value}; } @@ -124,67 +125,73 @@ ClientMessage Publish(int pubuid, std::string_view name) { } // namespace -TEST(NetworkOutgoingQueueTest, UpdatePeriodCalcUsesGcdAndMinimum) { - EXPECT_EQ(UpdatePeriodCalc(UINT32_MAX, 100), 100u); - EXPECT_EQ(UpdatePeriodCalc(100, 40), 20u); - EXPECT_EQ(UpdatePeriodCalc(6, 4), kMinPeriodMs); +TEST_CASE("NetworkOutgoingQueueTest UpdatePeriodCalcUsesGcdAndMinimum", + "[ntcore][network-outgoing-queue]") { + CHECK(UpdatePeriodCalc(UINT32_MAX, 100) == 100u); + CHECK(UpdatePeriodCalc(100, 40) == 20u); + CHECK(UpdatePeriodCalc(6, 4) == kMinPeriodMs); } -TEST(NetworkOutgoingQueueTest, CalculatePeriodUsesGcdAndMinimum) { +TEST_CASE("NetworkOutgoingQueueTest CalculatePeriodUsesGcdAndMinimum", + "[ntcore][network-outgoing-queue]") { std::vector periods{100, 40, 60}; - EXPECT_EQ(CalculatePeriod(periods, [](uint32_t period) { return period; }), - 20u); + CHECK(CalculatePeriod(periods, [](uint32_t period) { return period; }) == + 20u); periods = {2, 4}; - EXPECT_EQ(CalculatePeriod(periods, [](uint32_t period) { return period; }), - kMinPeriodMs); + CHECK(CalculatePeriod(periods, [](uint32_t period) { return period; }) == + kMinPeriodMs); } -TEST(NetworkOutgoingQueueTest, RemoteTextMessageWaitsForReadyAndMinimumPeriod) { +TEST_CASE( + "NetworkOutgoingQueueTest RemoteTextMessageWaitsForReadyAndMinimumPeriod", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; queue.SendMessage(5, Publish(5, "test")); queue.SendOutgoing(4, false); - EXPECT_TRUE(wire.textWrites.empty()); + CHECK(wire.textWrites.empty()); wire.ready = false; queue.SendOutgoing(5, false); - EXPECT_TRUE(wire.textWrites.empty()); + CHECK(wire.textWrites.empty()); wire.ready = true; queue.SendOutgoing(5, false); - ASSERT_EQ(wire.textWrites.size(), 1u); - EXPECT_EQ(wire.textWrites[0], - "{\"method\":\"publish\",\"params\":{\"name\":\"test\"," - "\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}"); - EXPECT_EQ(wire.flushCount, 1); + REQUIRE(wire.textWrites.size() == 1u); + CHECK(wire.textWrites[0] == + "{\"method\":\"publish\",\"params\":{\"name\":\"test\"," + "\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}"); + CHECK(wire.flushCount == 1); queue.SendOutgoing(10, false); - EXPECT_EQ(wire.textWrites.size(), 1u); + CHECK(wire.textWrites.size() == 1u); } -TEST(NetworkOutgoingQueueTest, FlushWaitsForMinimumPeriod) { +TEST_CASE("NetworkOutgoingQueueTest FlushWaitsForMinimumPeriod", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; queue.SendMessage(1, Publish(1, "first")); queue.SendOutgoing(5, false); - ASSERT_EQ(wire.textWrites.size(), 1u); + REQUIRE(wire.textWrites.size() == 1u); queue.SendMessage(2, Publish(2, "second")); queue.SendOutgoing(6, true); - ASSERT_EQ(wire.textWrites.size(), 1u); + REQUIRE(wire.textWrites.size() == 1u); queue.SendOutgoing(10, true); - ASSERT_EQ(wire.textWrites.size(), 2u); - EXPECT_EQ(wire.textWrites[1], - "{\"method\":\"publish\",\"params\":{\"name\":\"second\"," - "\"properties\":{},\"pubuid\":2,\"type\":\"double\"}}"); + REQUIRE(wire.textWrites.size() == 2u); + CHECK(wire.textWrites[1] == + "{\"method\":\"publish\",\"params\":{\"name\":\"second\"," + "\"properties\":{},\"pubuid\":2,\"type\":\"double\"}}"); } -TEST(NetworkOutgoingQueueTest, NormalValueKeepsOnlyNewestQueuedTimestamp) { +TEST_CASE("NetworkOutgoingQueueTest NormalValueKeepsOnlyNewestQueuedTimestamp", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -194,14 +201,15 @@ TEST(NetworkOutgoingQueueTest, NormalValueKeepsOnlyNewestQueuedTimestamp) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 1u); + REQUIRE(wire.binaryWrites.size() == 1u); auto [id, value] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id, 7); - EXPECT_EQ(value.time(), 200); - EXPECT_EQ(value.GetDouble(), 2.0); + CHECK(id == 7); + CHECK(value.time() == 200); + CHECK(value.GetDouble() == 2.0); } -TEST(NetworkOutgoingQueueTest, NormalValueShrinkUpdatesTotalSize) { +TEST_CASE("NetworkOutgoingQueueTest NormalValueShrinkUpdatesTotalSize", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -213,22 +221,23 @@ TEST(NetworkOutgoingQueueTest, NormalValueShrinkUpdatesTotalSize) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 3u); + REQUIRE(wire.binaryWrites.size() == 3u); auto [id0, value0] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id0, 1); - EXPECT_EQ(value0.GetDouble(), 1.0); + CHECK(id0 == 1); + CHECK(value0.GetDouble() == 1.0); auto [id1, value1] = DecodeBinary(wire.binaryWrites[1]); - EXPECT_EQ(id1, 1); - EXPECT_EQ(value1.GetDouble(), 2.0); + CHECK(id1 == 1); + CHECK(value1.GetDouble() == 2.0); auto [id2, value2] = DecodeBinary(wire.binaryWrites[2]); - EXPECT_EQ(id2, 1); - EXPECT_EQ(value2.GetDouble(), 3.0); + CHECK(id2 == 1); + CHECK(value2.GetDouble() == 3.0); } -TEST(NetworkOutgoingQueueTest, NormalValueReplacesAfterPeriodChange) { +TEST_CASE("NetworkOutgoingQueueTest NormalValueReplacesAfterPeriodChange", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -243,18 +252,20 @@ TEST(NetworkOutgoingQueueTest, NormalValueReplacesAfterPeriodChange) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 2u); + REQUIRE(wire.binaryWrites.size() == 2u); auto [id0, value0] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id0, 2); - EXPECT_EQ(value0.GetDouble(), 2.0); + CHECK(id0 == 2); + CHECK(value0.GetDouble() == 2.0); auto [id1, value1] = DecodeBinary(wire.binaryWrites[1]); - EXPECT_EQ(id1, 1); - EXPECT_EQ(value1.time(), 30); - EXPECT_EQ(value1.GetDouble(), 3.0); + CHECK(id1 == 1); + CHECK(value1.time() == 30); + CHECK(value1.GetDouble() == 3.0); } -TEST(NetworkOutgoingQueueTest, NormalValueReplacesLastValueAfterPeriodChange) { +TEST_CASE( + "NetworkOutgoingQueueTest NormalValueReplacesLastValueAfterPeriodChange", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -270,23 +281,25 @@ TEST(NetworkOutgoingQueueTest, NormalValueReplacesLastValueAfterPeriodChange) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 3u); + REQUIRE(wire.binaryWrites.size() == 3u); auto [id0, value0] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id0, 2); - EXPECT_EQ(value0.GetDouble(), 4.0); + CHECK(id0 == 2); + CHECK(value0.GetDouble() == 4.0); auto [id1, value1] = DecodeBinary(wire.binaryWrites[1]); - EXPECT_EQ(id1, 1); - EXPECT_EQ(value1.time(), 10); - EXPECT_EQ(value1.GetDouble(), 1.0); + CHECK(id1 == 1); + CHECK(value1.time() == 10); + CHECK(value1.GetDouble() == 1.0); auto [id2, value2] = DecodeBinary(wire.binaryWrites[2]); - EXPECT_EQ(id2, 1); - EXPECT_EQ(value2.time(), 30); - EXPECT_EQ(value2.GetDouble(), 3.0); + CHECK(id2 == 1); + CHECK(value2.time() == 30); + CHECK(value2.GetDouble() == 3.0); } -TEST(NetworkOutgoingQueueTest, PeriodChangeMovesMixedMessagesInStableOrder) { +TEST_CASE( + "NetworkOutgoingQueueTest PeriodChangeMovesMixedMessagesInStableOrder", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -303,25 +316,26 @@ TEST(NetworkOutgoingQueueTest, PeriodChangeMovesMixedMessagesInStableOrder) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.textWrites.size(), 3u); - EXPECT_EQ(wire.textWrites[0], - "{\"method\":\"publish\",\"params\":{\"name\":\"dest\"," - "\"properties\":{},\"pubuid\":2,\"type\":\"double\"}}"); - EXPECT_EQ(wire.textWrites[1], - "{\"method\":\"publish\",\"params\":{\"name\":\"first\"," - "\"properties\":{},\"pubuid\":1,\"type\":\"double\"}}"); - EXPECT_EQ(wire.textWrites[2], - "{\"method\":\"publish\",\"params\":{\"name\":\"second\"," - "\"properties\":{},\"pubuid\":1,\"type\":\"double\"}}"); + REQUIRE(wire.textWrites.size() == 3u); + CHECK(wire.textWrites[0] == + "{\"method\":\"publish\",\"params\":{\"name\":\"dest\"," + "\"properties\":{},\"pubuid\":2,\"type\":\"double\"}}"); + CHECK(wire.textWrites[1] == + "{\"method\":\"publish\",\"params\":{\"name\":\"first\"," + "\"properties\":{},\"pubuid\":1,\"type\":\"double\"}}"); + CHECK(wire.textWrites[2] == + "{\"method\":\"publish\",\"params\":{\"name\":\"second\"," + "\"properties\":{},\"pubuid\":1,\"type\":\"double\"}}"); - ASSERT_EQ(wire.binaryWrites.size(), 1u); + REQUIRE(wire.binaryWrites.size() == 1u); auto [id, value] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id, 1); - EXPECT_EQ(value.time(), 20); - EXPECT_EQ(value.GetDouble(), 2.0); + CHECK(id == 1); + CHECK(value.time() == 20); + CHECK(value.GetDouble() == 2.0); } -TEST(NetworkOutgoingQueueTest, SendAllValuesAppendBelowBackpressureLimit) { +TEST_CASE("NetworkOutgoingQueueTest SendAllValuesAppendBelowBackpressureLimit", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -330,17 +344,19 @@ TEST(NetworkOutgoingQueueTest, SendAllValuesAppendBelowBackpressureLimit) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 2u); + REQUIRE(wire.binaryWrites.size() == 2u); auto [id0, value0] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id0, 1); - EXPECT_EQ(value0.GetDouble(), 1.0); + CHECK(id0 == 1); + CHECK(value0.GetDouble() == 1.0); auto [id1, value1] = DecodeBinary(wire.binaryWrites[1]); - EXPECT_EQ(id1, 1); - EXPECT_EQ(value1.GetDouble(), 2.0); + CHECK(id1 == 1); + CHECK(value1.GetDouble() == 2.0); } -TEST(NetworkOutgoingQueueTest, SendAllValuesCoalesceAfterBackpressureLimit) { +TEST_CASE( + "NetworkOutgoingQueueTest SendAllValuesCoalesceAfterBackpressureLimit", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; @@ -352,13 +368,15 @@ TEST(NetworkOutgoingQueueTest, SendAllValuesCoalesceAfterBackpressureLimit) { queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 1u); + REQUIRE(wire.binaryWrites.size() == 1u); auto [id, value] = DecodeBinary(wire.binaryWrites[0]); - EXPECT_EQ(id, 1); - EXPECT_EQ(value.GetDouble(), 3.0); + CHECK(id == 1); + CHECK(value.GetDouble() == 3.0); } -TEST(NetworkOutgoingQueueTest, PartialWriteRetainsUnsentValueForReplacement) { +TEST_CASE( + "NetworkOutgoingQueueTest PartialWriteRetainsUnsentValueForReplacement", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; wire.binaryWriteReturns = {0, 1}; NetworkOutgoingQueue queue{wire, false}; @@ -367,19 +385,20 @@ TEST(NetworkOutgoingQueueTest, PartialWriteRetainsUnsentValueForReplacement) { queue.SendValue(2, Value::MakeDouble(2.0, 20), ValueSendMode::kNormal); queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 2u); + REQUIRE(wire.binaryWrites.size() == 2u); queue.SendValue(2, Value::MakeDouble(3.0, 30), ValueSendMode::kNormal); queue.SendOutgoing(10, true); - ASSERT_EQ(wire.binaryWrites.size(), 3u); + REQUIRE(wire.binaryWrites.size() == 3u); auto [id, value] = DecodeBinary(wire.binaryWrites[2]); - EXPECT_EQ(id, 2); - EXPECT_EQ(value.time(), 30); - EXPECT_EQ(value.GetDouble(), 3.0); + CHECK(id == 2); + CHECK(value.time() == 30); + CHECK(value.GetDouble() == 3.0); } -TEST(NetworkOutgoingQueueTest, FlushBackpressureRetainsLastUnsentValue) { +TEST_CASE("NetworkOutgoingQueueTest FlushBackpressureRetainsLastUnsentValue", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; wire.flushReturns = {1}; NetworkOutgoingQueue queue{wire, false}; @@ -388,32 +407,34 @@ TEST(NetworkOutgoingQueueTest, FlushBackpressureRetainsLastUnsentValue) { queue.SendValue(2, Value::MakeDouble(2.0, 20), ValueSendMode::kNormal); queue.SendOutgoing(5, true); - ASSERT_EQ(wire.binaryWrites.size(), 2u); + REQUIRE(wire.binaryWrites.size() == 2u); queue.SendOutgoing(10, true); - ASSERT_EQ(wire.binaryWrites.size(), 3u); + REQUIRE(wire.binaryWrites.size() == 3u); auto [id, value] = DecodeBinary(wire.binaryWrites[2]); - EXPECT_EQ(id, 2); - EXPECT_EQ(value.GetDouble(), 2.0); + CHECK(id == 2); + CHECK(value.GetDouble() == 2.0); } -TEST(NetworkOutgoingQueueTest, ClientImmediateValueAppliesTimeOffset) { +TEST_CASE("NetworkOutgoingQueueTest ClientImmediateValueAppliesTimeOffset", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, false}; queue.SetTimeOffset(5); queue.SendValue(3, Value::MakeDouble(1.0, 10), ValueSendMode::kImm); - ASSERT_EQ(wire.binarySends.size(), 1u); + REQUIRE(wire.binarySends.size() == 1u); auto [id, value] = DecodeBinary(wire.binarySends[0], -5); - EXPECT_EQ(id, 3); - EXPECT_EQ(value.time(), 10); - EXPECT_EQ(value.server_time(), 15); - EXPECT_EQ(value.GetDouble(), 1.0); + CHECK(id == 3); + CHECK(value.time() == 10); + CHECK(value.server_time() == 15); + CHECK(value.GetDouble() == 1.0); } -TEST(NetworkOutgoingQueueTest, LocalQueueSendsImmediately) { +TEST_CASE("NetworkOutgoingQueueTest LocalQueueSendsImmediately", + "[ntcore][network-outgoing-queue]") { RecordingWireConnection wire; NetworkOutgoingQueue queue{wire, true}; @@ -421,13 +442,13 @@ TEST(NetworkOutgoingQueueTest, LocalQueueSendsImmediately) { queue.SendValue(5, Value::MakeDouble(1.0, 10), ValueSendMode::kNormal); queue.SendOutgoing(5, true); - ASSERT_EQ(wire.textSends.size(), 1u); - EXPECT_EQ(wire.textSends[0], - "{\"method\":\"publish\",\"params\":{\"name\":\"local\"," - "\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}"); - ASSERT_EQ(wire.binarySends.size(), 1u); - EXPECT_TRUE(wire.textWrites.empty()); - EXPECT_TRUE(wire.binaryWrites.empty()); + REQUIRE(wire.textSends.size() == 1u); + CHECK(wire.textSends[0] == + "{\"method\":\"publish\",\"params\":{\"name\":\"local\"," + "\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}"); + REQUIRE(wire.binarySends.size() == 1u); + CHECK(wire.textWrites.empty()); + CHECK(wire.binaryWrites.empty()); } } // namespace wpi::nt::net diff --git a/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp b/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp index b71e6d4c52..8ad771a88e 100644 --- a/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp +++ b/ntcore/src/test/native/cpp/net/WireDecoderTest.cpp @@ -6,180 +6,237 @@ #include -#include +#include +#include "../MockAssertions.hpp" #include "../MockLogger.hpp" -#include "../PubSubOptionsMatcher.hpp" #include "../TestPrinters.hpp" #include "MockMessageHandler.hpp" #include "PubSubOptions.hpp" -#include "gmock/gmock.h" #include "net/MessageHandler.hpp" #include "wpi/nt/NetworkTableValue.hpp" #include "wpi/util/SmallString.hpp" #include "wpi/util/raw_ostream.hpp" using namespace std::string_view_literals; -using testing::_; -using testing::MockFunction; -using testing::StrictMock; namespace wpi::nt { -class WireDecodeTextClientTest : public ::testing::Test { +class WireDecodeTextClientTest { public: - StrictMock handler; - StrictMock logger; + net::MockClientMessageHandler handler; + wpi::MockLogger logger; }; -class WireDecodeTextServerTest : public ::testing::Test { +class WireDecodeTextServerTest { public: - StrictMock handler; - StrictMock logger; + net::MockServerMessageHandler handler; + wpi::MockLogger logger; }; -TEST_F(WireDecodeTextClientTest, EmptyArray) { +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest EmptyArray", + "[ntcore][wire][decoder]") { net::WireDecodeText("[]", handler, logger); + logger.CheckMessages({}); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorEmpty) { - EXPECT_CALL(logger, - Call(_, _, _, "could not decode JSON message: absent_value"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorEmpty", + "[ntcore][wire][decoder]") { net::WireDecodeText("", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, + "could not decode JSON message: absent_value"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorBadJson1) { - EXPECT_CALL(logger, - Call(_, _, _, "could not decode JSON message: unexpected_eof"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorBadJson1", + "[ntcore][wire][decoder]") { net::WireDecodeText("[", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, + "could not decode JSON message: unexpected_eof"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorBadJson2) { - EXPECT_CALL(logger, - Call(_, _, _, "could not decode JSON message: unexpected_eof"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorBadJson2", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, + "could not decode JSON message: unexpected_eof"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorNotArray) { - EXPECT_CALL(logger, Call(_, _, _, "expected JSON array at top level"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorNotArray", + "[ntcore][wire][decoder]") { net::WireDecodeText("{}", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "expected JSON array at top level"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorMessageNotObject) { - EXPECT_CALL(logger, Call(_, _, _, "0: expected message to be an object"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorMessageNotObject", + "[ntcore][wire][decoder]") { net::WireDecodeText("[5]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: expected message to be an object"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorNoMethodKey) { - EXPECT_CALL(logger, Call(_, _, _, "0: no method key"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorNoMethodKey", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: no method key"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorMethodNotString) { - EXPECT_CALL(logger, Call(_, _, _, "0: method must be a string"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorMethodNotString", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":5}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: method must be a string"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorNoParamsKey) { - EXPECT_CALL(logger, Call(_, _, _, "0: no params key"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorNoParamsKey", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":\"a\"}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: no params key"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorParamsNotObject) { - EXPECT_CALL(logger, Call(_, _, _, "0: params must be an object"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorParamsNotObject", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":\"a\",\"params\":5}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: params must be an object"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, ErrorUnknownMethod) { - EXPECT_CALL(logger, Call(_, _, _, "0: unrecognized method 'a'"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest ErrorUnknownMethod", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":\"a\",\"params\":{}}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: unrecognized method 'a'"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, PublishPropsEmpty) { - EXPECT_CALL( - handler, - ClientPublish(5, std::string_view{"test"}, std::string_view{"double"}, - wpi::util::json::object(), PubSubOptionsEq({}))); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest PublishPropsEmpty", + "[ntcore][wire][decoder]") { net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}]", handler, logger); + logger.CheckMessages({}); + CheckClientMessageCounts(handler, {.publish = 1}); + CheckPublish(handler.publishCalls.back(), 5, "test", "double", + wpi::util::json::object()); - EXPECT_CALL( - handler, - ClientPublish(5, std::string_view{"test"}, std::string_view{"double"}, - wpi::util::json::object(), PubSubOptionsEq({}))); net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"pubuid\":5,\"type\":\"double\"}}]", handler, logger); + logger.CheckMessages({}); + CheckClientMessageCounts(handler, {.publish = 2}); + CheckPublish(handler.publishCalls.back(), 5, "test", "double", + wpi::util::json::object()); } -TEST_F(WireDecodeTextClientTest, PublishProps) { +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest PublishProps", + "[ntcore][wire][decoder]") { auto props = wpi::util::json::object("k", 6); - EXPECT_CALL(handler, ClientPublish(5, std::string_view{"test"}, - std::string_view{"double"}, props, - PubSubOptionsEq({}))); net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"properties\":{\"k\":6}," "\"pubuid\":5,\"type\":\"double\"}}]", handler, logger); + logger.CheckMessages({}); + CheckClientMessageCounts(handler, {.publish = 1}); + CheckPublish(handler.publishCalls[0], 5, "test", "double", props); } -TEST_F(WireDecodeTextClientTest, PublishPropsError) { - EXPECT_CALL(logger, Call(_, _, _, "0: properties must be an object"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest PublishPropsError", + "[ntcore][wire][decoder]") { net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"properties\":[\"k\"]," "\"pubuid\":5,\"type\":\"double\"}}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: properties must be an object"sv); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, PublishError) { - EXPECT_CALL(logger, Call(_, _, _, "0: no name key"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest PublishError", + "[ntcore][wire][decoder]") { net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"pubuid\":5,\"type\":\"double\"}}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: no name key"sv); + CheckNoClientCalls(handler); - EXPECT_CALL(logger, Call(_, _, _, "0: no type key"sv)); net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"pubuid\":5}}]", handler, logger); + logger.CheckMessages({{NT_LOG_WARNING, "0: no name key"sv}, + {NT_LOG_WARNING, "0: no type key"sv}}); + CheckNoClientCalls(handler); - EXPECT_CALL(logger, Call(_, _, _, "0: no pubuid key"sv)); net::WireDecodeText( "[{\"method\":\"publish\",\"params\":{" "\"name\":\"test\",\"type\":\"double\"}}]", handler, logger); + logger.CheckMessages({{NT_LOG_WARNING, "0: no name key"sv}, + {NT_LOG_WARNING, "0: no type key"sv}, + {NT_LOG_WARNING, "0: no pubuid key"sv}}); + CheckNoClientCalls(handler); } -TEST_F(WireDecodeTextClientTest, Unpublish) { - EXPECT_CALL(handler, ClientUnpublish(5)); +TEST_CASE_METHOD(WireDecodeTextClientTest, "WireDecodeTextClientTest Unpublish", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}]", handler, logger); + logger.CheckMessages({}); + CheckClientMessageCounts(handler, {.unpublish = 1}); + CHECK(handler.unpublishCalls[0] == 5); } -TEST_F(WireDecodeTextClientTest, UnpublishMultiple) { - EXPECT_CALL(handler, ClientUnpublish(5)); - EXPECT_CALL(handler, ClientUnpublish(6)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest UnpublishMultiple", + "[ntcore][wire][decoder]") { net::WireDecodeText( "[{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}},{\"method\":" "\"unpublish\",\"params\":{\"pubuid\":6}}]", handler, logger); + logger.CheckMessages({}); + CheckClientMessageCounts(handler, {.unpublish = 2}); + CHECK(handler.unpublishCalls[0] == 5); + CHECK(handler.unpublishCalls[1] == 6); } -TEST_F(WireDecodeTextClientTest, UnpublishError) { - EXPECT_CALL(logger, Call(_, _, _, "0: no pubuid key"sv)); +TEST_CASE_METHOD(WireDecodeTextClientTest, + "WireDecodeTextClientTest UnpublishError", + "[ntcore][wire][decoder]") { net::WireDecodeText("[{\"method\":\"unpublish\",\"params\":{}}]", handler, logger); + logger.CheckMessage(NT_LOG_WARNING, "0: no pubuid key"sv); + CheckNoClientCalls(handler); - EXPECT_CALL(logger, Call(_, _, _, "0: pubuid must be a number"sv)); net::WireDecodeText( "[{\"method\":\"unpublish\",\"params\":{\"pubuid\":\"5\"}}]", handler, logger); + logger.CheckMessages({{NT_LOG_WARNING, "0: no pubuid key"sv}, + {NT_LOG_WARNING, "0: pubuid must be a number"sv}}); + CheckNoClientCalls(handler); } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp b/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp index a80cafb5cb..378e067169 100644 --- a/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp +++ b/ntcore/src/test/native/cpp/net/WireEncoderTest.cpp @@ -4,28 +4,43 @@ #include "net/WireEncoder.hpp" +#include + +#include +#include #include #include #include #include -#include +#include #include "../TestPrinters.hpp" #include "Handle.hpp" #include "PubSubOptions.hpp" -#include "gmock/gmock-matchers.h" #include "net/Message.hpp" #include "wpi/nt/NetworkTableValue.hpp" -#include "wpi/util/SpanMatcher.hpp" #include "wpi/util/json.hpp" #include "wpi/util/raw_ostream.hpp" using namespace std::string_view_literals; namespace wpi::nt { +namespace { -class WireEncoderTextTest : public ::testing::Test { +std::span operator""_us(const char* str, size_t len) { + return {reinterpret_cast(str), len}; +} + +bool SpanEquals(std::span actual, + std::span expected) { + return actual.size() == expected.size() && + std::equal(actual.begin(), actual.end(), expected.begin()); +} + +} // namespace + +class WireEncoderTextTest { protected: std::string out; wpi::util::raw_string_ostream os{out}; @@ -34,266 +49,304 @@ class WireEncoderTextTest : public ::testing::Test { } }; -class WireEncoderBinaryTest : public ::testing::Test { +class WireEncoderBinaryTest { protected: std::vector out; wpi::util::raw_uvector_ostream os{out}; }; -TEST_F(WireEncoderTextTest, PublishPropsEmpty) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest PublishPropsEmpty", + "[ntcore][wire][encoder]") { net::WireEncodePublish(os, 5, "test", "double", wpi::util::json::object()); - ASSERT_EQ( - os.str(), - "{\"method\":\"publish\",\"params\":{" - "\"name\":\"test\",\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}"); + REQUIRE(os.str() == + "{\"method\":\"publish\",\"params\":{" + "\"name\":\"test\",\"properties\":{},\"pubuid\":5," + "\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, PublishProps) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest PublishProps", + "[ntcore][wire][encoder]") { net::WireEncodePublish(os, 5, "test", "double", wpi::util::json::object("k", 6)); - ASSERT_EQ(os.str(), - "{\"method\":\"publish\",\"params\":{" - "\"name\":\"test\",\"properties\":{\"k\":6}," - "\"pubuid\":5,\"type\":\"double\"}}"); + REQUIRE(os.str() == + "{\"method\":\"publish\",\"params\":{" + "\"name\":\"test\",\"properties\":{\"k\":6}," + "\"pubuid\":5,\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, Unpublish) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest Unpublish", + "[ntcore][wire][encoder]") { net::WireEncodeUnpublish(os, 5); - ASSERT_EQ(os.str(), "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}"); + REQUIRE(os.str() == "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}"); } -TEST_F(WireEncoderTextTest, SetProperties) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest SetProperties", + "[ntcore][wire][encoder]") { net::WireEncodeSetProperties(os, "test", wpi::util::json::object("k", 6)); - ASSERT_EQ(os.str(), - "{\"method\":\"setproperties\",\"params\":{" - "\"name\":\"test\",\"update\":{\"k\":6}}}"); + REQUIRE(os.str() == + "{\"method\":\"setproperties\",\"params\":{" + "\"name\":\"test\",\"update\":{\"k\":6}}}"); } -TEST_F(WireEncoderTextTest, Subscribe) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest Subscribe", + "[ntcore][wire][encoder]") { net::WireEncodeSubscribe(os, 5, std::span{{"a", "b"}}, PubSubOptions{}); - ASSERT_EQ(os.str(), - "{\"method\":\"subscribe\",\"params\":{" - "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); + REQUIRE(os.str() == + "{\"method\":\"subscribe\",\"params\":{" + "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, SubscribeSendAll) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest SubscribeSendAll", + "[ntcore][wire][encoder]") { PubSubOptionsImpl options; options.sendAll = true; net::WireEncodeSubscribe(os, 5, std::span{{"a", "b"}}, options); - ASSERT_EQ(os.str(), - "{\"method\":\"subscribe\",\"params\":{" - "\"options\":{\"all\":true},\"topics\":[\"a\",\"b\"]," - "\"subuid\":5}}"); + REQUIRE(os.str() == + "{\"method\":\"subscribe\",\"params\":{" + "\"options\":{\"all\":true},\"topics\":[\"a\",\"b\"]," + "\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, SubscribePeriodic) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest SubscribePeriodic", + "[ntcore][wire][encoder]") { PubSubOptionsImpl options; options.periodicMs = 500u; net::WireEncodeSubscribe(os, 5, std::span{{"a", "b"}}, options); - ASSERT_EQ(os.str(), - "{\"method\":\"subscribe\",\"params\":{" - "\"options\":{\"periodic\":0.5},\"topics\":[\"a\",\"b\"]," - "\"subuid\":5}}"); + REQUIRE(os.str() == + "{\"method\":\"subscribe\",\"params\":{" + "\"options\":{\"periodic\":0.5},\"topics\":[\"a\",\"b\"]," + "\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, SubscribeAllOptions) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest SubscribeAllOptions", + "[ntcore][wire][encoder]") { PubSubOptionsImpl options; options.sendAll = true; options.periodicMs = 500u; net::WireEncodeSubscribe(os, 5, std::span{{"a", "b"}}, options); - ASSERT_EQ(os.str(), - "{\"method\":\"subscribe\",\"params\":{" - "\"options\":{\"all\":true,\"periodic\":0.5}," - "\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); + REQUIRE(os.str() == + "{\"method\":\"subscribe\",\"params\":{" + "\"options\":{\"all\":true,\"periodic\":0.5}," + "\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, Unsubscribe) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest Unsubscribe", + "[ntcore][wire][encoder]") { net::WireEncodeUnsubscribe(os, 5); - ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}"); + REQUIRE(os.str() == "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, Announce) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest Announce", + "[ntcore][wire][encoder]") { net::WireEncodeAnnounce(os, "test", 5, "double", wpi::util::json::object(), std::nullopt); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{},\"type\":\"double\"}}"); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{},\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, AnnounceProperties) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest AnnounceProperties", + "[ntcore][wire][encoder]") { net::WireEncodeAnnounce(os, "test", 5, "double", wpi::util::json::object("k", 6), std::nullopt); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{\"k\":6},\"type\":\"double\"}}"); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{\"k\":6},\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, AnnouncePubuid) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest AnnouncePubuid", + "[ntcore][wire][encoder]") { net::WireEncodeAnnounce(os, "test", 5, "double", wpi::util::json::object(), 6); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}"); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, Unannounce) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest Unannounce", + "[ntcore][wire][encoder]") { net::WireEncodeUnannounce(os, "test", 5); - ASSERT_EQ( - os.str(), + REQUIRE( + os.str() == "{\"method\":\"unannounce\",\"params\":{\"id\":5,\"name\":\"test\"}}"); } -TEST_F(WireEncoderTextTest, MessagePublish) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessagePublish", + "[ntcore][wire][encoder]") { net::ClientMessage msg{net::PublishMsg{ 5, "test", "double", wpi::util::json::object("k", 6), {}}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"publish\",\"params\":{" - "\"name\":\"test\",\"properties\":{\"k\":6}," - "\"pubuid\":5,\"type\":\"double\"}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"publish\",\"params\":{" + "\"name\":\"test\",\"properties\":{\"k\":6}," + "\"pubuid\":5,\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, MessageUnpublish) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessageUnpublish", + "[ntcore][wire][encoder]") { net::ClientMessage msg{net::UnpublishMsg{5}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}"); } -TEST_F(WireEncoderTextTest, MessageSetProperties) { +TEST_CASE_METHOD(WireEncoderTextTest, + "WireEncoderTextTest MessageSetProperties", + "[ntcore][wire][encoder]") { net::ClientMessage msg{ net::SetPropertiesMsg{"test", wpi::util::json::object("k", 6)}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"setproperties\",\"params\":{" - "\"name\":\"test\",\"update\":{\"k\":6}}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"setproperties\",\"params\":{" + "\"name\":\"test\",\"update\":{\"k\":6}}}"); } -TEST_F(WireEncoderTextTest, MessageSubscribe) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessageSubscribe", + "[ntcore][wire][encoder]") { net::ClientMessage msg{net::SubscribeMsg{5, {"a", "b"}, {}}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"subscribe\",\"params\":{" - "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"subscribe\",\"params\":{" + "\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, MessageUnsubscribe) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessageUnsubscribe", + "[ntcore][wire][encoder]") { net::ClientMessage msg{net::UnsubscribeMsg{5}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}"); } -TEST_F(WireEncoderTextTest, MessageAnnounce) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessageAnnounce", + "[ntcore][wire][encoder]") { net::ServerMessage msg{net::AnnounceMsg{"test", 5, "double", std::nullopt, wpi::util::json::object()}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{},\"type\":\"double\"}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{},\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, MessageAnnounceProperties) { +TEST_CASE_METHOD(WireEncoderTextTest, + "WireEncoderTextTest MessageAnnounceProperties", + "[ntcore][wire][encoder]") { net::ServerMessage msg{net::AnnounceMsg{"test", 5, "double", std::nullopt, wpi::util::json::object("k", 6)}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{\"k\":6},\"type\":\"double\"}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{\"k\":6},\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, MessageAnnouncePubuid) { +TEST_CASE_METHOD(WireEncoderTextTest, + "WireEncoderTextTest MessageAnnouncePubuid", + "[ntcore][wire][encoder]") { net::ServerMessage msg{ net::AnnounceMsg{"test", 5, "double", 6, wpi::util::json::object()}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ(os.str(), - "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," - "\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}"); + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE(os.str() == + "{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\"," + "\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}"); } -TEST_F(WireEncoderTextTest, MessageUnannounce) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest MessageUnannounce", + "[ntcore][wire][encoder]") { net::ServerMessage msg{net::UnannounceMsg{"test", 5}}; - ASSERT_TRUE(net::WireEncodeText(os, msg)); - ASSERT_EQ( - os.str(), + REQUIRE(net::WireEncodeText(os, msg)); + REQUIRE( + os.str() == "{\"method\":\"unannounce\",\"params\":{\"id\":5,\"name\":\"test\"}}"); } -TEST_F(WireEncoderTextTest, ServerMessageEmpty) { - ASSERT_FALSE(net::WireEncodeText(os, net::ServerMessage{})); +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest ServerMessageEmpty", + "[ntcore][wire][encoder]") { + REQUIRE_FALSE(net::WireEncodeText(os, net::ServerMessage{})); } -TEST_F(WireEncoderTextTest, ServerMessageValue) { +TEST_CASE_METHOD(WireEncoderTextTest, "WireEncoderTextTest ServerMessageValue", + "[ntcore][wire][encoder]") { net::ServerMessage msg{net::ServerValueMsg{}}; - ASSERT_FALSE(net::WireEncodeText(os, msg)); + REQUIRE_FALSE(net::WireEncodeText(os, msg)); } -TEST_F(WireEncoderBinaryTest, Boolean) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest Boolean", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeBoolean(true)); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x00\xc3"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x00\xc3"_us)); } -TEST_F(WireEncoderBinaryTest, Integer) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest Integer", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeInteger(7)); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x02\x07"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x02\x07"_us)); } -TEST_F(WireEncoderBinaryTest, Float) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest Float", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeFloat(2.5)); - ASSERT_THAT(out, - wpi::util::SpanEq("\x94\x05\x06\x03\xca\x40\x20\x00\x00"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x03\xca\x40\x20\x00\x00"_us)); } -TEST_F(WireEncoderBinaryTest, Double) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest Double", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeDouble(2.5)); - ASSERT_THAT(out, - wpi::util::SpanEq( - "\x94\x05\x06\x01\xcb\x40\x04\x00\x00\x00\x00\x00\x00"_us)); + REQUIRE(SpanEquals( + out, "\x94\x05\x06\x01\xcb\x40\x04\x00\x00\x00\x00\x00\x00"_us)); } -TEST_F(WireEncoderBinaryTest, String) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest String", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeString("hello")); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x04\xa5hello"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x04\xa5hello"_us)); } -TEST_F(WireEncoderBinaryTest, Raw) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest Raw", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeRaw("hello"_us)); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x05\xc4\x05hello"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x05\xc4\x05hello"_us)); } -TEST_F(WireEncoderBinaryTest, BooleanArray) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest BooleanArray", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeBooleanArray({true, false, true})); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x10\x93\xc3\xc2\xc3"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x10\x93\xc3\xc2\xc3"_us)); } -TEST_F(WireEncoderBinaryTest, IntegerArray) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest IntegerArray", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeIntegerArray({1, 2, 4})); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x12\x93\x01\x02\x04"_us)); + REQUIRE(SpanEquals(out, "\x94\x05\x06\x12\x93\x01\x02\x04"_us)); } -TEST_F(WireEncoderBinaryTest, FloatArray) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest FloatArray", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeFloatArray({1, 2, 3})); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x13\x93" - "\xca\x3f\x80\x00\x00" - "\xca\x40\x00\x00\x00" - "\xca\x40\x40\x00\x00"_us)); + REQUIRE(SpanEquals(out, + "\x94\x05\x06\x13\x93" + "\xca\x3f\x80\x00\x00" + "\xca\x40\x00\x00\x00" + "\xca\x40\x40\x00\x00"_us)); } -TEST_F(WireEncoderBinaryTest, DoubleArray) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest DoubleArray", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeDoubleArray({1, 2, 3})); - ASSERT_THAT(out, - wpi::util::SpanEq("\x94\x05\x06\x11\x93" - "\xcb\x3f\xf0\x00\x00\x00\x00\x00\x00" - "\xcb\x40\x00\x00\x00\x00\x00\x00\x00" - "\xcb\x40\x08\x00\x00\x00\x00\x00\x00"_us)); + REQUIRE(SpanEquals(out, + "\x94\x05\x06\x11\x93" + "\xcb\x3f\xf0\x00\x00\x00\x00\x00\x00" + "\xcb\x40\x00\x00\x00\x00\x00\x00\x00" + "\xcb\x40\x08\x00\x00\x00\x00\x00\x00"_us)); } -TEST_F(WireEncoderBinaryTest, StringArray) { +TEST_CASE_METHOD(WireEncoderBinaryTest, "WireEncoderBinaryTest StringArray", + "[ntcore][wire][encoder]") { net::WireEncodeBinary(os, 5, 6, Value::MakeStringArray({"hello", "bye"})); - ASSERT_THAT(out, wpi::util::SpanEq("\x94\x05\x06\x14\x92\xa5hello\xa3" - "bye"_us)); + REQUIRE(SpanEquals(out, + "\x94\x05\x06\x14\x92\xa5hello\xa3" + "bye"_us)); } } // namespace wpi::nt diff --git a/ntcore/src/test/native/cpp/server/ServerImplTest.cpp b/ntcore/src/test/native/cpp/server/ServerImplTest.cpp index c256936918..5cf4364e84 100644 --- a/ntcore/src/test/native/cpp/server/ServerImplTest.cpp +++ b/ntcore/src/test/native/cpp/server/ServerImplTest.cpp @@ -4,84 +4,125 @@ #include "server/ServerImpl.hpp" +#include #include #include +#include #include #include #include +#include #include -#include +#include +#include "../MockAssertions.hpp" #include "../MockLogger.hpp" -#include "../PubSubOptionsMatcher.hpp" #include "../TestPrinters.hpp" -#include "../ValueMatcher.hpp" #include "../net/MockClientMessageQueue.hpp" #include "../net/MockMessageHandler.hpp" #include "../net/MockWireConnection.hpp" #include "Handle.hpp" -#include "gmock/gmock.h" #include "net/Message.hpp" #include "net/WireEncoder.hpp" #include "wpi/nt/ntcore_c.h" #include "wpi/nt/ntcore_cpp.hpp" -#include "wpi/util/SpanMatcher.hpp" - -using ::testing::_; -using ::testing::AllOf; -using ::testing::ElementsAre; -using ::testing::Field; -using ::testing::IsEmpty; -using ::testing::Property; -using ::testing::Return; -using ::testing::StrEq; - -using MockSetPeriodicFunc = ::testing::MockFunction; -using MockConnected3Func = - ::testing::MockFunction; namespace wpi::nt { +namespace { -class ServerImplTest : public ::testing::Test { +class SetPeriodicRecorder { public: - ::testing::StrictMock local; - ::testing::StrictMock queue; + std::function AsStdFunction() { + return [this](uint32_t repeatMs) { calls.emplace_back(repeatMs); }; + } + + std::vector calls; +}; + +struct WireConnectionCounts { + size_t sendPing = 0; + size_t ready = 0; + size_t writeText = 0; + size_t writeBinary = 0; + size_t flush = 0; + size_t lastReceivedTime = 0; + size_t sendText = 0; + size_t sendBinary = 0; + size_t stopRead = 0; + size_t startRead = 0; + size_t disconnect = 0; +}; + +void CheckWireConnectionCounts(const net::MockWireConnection& wire, + const WireConnectionCounts& expected = {}) { + REQUIRE(wire.sendPingCalls.size() == expected.sendPing); + REQUIRE(static_cast(wire.readyCalls) == expected.ready); + REQUIRE(wire.writeTextCalls.size() == expected.writeText); + REQUIRE(wire.writeBinaryCalls.size() == expected.writeBinary); + REQUIRE(static_cast(wire.flushCalls) == expected.flush); + REQUIRE(static_cast(wire.lastReceivedTimeCalls) == + expected.lastReceivedTime); + REQUIRE(wire.sendTextCalls.size() == expected.sendText); + REQUIRE(wire.sendBinaryCalls.size() == expected.sendBinary); + REQUIRE(static_cast(wire.stopReadCalls) == expected.stopRead); + REQUIRE(static_cast(wire.startReadCalls) == expected.startRead); + REQUIRE(wire.disconnectCalls.size() == expected.disconnect); +} + +template +void CheckCallOrder(const std::vector>& calls) { + REQUIRE(calls.size() == sizeof...(Expected)); + size_t index = 0; + ( + [&] { + CHECK(std::holds_alternative(calls[index])); + ++index; + }(), + ...); +} + +} // namespace + +class ServerImplTest { + public: + net::MockServerMessageHandler local; + net::MockClientMessageQueue queue; wpi::MockLogger logger; server::ServerImpl server{logger}; }; -TEST_F(ServerImplTest, AddClient) { - ::testing::StrictMock wire; - // EXPECT_CALL(wire, Flush()); - MockSetPeriodicFunc setPeriodic; +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest AddClient", + "[ntcore][server]") { + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; auto [name, id] = server.AddClient("test", "connInfo", false, wire, setPeriodic.AsStdFunction()); - EXPECT_EQ(name, "test@1"); - EXPECT_NE(id, -1); + CHECK(name == "test@1"); + CHECK(id != -1); } -TEST_F(ServerImplTest, AddDuplicateClient) { - ::testing::StrictMock wire1; - ::testing::StrictMock wire2; - MockSetPeriodicFunc setPeriodic1; - MockSetPeriodicFunc setPeriodic2; - // EXPECT_CALL(wire1, Flush()); - // EXPECT_CALL(wire2, Flush()); +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest AddDuplicateClient", + "[ntcore][server]") { + net::MockWireConnection wire1; + net::MockWireConnection wire2; + SetPeriodicRecorder setPeriodic1; + SetPeriodicRecorder setPeriodic2; auto [name1, id1] = server.AddClient("test", "connInfo", false, wire1, setPeriodic1.AsStdFunction()); auto [name2, id2] = server.AddClient("test", "connInfo2", false, wire2, setPeriodic2.AsStdFunction()); - EXPECT_EQ(name1, "test@1"); - EXPECT_NE(id1, -1); - EXPECT_EQ(name2, "test@2"); - EXPECT_NE(id1, id2); - EXPECT_NE(id2, -1); + CHECK(name1 == "test@1"); + CHECK(id1 != -1); + CHECK(name2 == "test@2"); + CHECK(id1 != id2); + CHECK(id2 != -1); } -TEST_F(ServerImplTest, AddClient3) {} +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest AddClient3", + "[ntcore][server]") {} template static std::string EncodeText1(const T& msg) { @@ -145,66 +186,24 @@ static std::vector EncodeServerBinary(const T& msgs) { return data; } -TEST_F(ServerImplTest, PublishLocal) { +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest PublishLocal", + "[ntcore][server]") { // publish before client connect server.SetLocal(&local, &queue); constexpr int pubuid = 1; constexpr int pubuid2 = 2; constexpr int pubuid3 = 3; - { - ::testing::InSequence seq; - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{pubuid})); - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test2"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{pubuid2})); - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test3"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{pubuid3})); - } { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuid, "test", "double", wpi::util::json::object(), {}}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } // client connect; it should get already-published topic as soon as it // subscribes - ::testing::StrictMock wire; - MockSetPeriodicFunc setPeriodic; - EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401)); - { - ::testing::InSequence seq; - // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // AddClient() - EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe() - // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // ClientSubscribe() - EXPECT_CALL(wire, GetLastReceivedTime()).WillOnce(Return(0)); - EXPECT_CALL(wire, SendPing(100)); - EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendControl() - EXPECT_CALL( - wire, - DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{ - "test", 3, "double", std::nullopt, wpi::util::json::object()}})))) - .WillOnce(Return(0)); - EXPECT_CALL( - wire, - DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{ - "test2", 8, "double", std::nullopt, wpi::util::json::object()}})))) - .WillOnce(Return(0)); - EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // SendControl() - EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendControl() - EXPECT_CALL( - wire, - DoWriteText(StrEq(EncodeText1(net::ServerMessage{net::AnnounceMsg{ - "test3", 11, "double", std::nullopt, wpi::util::json::object()}})))) - .WillOnce(Return(0)); - EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // SendControl() - } + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; auto [name, id] = server.AddClient("test", "connInfo", false, wire, setPeriodic.AsStdFunction()); @@ -220,7 +219,7 @@ TEST_F(ServerImplTest, PublishLocal) { { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuid2, "test2", "double", wpi::util::json::object(), {}}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } server.SendAllOutgoing(100, false); @@ -229,56 +228,65 @@ TEST_F(ServerImplTest, PublishLocal) { { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuid3, "test3", "double", wpi::util::json::object(), {}}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } server.SendAllOutgoing(200, false); + + CheckServerMessageCounts(local, {.announce = 3}); + CheckCallOrder(local.calls); + CHECK(local.announceCalls[0].name == "test"); + CHECK(local.announceCalls[0].pubuid == std::optional{pubuid}); + CHECK(local.announceCalls[1].name == "test2"); + CHECK(local.announceCalls[1].pubuid == std::optional{pubuid2}); + CHECK(local.announceCalls[2].name == "test3"); + CHECK(local.announceCalls[2].pubuid == std::optional{pubuid3}); + + REQUIRE(setPeriodic.calls.size() == 1u); + CHECK(setPeriodic.calls[0] == 100u); + CheckWireConnectionCounts(wire, {.sendPing = 1, + .ready = 2, + .writeText = 3, + .flush = 2, + .lastReceivedTime = 1}); + CHECK(wire.sendPingCalls == std::vector{100}); + CheckCallOrder< + net::MockWireConnection::GetLastReceivedTimeCall, + net::MockWireConnection::SendPingCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteTextCall, + net::MockWireConnection::WriteTextCall, + net::MockWireConnection::FlushCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteTextCall, + net::MockWireConnection::FlushCall>(wire.calls); + CHECK(wire.writeTextCalls[0] == + EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test", 3, "double", std::nullopt, wpi::util::json::object()}})); + CHECK(wire.writeTextCalls[1] == + EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test2", 8, "double", std::nullopt, wpi::util::json::object()}})); + CHECK(wire.writeTextCalls[2] == + EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test3", 11, "double", std::nullopt, wpi::util::json::object()}})); } -TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) { +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest ClientSubTopicOnlyThenValue", + "[ntcore][server]") { // publish before client connect server.SetLocal(&local, &queue); constexpr int pubuid = 1; - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{pubuid})); { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuid, "test", "double", wpi::util::json::object(), {}}}); queue.msgs.emplace_back(net::ClientMessage{ net::ClientValueMsg{pubuid, Value::MakeDouble(1.0, 10)}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } - ::testing::StrictMock wire; - EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401)); - MockSetPeriodicFunc setPeriodic; - { - ::testing::InSequence seq; - // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // AddClient() - EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe() - // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // ClientSubscribe() - 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", 3, "double", std::nullopt, wpi::util::json::object()}})))) - .WillOnce(Return(0)); - EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // SendValues() - EXPECT_CALL(setPeriodic, Call(100)); // ClientSubscribe() - // EXPECT_CALL(wire, Flush()).WillOnce(Return(0)); // ClientSubscribe() - EXPECT_CALL(wire, Ready()).WillOnce(Return(true)); // SendValues() - EXPECT_CALL( - wire, - DoWriteBinary(wpi::util::SpanEq(EncodeServerBinary1(net::ServerMessage{ - net::ServerValueMsg{3, Value::MakeDouble(1.0, 10)}})))) - .WillOnce(Return(0)); - EXPECT_CALL(wire, Flush()); // SendValues() - } + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; // connect client auto [name, id] = server.AddClient("test", "connInfo", false, wire, @@ -307,53 +315,56 @@ TEST_F(ServerImplTest, ClientSubTopicOnlyThenValue) { } server.SendOutgoing(id, 200); + + CheckServerMessageCounts(local, {.announce = 1}); + CheckCallOrder(local.calls); + CHECK(local.announceCalls[0].name == "test"); + CHECK(local.announceCalls[0].pubuid == std::optional{pubuid}); + CHECK(setPeriodic.calls == std::vector{100, 100}); + CheckWireConnectionCounts(wire, {.sendPing = 1, + .ready = 2, + .writeText = 1, + .writeBinary = 1, + .flush = 2, + .lastReceivedTime = 1}); + CHECK(wire.sendPingCalls == std::vector{100}); + CheckCallOrder< + net::MockWireConnection::GetLastReceivedTimeCall, + net::MockWireConnection::SendPingCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteTextCall, + net::MockWireConnection::FlushCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteBinaryCall, + net::MockWireConnection::FlushCall>(wire.calls); + CHECK(wire.writeTextCalls[0] == + EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test", 3, "double", std::nullopt, wpi::util::json::object()}})); + CHECK(wire.writeBinaryCalls[0] == + EncodeServerBinary1(net::ServerMessage{ + net::ServerValueMsg{3, Value::MakeDouble(1.0, 10)}})); } -TEST_F(ServerImplTest, ClientDisconnectUnpublish) { +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest ClientDisconnectUnpublish", + "[ntcore][server]") { server.SetLocal(&local, &queue); constexpr int pubuidLocal = 1; constexpr int subuid = 1; - { - ::testing::InSequence seq; - EXPECT_CALL(local, ServerAnnounce(std::string_view{"test2"}, 0, - std::string_view{"double"}, - wpi::util::json::object(), - std::optional{pubuidLocal})); - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{})); - EXPECT_CALL(local, ServerUnannounce(std::string_view{"test"}, 0)); - } { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuidLocal, "test2", "double", wpi::util::json::object(), {}}}); queue.msgs.emplace_back(net::ClientMessage{ net::ClientValueMsg{pubuidLocal, Value::MakeDouble(1.0, 10)}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } { queue.msgs.emplace_back( net::ClientMessage{net::SubscribeMsg{subuid, {"test"}, {}}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } - ::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::util::json::object()}})))) - .WillOnce(Return(0)); - EXPECT_CALL(wire, Flush()); // SendValues() - } + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; // connect client auto [name, id] = server.AddClient("test", "connInfo", false, wire, @@ -372,9 +383,35 @@ TEST_F(ServerImplTest, ClientDisconnectUnpublish) { // disconnect client server.RemoveClient(id); + + CheckServerMessageCounts(local, {.announce = 2, .unannounce = 1}); + CheckCallOrder(local.calls); + CHECK(local.announceCalls[0].name == "test2"); + CHECK(local.announceCalls[0].pubuid == std::optional{pubuidLocal}); + CHECK(local.announceCalls[1].name == "test"); + CHECK(local.announceCalls[1].pubuid == std::optional{}); + CHECK(local.unannounceCalls[0].name == "test"); + CHECK(local.unannounceCalls[0].id == 0); + CheckWireConnectionCounts(wire, {.sendPing = 1, + .ready = 1, + .writeText = 1, + .flush = 1, + .lastReceivedTime = 1}); + CHECK(wire.sendPingCalls == std::vector{100}); + CheckCallOrder(wire.calls); + CHECK(wire.writeTextCalls[0] == + EncodeText1(net::ServerMessage{net::AnnounceMsg{ + "test", 8, "double", 1, wpi::util::json::object()}})); } -TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest ZeroTimestampNegativeTime", + "[ntcore][server]") { // publish before client connect server.SetLocal(&local, &queue); constexpr int pubuid = 1; @@ -384,16 +421,7 @@ TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { defaultValue.SetTime(0); defaultValue.SetServerTime(0); Value value = Value::MakeDouble(5, -10); - { - ::testing::InSequence seq; - EXPECT_CALL( - local, - ServerAnnounce(std::string_view{"test"}, 0, std::string_view{"double"}, - wpi::util::json::object(), std::optional{pubuid})) - .WillOnce(Return(topicHandle)); - EXPECT_CALL(local, ServerSetValue(topicHandle, defaultValue)); - EXPECT_CALL(local, ServerSetValue(topicHandle, value)); - } + local.announceReturns.emplace_back(topicHandle); { queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ @@ -402,17 +430,13 @@ TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { net::ClientMessage{net::ClientValueMsg{pubuid, defaultValue}}); queue.msgs.emplace_back( net::ClientMessage{net::SubscribeMsg{subuid, {"test"}, {}}}); - EXPECT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + CHECK_FALSE(server.ProcessLocalMessages(UINT_MAX)); } // client connect; it should get already-published topic as soon as it // subscribes - ::testing::StrictMock wire; - MockSetPeriodicFunc setPeriodic; - { - ::testing::InSequence seq; - // EXPECT_CALL(wire, Flush()); // AddClient() - } + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; auto [name, id] = server.AddClient("test", "connInfo", false, wire, setPeriodic.AsStdFunction()); @@ -427,6 +451,17 @@ TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { msgs.emplace_back(net::ClientMessage{net::ClientValueMsg{pubuid2, value}}); server.ProcessIncomingBinary(id, EncodeServerBinary(msgs)); } + + CheckServerMessageCounts(local, {.announce = 1, .setValue = 2}); + CheckCallOrder(local.calls); + CHECK(local.announceCalls[0].name == "test"); + CHECK(local.announceCalls[0].pubuid == std::optional{pubuid}); + CHECK(local.setValueCalls[0].topicuid == topicHandle); + CHECK(local.setValueCalls[0].value == defaultValue); + CHECK(local.setValueCalls[1].topicuid == topicHandle); + CHECK(local.setValueCalls[1].value == value); } // When a client re-subscribes with a shorter period for a topic it already @@ -449,37 +484,29 @@ TEST_F(ServerImplTest, ZeroTimestampNegativeTime) { // DoWriteBinary called once. // With bug: SetPeriod not called → value stays in 200ms queue // (nextSendMs=200) → DoWriteBinary NOT called at t=150. -TEST_F(ServerImplTest, ResubscribeShorterPeriodUpdatesTopicOutgoing) { +TEST_CASE_METHOD(ServerImplTest, + "ServerImplTest ResubscribeShorterPeriodUpdatesTopicOutgoing", + "[ntcore][server]") { server.SetLocal(&local, &queue); constexpr int pubuid = 1; constexpr int subuid = 1; - EXPECT_CALL(local, ServerAnnounce(std::string_view{"test"}, _, _, _, _)) - .WillOnce(Return(0)); - EXPECT_CALL(local, ServerSetValue(_, _)).Times(::testing::AnyNumber()); - // Publish topic and initial value so there is a lastValue when the client // subscribes. queue.msgs.emplace_back(net::ClientMessage{net::PublishMsg{ pubuid, "test", "double", wpi::util::json::object(), {}}}); queue.msgs.emplace_back(net::ClientMessage{ net::ClientValueMsg{pubuid, Value::MakeDouble(1.0, 50)}}); - ASSERT_FALSE(server.ProcessLocalMessages(UINT_MAX)); + REQUIRE_FALSE(server.ProcessLocalMessages(UINT_MAX)); - ::testing::NiceMock wire; - EXPECT_CALL(wire, GetVersion()).WillRepeatedly(Return(0x0401)); - MockSetPeriodicFunc setPeriodic; - EXPECT_CALL(setPeriodic, Call(_)).Times(::testing::AnyNumber()); + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; int binaryCallCount = 0; - ON_CALL(wire, DoWriteBinary(_)).WillByDefault([&](std::span) { + wire.onWriteBinary = [&](std::span) { ++binaryCallCount; return 0; - }); - ON_CALL(wire, Ready()).WillByDefault(Return(true)); - ON_CALL(wire, DoWriteText(_)).WillByDefault(Return(0)); - ON_CALL(wire, Flush()).WillByDefault(Return(0)); - ON_CALL(wire, GetLastReceivedTime()).WillByDefault(Return(0)); + }; auto [name, id] = server.AddClient("test", "connInfo", false, wire, setPeriodic.AsStdFunction()); @@ -517,20 +544,35 @@ TEST_F(ServerImplTest, ResubscribeShorterPeriodUpdatesTopicOutgoing) { binaryCallCount = 0; server.SendOutgoing(id, 150); - EXPECT_EQ(binaryCallCount, 1) - << "Re-subscribing with a shorter period must update the topic's " - "outgoing queue period; the re-queued last value should be sent by " - "t=150ms (XOR bug: UpdatePeriod is skipped when added && removed are " - "both true, leaving the topic mapped to the 200ms queue)"; + UNSCOPED_INFO( + "Re-subscribing with a shorter period must update the topic's outgoing " + "queue period; the re-queued last value should be sent by t=150ms (XOR " + "bug: UpdatePeriod is skipped when added && removed are both true, " + "leaving the topic mapped to the 200ms queue)"); + CHECK(binaryCallCount == 1); + CheckWireConnectionCounts(wire, {.sendPing = 1, + .ready = 2, + .writeText = 1, + .writeBinary = 2, + .flush = 2, + .lastReceivedTime = 1}); + CheckCallOrder< + net::MockWireConnection::GetLastReceivedTimeCall, + net::MockWireConnection::SendPingCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteTextCall, + net::MockWireConnection::WriteBinaryCall, + net::MockWireConnection::FlushCall, net::MockWireConnection::ReadyCall, + net::MockWireConnection::WriteBinaryCall, + net::MockWireConnection::FlushCall>(wire.calls); } -TEST_F(ServerImplTest, InvalidPubUid) { - EXPECT_CALL(logger, Call(_, _, _, "0: pubuid out of range")); +TEST_CASE_METHOD(ServerImplTest, "ServerImplTest InvalidPubUid", + "[ntcore][server]") { server.SetLocal(&local, &queue); // connect client - ::testing::StrictMock wire; - MockSetPeriodicFunc setPeriodic; + net::MockWireConnection wire; + SetPeriodicRecorder setPeriodic; auto [name, id] = server.AddClient("test", "connInfo", false, wire, setPeriodic.AsStdFunction()); @@ -538,6 +580,8 @@ TEST_F(ServerImplTest, InvalidPubUid) { id, "[{\"method\":\"publish\",\"params\":{\"type\":\"string\",\"name\":" "\"myvalue\",\"pubuid\":2147483647,\"properties\":{}}}]"); + logger.CheckMessage(NT_LOG_WARNING, "0: pubuid out of range"); + CheckNoServerCalls(local); } } // namespace wpi::nt