mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
[ntcore] NetworkTables 4 (#3217)
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_cpp.h"
|
||||
@@ -22,20 +25,27 @@ class ConnectionListenerTest : public ::testing::Test {
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect(unsigned int port);
|
||||
void Connect(const char* address, unsigned int port3, unsigned int port4);
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void ConnectionListenerTest::Connect(unsigned int port) {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", "127.0.0.1", port);
|
||||
nt::StartClient(client_inst, "127.0.0.1", port);
|
||||
void ConnectionListenerTest::Connect(const char* address, unsigned int port3,
|
||||
unsigned int port4) {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", address, port3,
|
||||
port4);
|
||||
nt::StartClient4(client_inst);
|
||||
nt::SetServer(client_inst, address, port4);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
while ((nt::GetNetworkMode(client_inst) & NT_NET_MODE_STARTING) != 0) {
|
||||
// wait for client to report it's connected, then wait another 0.1 sec
|
||||
int count = 0;
|
||||
while (!nt::IsConnected(client_inst)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (++count > 30) {
|
||||
FAIL() << "timed out waiting for client to start";
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
@@ -49,13 +59,13 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
Connect(10000);
|
||||
Connect("127.0.0.1", 0, 10020);
|
||||
|
||||
// get the event
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
bool timed_out = false;
|
||||
auto result = nt::PollConnectionListener(poller, 0.1, &timed_out);
|
||||
EXPECT_FALSE(timed_out);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto result = nt::ReadConnectionListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
@@ -65,41 +75,61 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// get the event
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
timed_out = false;
|
||||
result = nt::PollConnectionListener(poller, 0.1, &timed_out);
|
||||
EXPECT_FALSE(timed_out);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
result = nt::ReadConnectionListenerQueue(poller);
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
}
|
||||
|
||||
TEST_F(ConnectionListenerTest, Threaded) {
|
||||
class ConnectionListenerVariantTest
|
||||
: public ConnectionListenerTest,
|
||||
public ::testing::WithParamInterface<std::pair<const char*, int>> {};
|
||||
|
||||
TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
wpi::mutex m;
|
||||
std::vector<nt::ConnectionNotification> result;
|
||||
auto handle = nt::AddConnectionListener(
|
||||
server_inst,
|
||||
[&](const nt::ConnectionNotification& event) { result.push_back(event); },
|
||||
[&](const nt::ConnectionNotification& event) {
|
||||
std::scoped_lock lock{m};
|
||||
result.push_back(event);
|
||||
},
|
||||
false);
|
||||
|
||||
// trigger a connect event
|
||||
Connect(10001);
|
||||
Connect(GetParam().first, 0, 20001 + GetParam().second);
|
||||
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
bool timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(handle, 1.0, &timed_out));
|
||||
ASSERT_FALSE(timed_out);
|
||||
|
||||
// get the event
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
result.clear();
|
||||
{
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
result.clear();
|
||||
}
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// get the event
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
{
|
||||
std::scoped_lock lock{m};
|
||||
ASSERT_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
}
|
||||
}
|
||||
|
||||
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}));
|
||||
|
||||
@@ -1,165 +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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
class EntryListenerTest : public ::testing::Test {
|
||||
public:
|
||||
EntryListenerTest()
|
||||
: server_inst(nt::CreateInstance()), client_inst(nt::CreateInstance()) {
|
||||
nt::SetNetworkIdentity(server_inst, "server");
|
||||
nt::SetNetworkIdentity(client_inst, "client");
|
||||
#if 0
|
||||
nt::AddLogger(server_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(client_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
~EntryListenerTest() override {
|
||||
nt::DestroyInstance(server_inst);
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect(unsigned int port);
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void EntryListenerTest::Connect(unsigned int port) {
|
||||
nt::StartServer(server_inst, "entrylistenertest.ini", "127.0.0.1", port);
|
||||
nt::StartClient(client_inst, "127.0.0.1", port);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
nt::AddPolledConnectionListener(poller, false);
|
||||
bool timed_out = false;
|
||||
if (nt::PollConnectionListener(poller, 1.0, &timed_out).empty()) {
|
||||
FAIL() << "client didn't connect to server";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, EntryNewLocal) {
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
nt::GetEntry(server_inst, "/foo"),
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(2.0));
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo"));
|
||||
ASSERT_EQ(events[0].name, "/foo");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, (unsigned int)(NT_NOTIFY_NEW | NT_NOTIFY_LOCAL));
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, DISABLED_EntryNewRemote) {
|
||||
Connect(10010);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
nt::GetEntry(server_inst, "/foo"),
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(2.0));
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::Flush(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo"));
|
||||
ASSERT_EQ(events[0].name, "/foo");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, NT_NOTIFY_NEW);
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, PrefixNewLocal) {
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
server_inst, "/foo",
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::SetEntryValue(nt::GetEntry(server_inst, "/baz"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo/bar"));
|
||||
ASSERT_EQ(events[0].name, "/foo/bar");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, (unsigned int)(NT_NOTIFY_NEW | NT_NOTIFY_LOCAL));
|
||||
}
|
||||
|
||||
TEST_F(EntryListenerTest, DISABLED_PrefixNewRemote) {
|
||||
Connect(10011);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
std::vector<nt::EntryNotification> events;
|
||||
auto handle = nt::AddEntryListener(
|
||||
server_inst, "/foo",
|
||||
[&](const nt::EntryNotification& event) { events.push_back(event); },
|
||||
NT_NOTIFY_NEW);
|
||||
|
||||
// Trigger an event
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/foo/bar"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::SetEntryValue(nt::GetEntry(client_inst, "/baz"),
|
||||
nt::Value::MakeDouble(1.0));
|
||||
nt::Flush(client_inst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
ASSERT_TRUE(nt::WaitForEntryListenerQueue(server_inst, 1.0));
|
||||
|
||||
// Check the event
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].entry, nt::GetEntry(server_inst, "/foo/bar"));
|
||||
ASSERT_EQ(events[0].name, "/foo/bar");
|
||||
ASSERT_THAT(events[0].value, nt::ValueEq(nt::Value::MakeDouble(1.0)));
|
||||
ASSERT_EQ(events[0].flags, NT_NOTIFY_NEW);
|
||||
}
|
||||
@@ -1,312 +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 <wpi/Logger.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "EntryNotifier.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class EntryNotifierTest : public ::testing::Test {
|
||||
public:
|
||||
EntryNotifierTest() : notifier(1, logger) { notifier.Start(); }
|
||||
|
||||
void GenerateNotifications();
|
||||
|
||||
protected:
|
||||
wpi::Logger logger;
|
||||
EntryNotifier notifier;
|
||||
};
|
||||
|
||||
void EntryNotifierTest::GenerateNotifications() {
|
||||
// All flags combos that can be generated by Storage
|
||||
static const unsigned int flags[] = {
|
||||
// "normal" notifications
|
||||
NT_NOTIFY_NEW, NT_NOTIFY_DELETE, NT_NOTIFY_UPDATE, NT_NOTIFY_FLAGS,
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS,
|
||||
// immediate notifications are always "new"
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW,
|
||||
// local notifications can be of any flag combo
|
||||
NT_NOTIFY_LOCAL | NT_NOTIFY_NEW, NT_NOTIFY_LOCAL | NT_NOTIFY_DELETE,
|
||||
NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE, NT_NOTIFY_LOCAL | NT_NOTIFY_FLAGS,
|
||||
NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS};
|
||||
// Generate across keys
|
||||
static const char* keys[] = {"/foo/bar", "/baz", "/boo"};
|
||||
|
||||
auto val = Value::MakeDouble(1);
|
||||
|
||||
// Provide unique key indexes for each key
|
||||
unsigned int keyindex = 5;
|
||||
for (auto key : keys) {
|
||||
for (auto flag : flags) {
|
||||
notifier.NotifyEntry(keyindex, key, val, flag);
|
||||
}
|
||||
++keyindex;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollEntryMultiple) {
|
||||
auto poller1 = notifier.CreatePoller();
|
||||
auto poller2 = notifier.CreatePoller();
|
||||
auto poller3 = notifier.CreatePoller();
|
||||
auto h1 = notifier.AddPolled(poller1, 6, NT_NOTIFY_NEW);
|
||||
auto h2 = notifier.AddPolled(poller2, 6, NT_NOTIFY_NEW);
|
||||
auto h3 = notifier.AddPolled(poller3, 6, NT_NOTIFY_UPDATE);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results1 = notifier.Poll(poller1, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto results2 = notifier.Poll(poller2, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto results3 = notifier.Poll(poller3, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
|
||||
ASSERT_EQ(results1.size(), 2u);
|
||||
for (const auto& result : results1) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h1);
|
||||
}
|
||||
|
||||
ASSERT_EQ(results2.size(), 2u);
|
||||
for (const auto& result : results2) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h2);
|
||||
}
|
||||
|
||||
ASSERT_EQ(results3.size(), 2u);
|
||||
for (const auto& result : results3) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollEntryBasic) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
auto g1 = notifier.AddPolled(poller, 6, NT_NOTIFY_NEW);
|
||||
auto g2 = notifier.AddPolled(poller, 6, NT_NOTIFY_DELETE);
|
||||
auto g3 = notifier.AddPolled(poller, 6, NT_NOTIFY_UPDATE);
|
||||
auto g4 = notifier.AddPolled(poller, 6, NT_NOTIFY_FLAGS);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
|
||||
int g1count = 0;
|
||||
int g2count = 0;
|
||||
int g3count = 0;
|
||||
int g4count = 0;
|
||||
for (const auto& result : results) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(result.name, "/baz");
|
||||
EXPECT_THAT(result.value, ValueEq(Value::MakeDouble(1)));
|
||||
EXPECT_EQ(Handle{result.entry}.GetType(), Handle::kEntry);
|
||||
EXPECT_EQ(Handle{result.entry}.GetInst(), 1);
|
||||
EXPECT_EQ(Handle{result.entry}.GetIndex(), 6);
|
||||
EXPECT_EQ(Handle{result.listener}.GetType(), Handle::kEntryListener);
|
||||
EXPECT_EQ(Handle{result.listener}.GetInst(), 1);
|
||||
if (Handle{result.listener}.GetIndex() == static_cast<int>(g1)) {
|
||||
++g1count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_NEW) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g2)) {
|
||||
++g2count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_DELETE) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g3)) {
|
||||
++g3count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_UPDATE) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g4)) {
|
||||
++g4count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_FLAGS) != 0);
|
||||
} else {
|
||||
ADD_FAILURE() << "unknown listener index";
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(g1count, 2);
|
||||
EXPECT_EQ(g2count, 1); // NT_NOTIFY_DELETE
|
||||
EXPECT_EQ(g3count, 2);
|
||||
EXPECT_EQ(g4count, 2);
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollEntryImmediate) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
notifier.AddPolled(poller, 6, NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE);
|
||||
notifier.AddPolled(poller, 6, NT_NOTIFY_NEW);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
SCOPED_TRACE(::testing::PrintToString(results));
|
||||
ASSERT_EQ(results.size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollEntryLocal) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
notifier.AddPolled(poller, 6, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
notifier.AddPolled(poller, 6, NT_NOTIFY_NEW);
|
||||
|
||||
ASSERT_TRUE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
SCOPED_TRACE(::testing::PrintToString(results));
|
||||
ASSERT_EQ(results.size(), 6u);
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollPrefixMultiple) {
|
||||
auto poller1 = notifier.CreatePoller();
|
||||
auto poller2 = notifier.CreatePoller();
|
||||
auto poller3 = notifier.CreatePoller();
|
||||
auto h1 = notifier.AddPolled(poller1, "/foo", NT_NOTIFY_NEW);
|
||||
auto h2 = notifier.AddPolled(poller2, "/foo", NT_NOTIFY_NEW);
|
||||
auto h3 = notifier.AddPolled(poller3, "/foo", NT_NOTIFY_UPDATE);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results1 = notifier.Poll(poller1, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto results2 = notifier.Poll(poller2, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
auto results3 = notifier.Poll(poller3, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
|
||||
ASSERT_EQ(results1.size(), 2u);
|
||||
for (const auto& result : results1) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h1);
|
||||
}
|
||||
|
||||
ASSERT_EQ(results2.size(), 2u);
|
||||
for (const auto& result : results2) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h2);
|
||||
}
|
||||
|
||||
ASSERT_EQ(results3.size(), 2u);
|
||||
for (const auto& result : results3) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollPrefixBasic) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
auto g1 = notifier.AddPolled(poller, "/foo", NT_NOTIFY_NEW);
|
||||
auto g2 = notifier.AddPolled(poller, "/foo", NT_NOTIFY_DELETE);
|
||||
auto g3 = notifier.AddPolled(poller, "/foo", NT_NOTIFY_UPDATE);
|
||||
auto g4 = notifier.AddPolled(poller, "/foo", NT_NOTIFY_FLAGS);
|
||||
notifier.AddPolled(poller, "/bar", NT_NOTIFY_NEW);
|
||||
notifier.AddPolled(poller, "/bar", NT_NOTIFY_DELETE);
|
||||
notifier.AddPolled(poller, "/bar", NT_NOTIFY_UPDATE);
|
||||
notifier.AddPolled(poller, "/bar", NT_NOTIFY_FLAGS);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
|
||||
int g1count = 0;
|
||||
int g2count = 0;
|
||||
int g3count = 0;
|
||||
int g4count = 0;
|
||||
for (const auto& result : results) {
|
||||
SCOPED_TRACE(::testing::PrintToString(result));
|
||||
EXPECT_TRUE(wpi::starts_with(result.name, "/foo"));
|
||||
EXPECT_THAT(result.value, ValueEq(Value::MakeDouble(1)));
|
||||
EXPECT_EQ(Handle{result.entry}.GetType(), Handle::kEntry);
|
||||
EXPECT_EQ(Handle{result.entry}.GetInst(), 1);
|
||||
EXPECT_EQ(Handle{result.entry}.GetIndex(), 5);
|
||||
EXPECT_EQ(Handle{result.listener}.GetType(), Handle::kEntryListener);
|
||||
EXPECT_EQ(Handle{result.listener}.GetInst(), 1);
|
||||
if (Handle{result.listener}.GetIndex() == static_cast<int>(g1)) {
|
||||
++g1count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_NEW) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g2)) {
|
||||
++g2count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_DELETE) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g3)) {
|
||||
++g3count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_UPDATE) != 0);
|
||||
} else if (Handle{result.listener}.GetIndex() == static_cast<int>(g4)) {
|
||||
++g4count;
|
||||
EXPECT_TRUE((result.flags & NT_NOTIFY_FLAGS) != 0);
|
||||
} else {
|
||||
ADD_FAILURE() << "unknown listener index";
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(g1count, 2);
|
||||
EXPECT_EQ(g2count, 1); // NT_NOTIFY_DELETE
|
||||
EXPECT_EQ(g3count, 2);
|
||||
EXPECT_EQ(g4count, 2);
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollPrefixImmediate) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
notifier.AddPolled(poller, "/foo", NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE);
|
||||
notifier.AddPolled(poller, "/foo", NT_NOTIFY_NEW);
|
||||
|
||||
ASSERT_FALSE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
SCOPED_TRACE(::testing::PrintToString(results));
|
||||
ASSERT_EQ(results.size(), 4u);
|
||||
}
|
||||
|
||||
TEST_F(EntryNotifierTest, PollPrefixLocal) {
|
||||
auto poller = notifier.CreatePoller();
|
||||
notifier.AddPolled(poller, "/foo", NT_NOTIFY_NEW | NT_NOTIFY_LOCAL);
|
||||
notifier.AddPolled(poller, "/foo", NT_NOTIFY_NEW);
|
||||
|
||||
ASSERT_TRUE(notifier.local_notifiers());
|
||||
|
||||
GenerateNotifications();
|
||||
|
||||
ASSERT_TRUE(notifier.WaitForQueue(1.0));
|
||||
bool timed_out = false;
|
||||
auto results = notifier.Poll(poller, 0, &timed_out);
|
||||
ASSERT_FALSE(timed_out);
|
||||
SCOPED_TRACE(::testing::PrintToString(results));
|
||||
ASSERT_EQ(results.size(), 6u);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
469
ntcore/src/test/native/cpp/LocalStorageTest.cpp
Normal file
469
ntcore/src/test/native/cpp/LocalStorageTest.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
// 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 "LocalStorage.h"
|
||||
#include "MockLogger.h"
|
||||
#include "PubSubOptionsMatcher.h"
|
||||
#include "SpanMatcher.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net/MockNetworkInterface.h"
|
||||
#include "ntcore_c.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace nt {
|
||||
|
||||
::testing::Matcher<const PubSubOptions&> IsPubSubOptions(
|
||||
const PubSubOptions& good) {
|
||||
return ::testing::AllOf(
|
||||
::testing::Field("periodic", &PubSubOptions::periodic, good.periodic),
|
||||
::testing::Field("pollStorageSize", &PubSubOptions::pollStorageSize,
|
||||
good.pollStorageSize),
|
||||
::testing::Field("logging", &PubSubOptions::sendAll, good.sendAll),
|
||||
::testing::Field("keepDuplicates", &PubSubOptions::keepDuplicates,
|
||||
good.keepDuplicates));
|
||||
}
|
||||
|
||||
class LocalStorageTest : public ::testing::Test {
|
||||
public:
|
||||
LocalStorageTest() {
|
||||
storage.StartNetwork(startup);
|
||||
storage.SetNetwork(&network);
|
||||
}
|
||||
|
||||
::testing::StrictMock<net::MockNetworkStartupInterface> startup;
|
||||
::testing::StrictMock<net::MockNetworkInterface> network;
|
||||
wpi::MockLogger logger;
|
||||
LocalStorage storage{0, logger};
|
||||
NT_Topic fooTopic{storage.GetTopic("foo")};
|
||||
NT_Topic barTopic{storage.GetTopic("bar")};
|
||||
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_F(LocalStorageTest, GetTopic2) {
|
||||
auto foo2 = storage.GetTopic("foo");
|
||||
EXPECT_EQ(fooTopic, foo2);
|
||||
EXPECT_NE(fooTopic, barTopic);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetTopicEmptyName) {
|
||||
EXPECT_EQ(storage.GetTopic(""), 0u);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetEntryEmptyName) {
|
||||
EXPECT_EQ(storage.GetEntry(""), 0u);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetTopicName) {
|
||||
EXPECT_EQ(storage.GetTopicName(fooTopic), "foo");
|
||||
EXPECT_EQ(storage.GetTopicName(barTopic), "bar");
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, GetTopicInfoUnpublished) {
|
||||
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, "{}");
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_UNASSIGNED);
|
||||
EXPECT_TRUE(storage.GetTopicTypeString(fooTopic).empty());
|
||||
EXPECT_FALSE(storage.GetTopicExists(fooTopic));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, PublishNewNoProps) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", wpi::json::object(), {});
|
||||
|
||||
auto info = storage.GetTopicInfo(fooTopic);
|
||||
EXPECT_EQ(info.properties, "{}");
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, PublishNewNoPropsNull) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto info = storage.GetTopicInfo(fooTopic);
|
||||
EXPECT_EQ(info.properties, "{}");
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, PublishNew) {
|
||||
wpi::json properties = {{"persistent", true}};
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, properties,
|
||||
IsPubSubOptions({})));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {{"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}");
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPost) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
EXPECT_CALL(network, SetValue(pub, val));
|
||||
storage.SetEntryValue(pub, val);
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
auto value = storage.GetEntryValue(sub);
|
||||
ASSERT_TRUE(value.IsBoolean());
|
||||
EXPECT_EQ(value.GetBoolean(), true);
|
||||
EXPECT_EQ(value.time(), 5);
|
||||
|
||||
auto vals = storage.ReadQueueBoolean(sub);
|
||||
ASSERT_EQ(vals.size(), 1u);
|
||||
EXPECT_EQ(vals[0].value, true);
|
||||
EXPECT_EQ(vals[0].time, 5);
|
||||
|
||||
val = Value::MakeBoolean(true, 6);
|
||||
EXPECT_CALL(network, SetValue(pub, val));
|
||||
storage.SetEntryValue(pub, val);
|
||||
|
||||
auto vals2 = storage.ReadQueueInteger(sub); // mismatched type
|
||||
ASSERT_TRUE(vals2.empty());
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SubscribeNoTypeLocalPubPre) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
EXPECT_CALL(network, SetValue(pub, val));
|
||||
storage.SetEntryValue(pub, val);
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
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));
|
||||
|
||||
auto value = storage.GetEntryValue(sub);
|
||||
ASSERT_TRUE(value.IsBoolean());
|
||||
EXPECT_EQ(value.GetBoolean(), true);
|
||||
EXPECT_EQ(value.time(), 5);
|
||||
|
||||
auto vals = storage.ReadQueueValue(sub); // read queue won't get anything
|
||||
ASSERT_TRUE(vals.empty());
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, EntryNoTypeLocalSet) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
// results in a publish and value set
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
EXPECT_CALL(network, SetValue(_, val));
|
||||
EXPECT_TRUE(storage.SetEntryValue(entry, val));
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
auto value = storage.GetEntryValue(entry);
|
||||
ASSERT_TRUE(value.IsBoolean());
|
||||
EXPECT_EQ(value.GetBoolean(), true);
|
||||
EXPECT_EQ(value.time(), 5);
|
||||
|
||||
auto vals = storage.ReadQueueBoolean(entry);
|
||||
ASSERT_EQ(vals.size(), 1u);
|
||||
EXPECT_EQ(vals[0].value, true);
|
||||
EXPECT_EQ(vals[0].time, 5);
|
||||
|
||||
// normal set with same type
|
||||
val = Value::MakeBoolean(true, 6);
|
||||
EXPECT_CALL(network, SetValue(_, val));
|
||||
EXPECT_TRUE(storage.SetEntryValue(entry, val));
|
||||
|
||||
auto vals2 = storage.ReadQueueInteger(entry); // mismatched type
|
||||
ASSERT_TRUE(vals2.empty());
|
||||
|
||||
// cannot change type; won't generate network message
|
||||
EXPECT_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");
|
||||
|
||||
auto vals3 = storage.ReadQueueInteger(entry); // mismatched type
|
||||
ASSERT_TRUE(vals3.empty());
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, PubUnpubPub) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_INTEGER, "int", {});
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
auto val = Value::MakeBoolean(true, 5);
|
||||
EXPECT_CALL(network, SetValue(pub, val));
|
||||
EXPECT_TRUE(storage.SetEntryValue(pub, val));
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
EXPECT_TRUE(storage.ReadQueueInteger(sub).empty());
|
||||
|
||||
EXPECT_CALL(network, Unpublish(pub, fooTopic));
|
||||
storage.Unpublish(pub);
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_UNASSIGNED);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "");
|
||||
EXPECT_FALSE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"int"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
pub = storage.Publish(fooTopic, NT_INTEGER, "int", {}, {});
|
||||
|
||||
val = Value::MakeInteger(3, 5);
|
||||
EXPECT_CALL(network, SetValue(pub, val));
|
||||
EXPECT_TRUE(storage.SetEntryValue(pub, val));
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
EXPECT_EQ(storage.ReadQueueInteger(sub).size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, LocalPubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub1 = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
"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));
|
||||
|
||||
EXPECT_CALL(network, SetValue(pub1, _));
|
||||
|
||||
EXPECT_TRUE(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5)));
|
||||
EXPECT_FALSE(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5)));
|
||||
|
||||
// unpublishing pub1 will publish pub2 to the network
|
||||
EXPECT_CALL(network, Unpublish(pub1, fooTopic));
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"int"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
storage.Unpublish(pub1);
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
EXPECT_CALL(network, SetValue(pub2, _));
|
||||
|
||||
EXPECT_FALSE(storage.SetEntryValue(pub1, Value::MakeBoolean(true, 5)));
|
||||
EXPECT_TRUE(storage.SetEntryValue(pub2, Value::MakeInteger(3, 5)));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, LocalSubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
"local subscribe to 'foo' disabled due to type "
|
||||
"mismatch (wanted 'int', currently 'boolean')"));
|
||||
storage.Subscribe(fooTopic, NT_INTEGER, "int", {});
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, RemotePubConflict) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
|
||||
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
EXPECT_CALL(logger, Call(NT_LOG_INFO, _, _,
|
||||
"network announce of 'foo' overriding local publish "
|
||||
"(was 'boolean', now 'int')"));
|
||||
|
||||
storage.NetworkAnnounce("foo", "int", wpi::json::object(), {});
|
||||
|
||||
// network overrides local
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_INTEGER);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "int");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
|
||||
storage.NetworkUnannounce("foo");
|
||||
|
||||
EXPECT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
EXPECT_EQ(storage.GetTopicTypeString(fooTopic), "boolean");
|
||||
EXPECT_TRUE(storage.GetTopicExists(fooTopic));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SubNonExist) {
|
||||
// makes sure no warning is emitted
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultSubscribe) {
|
||||
// no publish, no value on wire, this is just handled locally
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto sub = storage.Subscribe(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(sub, Value::MakeBoolean(true)));
|
||||
auto val = storage.GetEntryValue(sub);
|
||||
ASSERT_TRUE(val.IsBoolean());
|
||||
ASSERT_TRUE(val.GetBoolean());
|
||||
ASSERT_EQ(val.time(), 0);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultPublish) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {}, {});
|
||||
|
||||
// expect a value across the wire
|
||||
auto expectVal = Value::MakeBoolean(true, 0);
|
||||
EXPECT_CALL(network, SetValue(pub, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true)));
|
||||
|
||||
EXPECT_CALL(network, Subscribe(_, _, IsPubSubOptions({})));
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultEntry) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_BOOLEAN, "boolean", {});
|
||||
|
||||
// expect a publish and value
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto expectVal = Value::MakeBoolean(true, 0);
|
||||
EXPECT_CALL(network, SetValue(_, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true)));
|
||||
|
||||
auto val = storage.GetEntryValue(entry);
|
||||
ASSERT_TRUE(val.IsBoolean());
|
||||
ASSERT_TRUE(val.GetBoolean());
|
||||
ASSERT_EQ(val.time(), 0);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultEntryUnassigned) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
|
||||
// expect a publish and value
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"boolean"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto expectVal = Value::MakeBoolean(true, 0);
|
||||
EXPECT_CALL(network, SetValue(_, expectVal));
|
||||
EXPECT_TRUE(storage.SetDefaultEntryValue(entry, Value::MakeBoolean(true)));
|
||||
|
||||
ASSERT_EQ(storage.GetTopicType(fooTopic), NT_BOOLEAN);
|
||||
auto val = storage.GetEntryValue(entry);
|
||||
ASSERT_TRUE(val.IsBoolean());
|
||||
ASSERT_TRUE(val.GetBoolean());
|
||||
ASSERT_EQ(val.time(), 0);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetDefaultEntryDiffType) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"string"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {});
|
||||
|
||||
EXPECT_FALSE(storage.SetDefaultEntryValue(pub, Value::MakeBoolean(true)));
|
||||
ASSERT_EQ(storage.GetTopicType(fooTopic), NT_STRING);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetValueEmptyValue) {
|
||||
EXPECT_CALL(network, Publish(_, fooTopic, std::string_view{"foo"},
|
||||
std::string_view{"string"}, wpi::json::object(),
|
||||
IsPubSubOptions({})));
|
||||
auto pub = storage.Publish(fooTopic, NT_STRING, "string", {}, {});
|
||||
|
||||
EXPECT_FALSE(storage.SetEntryValue(pub, {}));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetValueEmptyUntypedEntry) {
|
||||
EXPECT_CALL(network, Subscribe(_, wpi::SpanEq({std::string{"foo"}}),
|
||||
IsPubSubOptions({})));
|
||||
auto entry = storage.GetEntry(fooTopic, NT_UNASSIGNED, "", {});
|
||||
EXPECT_FALSE(storage.SetEntryValue(entry, {}));
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, PublishUntyped) {
|
||||
EXPECT_EQ(storage.Publish(fooTopic, NT_UNASSIGNED, "", {}, {}), 0u);
|
||||
}
|
||||
|
||||
TEST_F(LocalStorageTest, SetValueInvalidHandle) {
|
||||
EXPECT_FALSE(storage.SetEntryValue(0u, {}));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,40 +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.
|
||||
|
||||
#ifndef NTCORE_MESSAGEMATCHER_H_
|
||||
#define NTCORE_MESSAGEMATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
#include "Message.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MessageMatcher
|
||||
: public ::testing::MatcherInterface<std::shared_ptr<Message>> {
|
||||
public:
|
||||
explicit MessageMatcher(std::shared_ptr<Message> goodmsg_)
|
||||
: goodmsg(std::move(goodmsg_)) {}
|
||||
|
||||
bool MatchAndExplain(std::shared_ptr<Message> msg,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Message> goodmsg;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<std::shared_ptr<Message>> MessageEq(
|
||||
std::shared_ptr<Message> goodmsg) {
|
||||
return ::testing::MakeMatcher(new MessageMatcher(goodmsg));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MESSAGEMATCHER_H_
|
||||
24
ntcore/src/test/native/cpp/MockConnectionList.h
Normal file
24
ntcore/src/test/native/cpp/MockConnectionList.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include "IConnectionList.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace 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<ConnectionInfo>, GetConnections, (),
|
||||
(const, override));
|
||||
MOCK_METHOD(bool, IsConnected, (), (const, override));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,27 +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.
|
||||
|
||||
#ifndef NTCORE_MOCKCONNECTIONNOTIFIER_H_
|
||||
#define NTCORE_MOCKCONNECTIONNOTIFIER_H_
|
||||
|
||||
#include "IConnectionNotifier.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockConnectionNotifier : public IConnectionNotifier {
|
||||
public:
|
||||
MOCK_METHOD1(
|
||||
Add,
|
||||
unsigned int(
|
||||
std::function<void(const ConnectionNotification& event)> callback));
|
||||
MOCK_METHOD1(AddPolled, unsigned int(unsigned int poller_uid));
|
||||
MOCK_METHOD3(NotifyConnection,
|
||||
void(bool connected, const ConnectionInfo& conn_info,
|
||||
unsigned int only_listener));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKCONNECTIONNOTIFIER_H_
|
||||
@@ -1,24 +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.
|
||||
|
||||
#ifndef NTCORE_MOCKDISPATCHER_H_
|
||||
#define NTCORE_MOCKDISPATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "IDispatcher.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockDispatcher : public IDispatcher {
|
||||
public:
|
||||
MOCK_METHOD3(QueueOutgoing,
|
||||
void(std::shared_ptr<Message> msg, INetworkConnection* only,
|
||||
INetworkConnection* except));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKDISPATCHER_H_
|
||||
@@ -1,40 +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.
|
||||
|
||||
#ifndef NTCORE_MOCKENTRYNOTIFIER_H_
|
||||
#define NTCORE_MOCKENTRYNOTIFIER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "IEntryNotifier.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockEntryNotifier : public IEntryNotifier {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(local_notifiers, bool());
|
||||
MOCK_METHOD3(
|
||||
Add,
|
||||
unsigned int(std::function<void(const EntryNotification& event)> callback,
|
||||
std::string_view prefix, unsigned int flags));
|
||||
MOCK_METHOD3(
|
||||
Add,
|
||||
unsigned int(std::function<void(const EntryNotification& event)> callback,
|
||||
unsigned int local_id, unsigned int flags));
|
||||
MOCK_METHOD3(AddPolled,
|
||||
unsigned int(unsigned int poller_uid, std::string_view prefix,
|
||||
unsigned int flags));
|
||||
MOCK_METHOD3(AddPolled,
|
||||
unsigned int(unsigned int poller_uid, unsigned int local_id,
|
||||
unsigned int flags));
|
||||
MOCK_METHOD5(NotifyEntry,
|
||||
void(unsigned int local_id, std::string_view name,
|
||||
std::shared_ptr<Value> value, unsigned int flags,
|
||||
unsigned int only_listener));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKENTRYNOTIFIER_H_
|
||||
24
ntcore/src/test/native/cpp/MockLogger.h
Normal file
24
ntcore/src/test/native/cpp/MockLogger.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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 <wpi/Logger.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
class MockLogger : public Logger,
|
||||
public ::testing::MockFunction<void(
|
||||
unsigned int level, std::string_view file,
|
||||
unsigned int line, std::string_view msg)> {
|
||||
public:
|
||||
MockLogger() {
|
||||
SetLogger([this](unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) { Call(level, file, line, msg); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace wpi
|
||||
@@ -1,31 +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.
|
||||
|
||||
#ifndef NTCORE_MOCKNETWORKCONNECTION_H_
|
||||
#define NTCORE_MOCKNETWORKCONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "INetworkConnection.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockNetworkConnection : public INetworkConnection {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(info, ConnectionInfo());
|
||||
|
||||
MOCK_METHOD1(QueueOutgoing, void(std::shared_ptr<Message> msg));
|
||||
MOCK_METHOD1(PostOutgoing, void(bool keep_alive));
|
||||
|
||||
MOCK_CONST_METHOD0(proto_rev, unsigned int());
|
||||
MOCK_METHOD1(set_proto_rev, void(unsigned int proto_rev));
|
||||
|
||||
MOCK_CONST_METHOD0(state, State());
|
||||
MOCK_METHOD1(set_state, void(State state));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKNETWORKCONNECTION_H_
|
||||
@@ -1,26 +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.
|
||||
|
||||
#ifndef NTCORE_MOCKRPCSERVER_H_
|
||||
#define NTCORE_MOCKRPCSERVER_H_
|
||||
|
||||
#include "IRpcServer.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockRpcServer : public IRpcServer {
|
||||
public:
|
||||
MOCK_METHOD0(Start, void());
|
||||
MOCK_METHOD1(RemoveRpc, void(unsigned int rpc_uid));
|
||||
MOCK_METHOD7(ProcessRpc,
|
||||
void(unsigned int local_id, unsigned int call_uid,
|
||||
std::string_view name, std::string_view params,
|
||||
const ConnectionInfo& conn, SendResponseFunc send_response,
|
||||
unsigned int rpc_uid));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKRPCSERVER_H_
|
||||
42
ntcore/src/test/native/cpp/PubSubOptionsMatcher.cpp
Normal file
42
ntcore/src/test/native/cpp/PubSubOptionsMatcher.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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.h"
|
||||
|
||||
#include "TestPrinters.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
bool PubSubOptionsMatcher::MatchAndExplain(
|
||||
const PubSubOptions& val, ::testing::MatchResultListener* listener) const {
|
||||
bool match = true;
|
||||
if (val.periodic != good.periodic) {
|
||||
*listener << "periodic mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.pollStorageSize != good.pollStorageSize) {
|
||||
*listener << "pollStorageSize mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.sendAll != good.sendAll) {
|
||||
*listener << "logging mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (val.keepDuplicates != good.keepDuplicates) {
|
||||
*listener << "keepDuplicates 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 nt
|
||||
34
ntcore/src/test/native/cpp/PubSubOptionsMatcher.h
Normal file
34
ntcore/src/test/native/cpp/PubSubOptionsMatcher.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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 <ostream>
|
||||
#include <utility>
|
||||
|
||||
#include "PubSubOptions.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class PubSubOptionsMatcher
|
||||
: public ::testing::MatcherInterface<const PubSubOptions&> {
|
||||
public:
|
||||
explicit PubSubOptionsMatcher(PubSubOptions good) : good{std::move(good)} {}
|
||||
|
||||
bool MatchAndExplain(const PubSubOptions& val,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
PubSubOptions good;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<const PubSubOptions&> PubSubOptionsEq(
|
||||
PubSubOptions good) {
|
||||
return ::testing::MakeMatcher(new PubSubOptionsMatcher(std::move(good)));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
73
ntcore/src/test/native/cpp/SpanMatcher.h
Normal file
73
ntcore/src/test/native/cpp/SpanMatcher.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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 <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/span.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
template <typename T>
|
||||
class SpanMatcher : public ::testing::MatcherInterface<span<T>> {
|
||||
public:
|
||||
explicit SpanMatcher(span<T> good_) : good{good_.begin(), good_.end()} {}
|
||||
|
||||
bool MatchAndExplain(span<T> val,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::remove_cv_t<T>> good;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline ::testing::Matcher<span<const T>> SpanEq(span<const T> good) {
|
||||
return ::testing::MakeMatcher(new SpanMatcher(good));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ::testing::Matcher<span<const T>> SpanEq(
|
||||
std::initializer_list<const T> good) {
|
||||
return ::testing::MakeMatcher(
|
||||
new SpanMatcher<const T>({good.begin(), good.end()}));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SpanMatcher<T>::MatchAndExplain(
|
||||
span<T> val, ::testing::MatchResultListener* listener) const {
|
||||
if (val.size() != good.size() ||
|
||||
!std::equal(val.begin(), val.end(), good.begin())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SpanMatcher<T>::DescribeTo(::std::ostream* os) const {
|
||||
PrintTo(span<T>{good}, os);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SpanMatcher<T>::DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "is not equal to ";
|
||||
PrintTo(span<T>{good}, os);
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
inline wpi::span<const uint8_t> operator"" _us(const char* str, size_t len) {
|
||||
return {reinterpret_cast<const uint8_t*>(str), len};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,7 @@
|
||||
// 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.
|
||||
|
||||
#ifndef NTCORE_STORAGETEST_H_
|
||||
#define NTCORE_STORAGETEST_H_
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -11,15 +10,13 @@
|
||||
|
||||
#include "Log.h"
|
||||
#include "MockDispatcher.h"
|
||||
#include "MockEntryNotifier.h"
|
||||
#include "MockRpcServer.h"
|
||||
#include "Storage.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class StorageTest {
|
||||
public:
|
||||
StorageTest() : storage(notifier, rpc_server, logger), tmp_entry("foobar") {}
|
||||
StorageTest() : storage(logger), tmp_entry("foobar") {}
|
||||
|
||||
Storage::EntriesMap& entries() { return storage.m_entries; }
|
||||
Storage::IdMap& idmap() { return storage.m_idmap; }
|
||||
@@ -32,13 +29,9 @@ class StorageTest {
|
||||
void HookOutgoing(bool server) { storage.SetDispatcher(&dispatcher, server); }
|
||||
|
||||
wpi::Logger logger;
|
||||
::testing::StrictMock<MockEntryNotifier> notifier;
|
||||
::testing::StrictMock<MockRpcServer> rpc_server;
|
||||
::testing::StrictMock<MockDispatcher> dispatcher;
|
||||
Storage storage;
|
||||
Storage::Entry tmp_entry;
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_STORAGETEST_H_
|
||||
|
||||
@@ -4,13 +4,23 @@
|
||||
|
||||
#include "TestPrinters.h"
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "Message.h"
|
||||
#include "PubSubOptions.h"
|
||||
#include "net/Message.h"
|
||||
#include "net3/Message3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
namespace wpi {
|
||||
void PrintTo(const json& val, ::std::ostream* os) {
|
||||
*os << val.dump();
|
||||
}
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
#if 0
|
||||
void PrintTo(const EntryNotification& event, std::ostream* os) {
|
||||
*os << "EntryNotification{listener=";
|
||||
PrintTo(Handle{event.listener}, os);
|
||||
@@ -20,7 +30,7 @@ void PrintTo(const EntryNotification& event, std::ostream* os) {
|
||||
PrintTo(event.value, os);
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
#endif
|
||||
void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << "Handle{";
|
||||
switch (handle.GetType()) {
|
||||
@@ -48,11 +58,14 @@ void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
case Handle::kLoggerPoller:
|
||||
*os << "kLoggerPoller";
|
||||
break;
|
||||
case Handle::kRpcCall:
|
||||
*os << "kRpcCall";
|
||||
case Handle::kTopic:
|
||||
*os << "kTopic";
|
||||
break;
|
||||
case Handle::kRpcCallPoller:
|
||||
*os << "kRpcCallPoller";
|
||||
case Handle::kSubscriber:
|
||||
*os << "kSubscriber";
|
||||
break;
|
||||
case Handle::kPublisher:
|
||||
*os << "kPublisher";
|
||||
break;
|
||||
default:
|
||||
*os << "UNKNOWN";
|
||||
@@ -61,46 +74,46 @@ void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << ", " << handle.GetInst() << ", " << handle.GetIndex() << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const Message& msg, std::ostream* os) {
|
||||
void PrintTo(const net3::Message3& msg, std::ostream* os) {
|
||||
*os << "Message{";
|
||||
switch (msg.type()) {
|
||||
case Message::kKeepAlive:
|
||||
case net3::Message3::kKeepAlive:
|
||||
*os << "kKeepAlive";
|
||||
break;
|
||||
case Message::kClientHello:
|
||||
case net3::Message3::kClientHello:
|
||||
*os << "kClientHello";
|
||||
break;
|
||||
case Message::kProtoUnsup:
|
||||
case net3::Message3::kProtoUnsup:
|
||||
*os << "kProtoUnsup";
|
||||
break;
|
||||
case Message::kServerHelloDone:
|
||||
case net3::Message3::kServerHelloDone:
|
||||
*os << "kServerHelloDone";
|
||||
break;
|
||||
case Message::kServerHello:
|
||||
case net3::Message3::kServerHello:
|
||||
*os << "kServerHello";
|
||||
break;
|
||||
case Message::kClientHelloDone:
|
||||
case net3::Message3::kClientHelloDone:
|
||||
*os << "kClientHelloDone";
|
||||
break;
|
||||
case Message::kEntryAssign:
|
||||
case net3::Message3::kEntryAssign:
|
||||
*os << "kEntryAssign";
|
||||
break;
|
||||
case Message::kEntryUpdate:
|
||||
case net3::Message3::kEntryUpdate:
|
||||
*os << "kEntryUpdate";
|
||||
break;
|
||||
case Message::kFlagsUpdate:
|
||||
case net3::Message3::kFlagsUpdate:
|
||||
*os << "kFlagsUpdate";
|
||||
break;
|
||||
case Message::kEntryDelete:
|
||||
case net3::Message3::kEntryDelete:
|
||||
*os << "kEntryDelete";
|
||||
break;
|
||||
case Message::kClearEntries:
|
||||
case net3::Message3::kClearEntries:
|
||||
*os << "kClearEntries";
|
||||
break;
|
||||
case Message::kExecuteRpc:
|
||||
case net3::Message3::kExecuteRpc:
|
||||
*os << "kExecuteRpc";
|
||||
break;
|
||||
case Message::kRpcResponse:
|
||||
case net3::Message3::kRpcResponse:
|
||||
*os << "kRpcResponse";
|
||||
break;
|
||||
default:
|
||||
@@ -125,6 +138,12 @@ void PrintTo(const Value& value, std::ostream* os) {
|
||||
case NT_DOUBLE:
|
||||
*os << value.GetDouble();
|
||||
break;
|
||||
case NT_FLOAT:
|
||||
*os << value.GetFloat();
|
||||
break;
|
||||
case NT_INTEGER:
|
||||
*os << value.GetInteger();
|
||||
break;
|
||||
case NT_STRING:
|
||||
*os << '"' << value.GetString() << '"';
|
||||
break;
|
||||
@@ -137,12 +156,15 @@ void PrintTo(const Value& value, std::ostream* os) {
|
||||
case NT_DOUBLE_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetDoubleArray());
|
||||
break;
|
||||
case NT_FLOAT_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetFloatArray());
|
||||
break;
|
||||
case NT_INTEGER_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetIntegerArray());
|
||||
break;
|
||||
case NT_STRING_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetStringArray());
|
||||
break;
|
||||
case NT_RPC:
|
||||
*os << ::testing::PrintToString(value.GetRpc());
|
||||
break;
|
||||
default:
|
||||
*os << "UNKNOWN TYPE " << value.type();
|
||||
break;
|
||||
@@ -150,4 +172,11 @@ void PrintTo(const Value& value, std::ostream* os) {
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const PubSubOptions& options, std::ostream* os) {
|
||||
*os << "PubSubOptions{periodic=" << options.periodic
|
||||
<< ", pollStorageSize=" << options.pollStorageSize
|
||||
<< ", logging=" << options.sendAll
|
||||
<< ", keepDuplicates=" << options.keepDuplicates << '}';
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -2,54 +2,65 @@
|
||||
// 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.
|
||||
|
||||
#ifndef NTCORE_TESTPRINTERS_H_
|
||||
#define NTCORE_TESTPRINTERS_H_
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <wpi/span.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
class json;
|
||||
|
||||
inline void PrintTo(std::string_view str, ::std::ostream* os) {
|
||||
::testing::internal::PrintStringTo(std::string{str}, os);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrintTo(span<T> val, ::std::ostream* os) {
|
||||
*os << '{';
|
||||
bool first = true;
|
||||
for (auto v : val) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
*os << ", ";
|
||||
}
|
||||
*os << ::testing::PrintToString(v);
|
||||
}
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const json& val, ::std::ostream* os);
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
class EntryNotification;
|
||||
namespace net3 {
|
||||
class Message3;
|
||||
} // namespace net3
|
||||
|
||||
namespace net {
|
||||
struct ClientMessage;
|
||||
struct ServerMessage;
|
||||
} // namespace net
|
||||
|
||||
// class EntryNotification;
|
||||
class Handle;
|
||||
class Message;
|
||||
class PubSubOptions;
|
||||
class Value;
|
||||
|
||||
void PrintTo(const EntryNotification& event, std::ostream* os);
|
||||
// void PrintTo(const EntryNotification& event, std::ostream* os);
|
||||
void PrintTo(const Handle& handle, std::ostream* os);
|
||||
|
||||
void PrintTo(const Message& msg, std::ostream* os);
|
||||
|
||||
inline void PrintTo(std::shared_ptr<Message> msg, std::ostream* os) {
|
||||
*os << "shared_ptr{";
|
||||
if (msg) {
|
||||
PrintTo(*msg, os);
|
||||
}
|
||||
*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);
|
||||
|
||||
inline void PrintTo(std::shared_ptr<Value> value, std::ostream* os) {
|
||||
*os << "shared_ptr{";
|
||||
if (value) {
|
||||
PrintTo(*value, os);
|
||||
}
|
||||
*os << '}';
|
||||
}
|
||||
void PrintTo(const PubSubOptions& options, std::ostream* os);
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_TESTPRINTERS_H_
|
||||
|
||||
286
ntcore/src/test/native/cpp/TopicListenerTest.cpp
Normal file
286
ntcore/src/test/native/cpp/TopicListenerTest.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
// 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 <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <wpi/Synchronization.h>
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
class TopicListenerTest : public ::testing::Test {
|
||||
public:
|
||||
TopicListenerTest()
|
||||
: m_serverInst(nt::CreateInstance()), m_clientInst(nt::CreateInstance()) {
|
||||
nt::SetNetworkIdentity(m_serverInst, "server");
|
||||
nt::SetNetworkIdentity(m_clientInst, "client");
|
||||
#if 0
|
||||
nt::AddLogger(server_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "SERVER: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
nt::AddLogger(client_inst,
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fprintf(stderr, "CLIENT: %s\n", msg.message.c_str());
|
||||
},
|
||||
0, UINT_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
~TopicListenerTest() override {
|
||||
nt::DestroyInstance(m_serverInst);
|
||||
nt::DestroyInstance(m_clientInst);
|
||||
}
|
||||
|
||||
void Connect(unsigned int port);
|
||||
static void PublishTopics(NT_Inst inst);
|
||||
void CheckEvents(const std::vector<nt::TopicNotification>& events,
|
||||
NT_TopicListener handle, unsigned int flags,
|
||||
std::string_view topicName = "/foo/bar");
|
||||
|
||||
protected:
|
||||
NT_Inst m_serverInst;
|
||||
NT_Inst m_clientInst;
|
||||
};
|
||||
|
||||
void TopicListenerTest::Connect(unsigned int port) {
|
||||
nt::StartServer(m_serverInst, "topiclistenertest.json", "127.0.0.1", 0, port);
|
||||
nt::StartClient4(m_clientInst);
|
||||
nt::SetServer(m_clientInst, "127.0.0.1", port);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(m_clientInst);
|
||||
nt::AddPolledConnectionListener(poller, false);
|
||||
bool timedOut = false;
|
||||
if (!wpi::WaitForObject(poller, 1.0, &timedOut)) {
|
||||
FAIL() << "client didn't connect to server";
|
||||
}
|
||||
}
|
||||
|
||||
void TopicListenerTest::PublishTopics(NT_Inst inst) {
|
||||
nt::Publish(nt::GetTopic(inst, "/foo/bar"), NT_DOUBLE, "double");
|
||||
nt::Publish(nt::GetTopic(inst, "/foo"), NT_DOUBLE, "double");
|
||||
nt::Publish(nt::GetTopic(inst, "/baz"), NT_DOUBLE, "double");
|
||||
}
|
||||
|
||||
void TopicListenerTest::CheckEvents(
|
||||
const std::vector<nt::TopicNotification>& events, NT_TopicListener handle,
|
||||
unsigned int flags, std::string_view topicName) {
|
||||
ASSERT_EQ(events.size(), 1u);
|
||||
ASSERT_EQ(events[0].listener, handle);
|
||||
ASSERT_EQ(events[0].info.topic, nt::GetTopic(m_serverInst, topicName));
|
||||
ASSERT_EQ(events[0].info.name, topicName);
|
||||
ASSERT_EQ(events[0].flags, flags);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicNewLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicNewRemote) {
|
||||
Connect(10010);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PUBLISH);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
nt::Flush(m_clientInst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, nt::GetTopic(m_serverInst, "/foo"),
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicUnpublishLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
nt::Unpublish(pub);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicUnpublishRemote) {
|
||||
Connect(10010);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_UNPUBLISH);
|
||||
|
||||
auto pub =
|
||||
nt::Publish(nt::GetTopic(m_clientInst, "/foo"), NT_DOUBLE, "double");
|
||||
nt::Flush(m_clientInst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
nt::Unpublish(pub);
|
||||
|
||||
nt::Flush(m_clientInst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_UNPUBLISH, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, TopicPropertiesLocal) {
|
||||
auto topic = nt::GetTopic(m_serverInst, "/foo");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, topic, NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
|
||||
nt::SetTopicProperty(topic, "foo", 5);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_TopicPropertiesRemote) {
|
||||
Connect(10010);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
// the topic needs to actually exist
|
||||
nt::Publish(nt::GetTopic(m_serverInst, "/foo"), NT_BOOLEAN, "boolean");
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, nt::GetTopic(m_serverInst, "/foo"), NT_TOPIC_NOTIFY_PROPERTIES);
|
||||
nt::FlushLocal(m_serverInst);
|
||||
|
||||
nt::SetTopicProperty(nt::GetTopic(m_clientInst, "/foo"), "foo", 5);
|
||||
nt::Flush(m_clientInst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PROPERTIES, "/foo");
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishLocal) {
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, DISABLED_PrefixPublishRemote) {
|
||||
Connect(10011);
|
||||
if (HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle =
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
|
||||
PublishTopics(m_clientInst);
|
||||
|
||||
nt::Flush(m_clientInst);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle, NT_TOPIC_NOTIFY_PUBLISH);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixPublishImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
auto handle = nt::AddPolledTopicListener(
|
||||
poller, {{"/foo/"}}, NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
CheckEvents(events, handle,
|
||||
NT_TOPIC_NOTIFY_PUBLISH | NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
}
|
||||
|
||||
TEST_F(TopicListenerTest, PrefixUnpublishPropsImm) {
|
||||
PublishTopics(m_serverInst);
|
||||
|
||||
auto poller = nt::CreateTopicListenerPoller(m_serverInst);
|
||||
nt::AddPolledTopicListener(poller, {{"/foo/"}},
|
||||
NT_TOPIC_NOTIFY_UNPUBLISH |
|
||||
NT_TOPIC_NOTIFY_PROPERTIES |
|
||||
NT_TOPIC_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
auto events = nt::ReadTopicListenerQueue(poller);
|
||||
ASSERT_TRUE(events.empty());
|
||||
}
|
||||
280
ntcore/src/test/native/cpp/ValueListenerTest.cpp
Normal file
280
ntcore/src/test/native/cpp/ValueListenerTest.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
// 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 <wpi/StringExtras.h>
|
||||
#include <wpi/Synchronization.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_c.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace nt {
|
||||
|
||||
// Test only local here; it's more reliable to mock the network
|
||||
class ValueListenerTest : public ::testing::Test {
|
||||
public:
|
||||
ValueListenerTest() : m_inst{nt::CreateInstance()} {
|
||||
nt::SetNetworkIdentity(m_inst, "server");
|
||||
}
|
||||
|
||||
~ValueListenerTest() override { nt::DestroyInstance(m_inst); }
|
||||
|
||||
protected:
|
||||
NT_Inst m_inst;
|
||||
};
|
||||
|
||||
TEST_F(ValueListenerTest, MultiPollSub) {
|
||||
auto topic = nt::GetTopic(m_inst, "foo");
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
auto sub = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller1 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller2 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto poller3 = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller1, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller2, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h3 = nt::AddPolledValueListener(poller3, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller1, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller2, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller3, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results1 = nt::ReadValueListenerQueue(poller1);
|
||||
auto results2 = nt::ReadValueListenerQueue(poller2);
|
||||
auto results3 = nt::ReadValueListenerQueue(poller3);
|
||||
|
||||
ASSERT_EQ(results1.size(), 1u);
|
||||
EXPECT_EQ(results1[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results1[0].listener, h1);
|
||||
EXPECT_EQ(results1[0].subentry, sub);
|
||||
EXPECT_EQ(results1[0].topic, topic);
|
||||
EXPECT_EQ(results1[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results2.size(), 1u);
|
||||
EXPECT_EQ(results2[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results2[0].listener, h2);
|
||||
EXPECT_EQ(results2[0].subentry, sub);
|
||||
EXPECT_EQ(results2[0].topic, topic);
|
||||
EXPECT_EQ(results2[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
ASSERT_EQ(results3.size(), 1u);
|
||||
EXPECT_EQ(results3[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results3[0].listener, h3);
|
||||
EXPECT_EQ(results3[0].subentry, sub);
|
||||
EXPECT_EQ(results3[0].topic, topic);
|
||||
EXPECT_EQ(results3[0].value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSub) {
|
||||
auto topic = nt::GetTopic(m_inst, "foo");
|
||||
auto pub = nt::Publish(topic, NT_DOUBLE, "double");
|
||||
auto sub1 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
|
||||
nt::SetDouble(pub, 0);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollMultiSubTopic) {
|
||||
auto topic1 = nt::GetTopic(m_inst, "foo");
|
||||
auto topic2 = nt::GetTopic(m_inst, "bar");
|
||||
auto pub1 = nt::Publish(topic1, NT_DOUBLE, "double");
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto sub1 = nt::Subscribe(topic1, NT_DOUBLE, "double");
|
||||
auto sub2 = nt::Subscribe(topic2, NT_DOUBLE, "double");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h1 = nt::AddPolledValueListener(poller, sub1, NT_VALUE_NOTIFY_LOCAL);
|
||||
auto h2 = nt::AddPolledValueListener(poller, sub2, NT_VALUE_NOTIFY_LOCAL);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].listener, h1);
|
||||
EXPECT_EQ(results[0].subentry, sub1);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].listener, h2);
|
||||
EXPECT_EQ(results[1].subentry, sub2);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollSubMultiple) {
|
||||
auto topic1 = nt::GetTopic(m_inst, "foo/1");
|
||||
auto topic2 = nt::GetTopic(m_inst, "foo/2");
|
||||
auto pub1 = nt::Publish(topic1, NT_DOUBLE, "double");
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto sub = nt::SubscribeMultiple(m_inst, {{"foo"}});
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, sub, NT_VALUE_NOTIFY_LOCAL);
|
||||
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollEntry) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(poller, entry, NT_VALUE_NOTIFY_LOCAL);
|
||||
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediate) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateNoValue) {
|
||||
auto entry = nt::GetEntry(m_inst, "foo");
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, entry, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_FALSE(wpi::WaitForObject(poller, 0.02, &timedOut));
|
||||
ASSERT_TRUE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
ASSERT_TRUE(results.empty());
|
||||
|
||||
// now set a value
|
||||
ASSERT_TRUE(nt::SetDouble(entry, 0));
|
||||
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
results = nt::ReadValueListenerQueue(poller);
|
||||
ASSERT_FALSE(timedOut);
|
||||
|
||||
ASSERT_EQ(results.size(), 1u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_LOCAL);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, entry);
|
||||
EXPECT_EQ(results[0].topic, nt::GetTopic(m_inst, "foo"));
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
}
|
||||
|
||||
TEST_F(ValueListenerTest, PollImmediateSubMultiple) {
|
||||
auto topic1 = nt::GetTopic(m_inst, "foo/1");
|
||||
auto topic2 = nt::GetTopic(m_inst, "foo/2");
|
||||
auto pub1 = nt::Publish(topic1, NT_DOUBLE, "double");
|
||||
auto pub2 = nt::Publish(topic2, NT_DOUBLE, "double");
|
||||
auto sub = nt::SubscribeMultiple(m_inst, {{"foo"}});
|
||||
nt::SetDouble(pub1, 0);
|
||||
nt::SetDouble(pub2, 1);
|
||||
|
||||
auto poller = nt::CreateValueListenerPoller(m_inst);
|
||||
auto h = nt::AddPolledValueListener(
|
||||
poller, sub, NT_VALUE_NOTIFY_LOCAL | NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
|
||||
bool timedOut = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(poller, 1.0, &timedOut));
|
||||
ASSERT_FALSE(timedOut);
|
||||
auto results = nt::ReadValueListenerQueue(poller);
|
||||
|
||||
ASSERT_EQ(results.size(), 2u);
|
||||
EXPECT_EQ(results[0].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[0].listener, h);
|
||||
EXPECT_EQ(results[0].subentry, sub);
|
||||
EXPECT_EQ(results[0].topic, topic1);
|
||||
EXPECT_EQ(results[0].value, nt::Value::MakeDouble(0.0));
|
||||
|
||||
EXPECT_EQ(results[1].flags, NT_VALUE_NOTIFY_IMMEDIATE);
|
||||
EXPECT_EQ(results[1].listener, h);
|
||||
EXPECT_EQ(results[1].subentry, sub);
|
||||
EXPECT_EQ(results[1].topic, topic2);
|
||||
EXPECT_EQ(results[1].value, nt::Value::MakeDouble(1.0));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -9,10 +9,9 @@
|
||||
namespace nt {
|
||||
|
||||
bool ValueMatcher::MatchAndExplain(
|
||||
std::shared_ptr<Value> val,
|
||||
::testing::MatchResultListener* listener) const {
|
||||
Value val, ::testing::MatchResultListener* listener) const {
|
||||
if ((!val && goodval) || (val && !goodval) ||
|
||||
(val && goodval && *val != *goodval)) {
|
||||
(val && goodval && val != goodval)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// 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.
|
||||
|
||||
#ifndef NTCORE_VALUEMATCHER_H_
|
||||
#define NTCORE_VALUEMATCHER_H_
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
@@ -14,26 +13,21 @@
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ValueMatcher
|
||||
: public ::testing::MatcherInterface<std::shared_ptr<Value>> {
|
||||
class ValueMatcher : public ::testing::MatcherInterface<Value> {
|
||||
public:
|
||||
explicit ValueMatcher(std::shared_ptr<Value> goodval_)
|
||||
: goodval(std::move(goodval_)) {}
|
||||
explicit ValueMatcher(Value goodval_) : goodval(std::move(goodval_)) {}
|
||||
|
||||
bool MatchAndExplain(std::shared_ptr<Value> msg,
|
||||
bool MatchAndExplain(Value msg,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Value> goodval;
|
||||
Value goodval;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<std::shared_ptr<Value>> ValueEq(
|
||||
std::shared_ptr<Value> goodval) {
|
||||
inline ::testing::Matcher<Value> ValueEq(const Value& goodval) {
|
||||
return ::testing::MakeMatcher(new ValueMatcher(goodval));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_VALUEMATCHER_H_
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace wpi {
|
||||
template <typename T>
|
||||
inline bool operator==(span<T> lhs, span<T> rhs) {
|
||||
template <typename T, typename U>
|
||||
inline bool operator==(span<T> lhs, span<U> rhs) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -35,19 +35,19 @@ TEST_F(ValueTest, ConstructEmpty) {
|
||||
|
||||
TEST_F(ValueTest, Boolean) {
|
||||
auto v = Value::MakeBoolean(false);
|
||||
ASSERT_EQ(NT_BOOLEAN, v->type());
|
||||
ASSERT_FALSE(v->GetBoolean());
|
||||
ASSERT_EQ(NT_BOOLEAN, v.type());
|
||||
ASSERT_FALSE(v.GetBoolean());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN, cv.type);
|
||||
ASSERT_EQ(0, cv.data.v_boolean);
|
||||
|
||||
v = Value::MakeBoolean(true);
|
||||
ASSERT_EQ(NT_BOOLEAN, v->type());
|
||||
ASSERT_TRUE(v->GetBoolean());
|
||||
ASSERT_EQ(NT_BOOLEAN, v.type());
|
||||
ASSERT_TRUE(v.GetBoolean());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN, cv.type);
|
||||
ASSERT_EQ(1, cv.data.v_boolean);
|
||||
|
||||
@@ -56,19 +56,19 @@ TEST_F(ValueTest, Boolean) {
|
||||
|
||||
TEST_F(ValueTest, Double) {
|
||||
auto v = Value::MakeDouble(0.5);
|
||||
ASSERT_EQ(NT_DOUBLE, v->type());
|
||||
ASSERT_EQ(0.5, v->GetDouble());
|
||||
ASSERT_EQ(NT_DOUBLE, v.type());
|
||||
ASSERT_EQ(0.5, v.GetDouble());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE, cv.type);
|
||||
ASSERT_EQ(0.5, cv.data.v_double);
|
||||
|
||||
v = Value::MakeDouble(0.25);
|
||||
ASSERT_EQ(NT_DOUBLE, v->type());
|
||||
ASSERT_EQ(0.25, v->GetDouble());
|
||||
ASSERT_EQ(NT_DOUBLE, v.type());
|
||||
ASSERT_EQ(0.25, v.GetDouble());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE, cv.type);
|
||||
ASSERT_EQ(0.25, cv.data.v_double);
|
||||
|
||||
@@ -77,20 +77,20 @@ TEST_F(ValueTest, Double) {
|
||||
|
||||
TEST_F(ValueTest, String) {
|
||||
auto v = Value::MakeString("hello");
|
||||
ASSERT_EQ(NT_STRING, v->type());
|
||||
ASSERT_EQ("hello", v->GetString());
|
||||
ASSERT_EQ(NT_STRING, v.type());
|
||||
ASSERT_EQ("hello", v.GetString());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_STRING, cv.type);
|
||||
ASSERT_EQ("hello"sv, cv.data.v_string.str);
|
||||
ASSERT_EQ(5u, cv.data.v_string.len);
|
||||
|
||||
v = Value::MakeString("goodbye");
|
||||
ASSERT_EQ(NT_STRING, v->type());
|
||||
ASSERT_EQ("goodbye", v->GetString());
|
||||
ASSERT_EQ(NT_STRING, v.type());
|
||||
ASSERT_EQ("goodbye", v.GetString());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_STRING, cv.type);
|
||||
ASSERT_EQ("goodbye"sv, cv.data.v_string.str);
|
||||
ASSERT_EQ(7u, cv.data.v_string.len);
|
||||
@@ -99,24 +99,28 @@ TEST_F(ValueTest, String) {
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, Raw) {
|
||||
auto v = Value::MakeRaw("hello");
|
||||
ASSERT_EQ(NT_RAW, v->type());
|
||||
ASSERT_EQ("hello", v->GetRaw());
|
||||
std::vector<uint8_t> arr{5, 4, 3, 2, 1};
|
||||
auto v = Value::MakeRaw(arr);
|
||||
ASSERT_EQ(NT_RAW, v.type());
|
||||
ASSERT_EQ(wpi::span<const uint8_t>(arr), v.GetRaw());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_RAW, cv.type);
|
||||
ASSERT_EQ("hello"sv, cv.data.v_string.str);
|
||||
ASSERT_EQ(5u, cv.data.v_string.len);
|
||||
ASSERT_EQ(5u, cv.data.v_raw.size);
|
||||
ASSERT_EQ(wpi::span(reinterpret_cast<const uint8_t*>("\5\4\3\2\1"), 5),
|
||||
wpi::span(cv.data.v_raw.data, 5));
|
||||
|
||||
v = Value::MakeRaw("goodbye");
|
||||
ASSERT_EQ(NT_RAW, v->type());
|
||||
ASSERT_EQ("goodbye", v->GetRaw());
|
||||
std::vector<uint8_t> arr2{1, 2, 3, 4, 5, 6};
|
||||
v = Value::MakeRaw(arr2);
|
||||
ASSERT_EQ(NT_RAW, v.type());
|
||||
ASSERT_EQ(wpi::span<const uint8_t>(arr2), v.GetRaw());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_RAW, cv.type);
|
||||
ASSERT_EQ("goodbye"sv, cv.data.v_string.str);
|
||||
ASSERT_EQ(7u, cv.data.v_string.len);
|
||||
ASSERT_EQ(6u, cv.data.v_raw.size);
|
||||
ASSERT_EQ(wpi::span(reinterpret_cast<const uint8_t*>("\1\2\3\4\5\6"), 6),
|
||||
wpi::span(cv.data.v_raw.data, 6));
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
@@ -124,11 +128,11 @@ TEST_F(ValueTest, Raw) {
|
||||
TEST_F(ValueTest, BooleanArray) {
|
||||
std::vector<int> vec{1, 0, 1};
|
||||
auto v = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v->GetBooleanArray());
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v.GetBooleanArray());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -138,10 +142,10 @@ TEST_F(ValueTest, BooleanArray) {
|
||||
// assign with same size
|
||||
vec = {0, 1, 0};
|
||||
v = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v->GetBooleanArray());
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v.GetBooleanArray());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -151,10 +155,10 @@ TEST_F(ValueTest, BooleanArray) {
|
||||
// assign with different size
|
||||
vec = {1, 0};
|
||||
v = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v->GetBooleanArray());
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const int>(vec), v.GetBooleanArray());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -166,11 +170,11 @@ TEST_F(ValueTest, BooleanArray) {
|
||||
TEST_F(ValueTest, DoubleArray) {
|
||||
std::vector<double> vec{0.5, 0.25, 0.5};
|
||||
auto v = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v->GetDoubleArray());
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v.GetDoubleArray());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -180,10 +184,10 @@ TEST_F(ValueTest, DoubleArray) {
|
||||
// assign with same size
|
||||
vec = {0.25, 0.5, 0.25};
|
||||
v = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v->GetDoubleArray());
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v.GetDoubleArray());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -193,10 +197,10 @@ TEST_F(ValueTest, DoubleArray) {
|
||||
// assign with different size
|
||||
vec = {0.5, 0.25};
|
||||
v = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v->GetDoubleArray());
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v.type());
|
||||
ASSERT_EQ(wpi::span<const double>(vec), v.GetDoubleArray());
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &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]);
|
||||
@@ -211,14 +215,14 @@ TEST_F(ValueTest, StringArray) {
|
||||
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]);
|
||||
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]);
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_string.size);
|
||||
ASSERT_EQ("hello"sv, cv.data.arr_string.arr[0].str);
|
||||
@@ -231,13 +235,13 @@ 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]);
|
||||
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]);
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_string.size);
|
||||
ASSERT_EQ("s1"sv, cv.data.arr_string.arr[0].str);
|
||||
@@ -249,12 +253,12 @@ TEST_F(ValueTest, StringArray) {
|
||||
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]);
|
||||
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]);
|
||||
NT_DisposeValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ConvertToC(v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(2u, cv.data.arr_string.size);
|
||||
ASSERT_EQ("short"sv, cv.data.arr_string.arr[0].str);
|
||||
@@ -286,69 +290,69 @@ TEST_F(ValueTest, UnassignedComparison) {
|
||||
TEST_F(ValueTest, MixedComparison) {
|
||||
Value v1;
|
||||
auto v2 = Value::MakeBoolean(true);
|
||||
ASSERT_NE(v1, *v2); // unassigned vs boolean
|
||||
ASSERT_NE(v1, v2); // unassigned vs boolean
|
||||
auto v3 = Value::MakeDouble(0.5);
|
||||
ASSERT_NE(*v2, *v3); // boolean vs double
|
||||
ASSERT_NE(v2, v3); // boolean vs double
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, BooleanComparison) {
|
||||
auto v1 = Value::MakeBoolean(true);
|
||||
auto v2 = Value::MakeBoolean(true);
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
v2 = Value::MakeBoolean(false);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, DoubleComparison) {
|
||||
auto v1 = Value::MakeDouble(0.25);
|
||||
auto v2 = Value::MakeDouble(0.25);
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
v2 = Value::MakeDouble(0.5);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, StringComparison) {
|
||||
auto v1 = Value::MakeString("hello");
|
||||
auto v2 = Value::MakeString("hello");
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
v2 = Value::MakeString("world"); // different contents
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
v2 = Value::MakeString("goodbye"); // different size
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, BooleanArrayComparison) {
|
||||
std::vector<int> vec{1, 0, 1};
|
||||
auto v1 = Value::MakeBooleanArray(vec);
|
||||
auto v2 = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
|
||||
// different contents
|
||||
vec = {1, 1, 1};
|
||||
v2 = Value::MakeBooleanArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
|
||||
// different size
|
||||
vec = {1, 0};
|
||||
v2 = Value::MakeBooleanArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, DoubleArrayComparison) {
|
||||
std::vector<double> vec{0.5, 0.25, 0.5};
|
||||
auto v1 = Value::MakeDoubleArray(vec);
|
||||
auto v2 = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
|
||||
// different contents
|
||||
vec = {0.5, 0.5, 0.5};
|
||||
v2 = Value::MakeDoubleArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
|
||||
// different size
|
||||
vec = {0.5, 0.25};
|
||||
v2 = Value::MakeDoubleArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, StringArrayComparison) {
|
||||
@@ -362,7 +366,7 @@ TEST_F(ValueTest, StringArrayComparison) {
|
||||
vec.push_back("goodbye");
|
||||
vec.push_back("string");
|
||||
auto v2 = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
ASSERT_EQ(v1, v2);
|
||||
|
||||
// different contents
|
||||
vec.clear();
|
||||
@@ -370,7 +374,7 @@ TEST_F(ValueTest, StringArrayComparison) {
|
||||
vec.push_back("goodby2");
|
||||
vec.push_back("string");
|
||||
v2 = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
|
||||
// different sized contents
|
||||
vec.clear();
|
||||
@@ -378,14 +382,14 @@ TEST_F(ValueTest, StringArrayComparison) {
|
||||
vec.push_back("goodbye2");
|
||||
vec.push_back("string");
|
||||
v2 = Value::MakeStringArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
|
||||
// different size
|
||||
vec.clear();
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodbye");
|
||||
v2 = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_NE(*v1, *v2);
|
||||
ASSERT_NE(v1, v2);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
@@ -1,647 +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 <stdint.h>
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "WireDecoder.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireDecoderTest : public ::testing::Test {
|
||||
protected:
|
||||
WireDecoderTest() {
|
||||
v_boolean = Value::MakeBoolean(true);
|
||||
v_double = Value::MakeDouble(1.0);
|
||||
v_string = Value::MakeString("hello"sv);
|
||||
v_raw = Value::MakeRaw("hello"sv);
|
||||
v_boolean_array = Value::MakeBooleanArray(std::vector<int>{0, 1, 0});
|
||||
v_boolean_array_big = Value::MakeBooleanArray(std::vector<int>(255));
|
||||
v_double_array = Value::MakeDoubleArray(std::vector<double>{0.5, 0.25});
|
||||
v_double_array_big = Value::MakeDoubleArray(std::vector<double>(255));
|
||||
|
||||
std::vector<std::string> sa;
|
||||
sa.push_back("hello");
|
||||
sa.push_back("goodbye");
|
||||
v_string_array = Value::MakeStringArray(std::move(sa));
|
||||
|
||||
sa.clear();
|
||||
for (int i = 0; i < 255; ++i) {
|
||||
sa.push_back("h");
|
||||
}
|
||||
v_string_array_big = Value::MakeStringArray(std::move(sa));
|
||||
|
||||
s_normal = std::string("hello");
|
||||
|
||||
s_long.clear();
|
||||
s_long.append(127, '*');
|
||||
s_long.push_back('x');
|
||||
|
||||
s_big2.clear();
|
||||
s_big2.append(65534, '*');
|
||||
s_big2.push_back('x');
|
||||
|
||||
s_big3.clear();
|
||||
s_big3.append(65534, '*');
|
||||
s_big3.append(3, 'x');
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> v_boolean, v_double, v_string, v_raw;
|
||||
std::shared_ptr<Value> v_boolean_array, v_boolean_array_big;
|
||||
std::shared_ptr<Value> v_double_array, v_double_array_big;
|
||||
std::shared_ptr<Value> v_string_array, v_string_array_big;
|
||||
|
||||
std::string s_normal, s_long, s_big2, s_big3;
|
||||
};
|
||||
|
||||
TEST_F(WireDecoderTest, Construct) {
|
||||
wpi::raw_mem_istream is("", 0);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
EXPECT_EQ(nullptr, d.error());
|
||||
EXPECT_EQ(0x0300u, d.proto_rev());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, SetProtoRev) {
|
||||
wpi::raw_mem_istream is("", 0);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
d.set_proto_rev(0x0200u);
|
||||
EXPECT_EQ(0x0200u, d.proto_rev());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, Read8) {
|
||||
wpi::raw_mem_istream is("\x05\x01\x00", 3);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
unsigned int val;
|
||||
ASSERT_TRUE(d.Read8(&val));
|
||||
EXPECT_EQ(5u, val);
|
||||
ASSERT_TRUE(d.Read8(&val));
|
||||
EXPECT_EQ(1u, val);
|
||||
ASSERT_TRUE(d.Read8(&val));
|
||||
EXPECT_EQ(0u, val);
|
||||
ASSERT_FALSE(d.Read8(&val));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, Read16) {
|
||||
wpi::raw_mem_istream is("\x00\x05\x00\x01\x45\x67\x00\x00", 8);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
unsigned int val;
|
||||
ASSERT_TRUE(d.Read16(&val));
|
||||
EXPECT_EQ(5u, val);
|
||||
ASSERT_TRUE(d.Read16(&val));
|
||||
EXPECT_EQ(1u, val);
|
||||
ASSERT_TRUE(d.Read16(&val));
|
||||
EXPECT_EQ(0x4567u, val);
|
||||
ASSERT_TRUE(d.Read16(&val));
|
||||
EXPECT_EQ(0u, val);
|
||||
ASSERT_FALSE(d.Read16(&val));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, Read32) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
|
||||
"\x12\x34\x56\x78\x00\x00\x00\x00",
|
||||
20);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
uint32_t val;
|
||||
ASSERT_TRUE(d.Read32(&val));
|
||||
EXPECT_EQ(5ul, val);
|
||||
ASSERT_TRUE(d.Read32(&val));
|
||||
EXPECT_EQ(1ul, val);
|
||||
ASSERT_TRUE(d.Read32(&val));
|
||||
EXPECT_EQ(0xabcdul, val);
|
||||
ASSERT_TRUE(d.Read32(&val));
|
||||
EXPECT_EQ(0x12345678ul, val);
|
||||
ASSERT_TRUE(d.Read32(&val));
|
||||
EXPECT_EQ(0ul, val);
|
||||
ASSERT_FALSE(d.Read32(&val));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDouble) {
|
||||
// values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
wpi::raw_mem_istream is(
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x41\x0c\x13\x80\x00\x00\x00\x00"
|
||||
"\x7f\xf0\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x10\x00\x00\x00\x00\x00\x00"
|
||||
"\x7f\xef\xff\xff\xff\xff\xff\xff",
|
||||
40);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
double val;
|
||||
ASSERT_TRUE(d.ReadDouble(&val));
|
||||
EXPECT_EQ(0.0, val);
|
||||
ASSERT_TRUE(d.ReadDouble(&val));
|
||||
EXPECT_EQ(2.3e5, val);
|
||||
ASSERT_TRUE(d.ReadDouble(&val));
|
||||
EXPECT_EQ(std::numeric_limits<double>::infinity(), val);
|
||||
ASSERT_TRUE(d.ReadDouble(&val));
|
||||
EXPECT_EQ(DBL_MIN, val);
|
||||
ASSERT_TRUE(d.ReadDouble(&val));
|
||||
EXPECT_EQ(DBL_MAX, val);
|
||||
ASSERT_FALSE(d.ReadDouble(&val));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadUleb128) {
|
||||
wpi::raw_mem_istream is("\x00\x7f\x80\x01\x80", 5);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
uint64_t val;
|
||||
ASSERT_TRUE(d.ReadUleb128(&val));
|
||||
EXPECT_EQ(0ul, val);
|
||||
ASSERT_TRUE(d.ReadUleb128(&val));
|
||||
EXPECT_EQ(0x7ful, val);
|
||||
ASSERT_TRUE(d.ReadUleb128(&val));
|
||||
EXPECT_EQ(0x80ul, val);
|
||||
ASSERT_FALSE(d.ReadUleb128(&val)); // partial
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadType) {
|
||||
wpi::raw_mem_istream is("\x00\x01\x02\x03\x10\x11\x12\x20", 8);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
NT_Type val;
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_BOOLEAN, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_DOUBLE, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_STRING, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_RAW, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_BOOLEAN_ARRAY, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_DOUBLE_ARRAY, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_STRING_ARRAY, val);
|
||||
ASSERT_TRUE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_RPC, val);
|
||||
ASSERT_FALSE(d.ReadType(&val));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadTypeError) {
|
||||
wpi::raw_mem_istream is("\x30", 1);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
NT_Type val;
|
||||
ASSERT_FALSE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_UNASSIGNED, val);
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, Reset) {
|
||||
wpi::raw_mem_istream is("\x30", 1);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
NT_Type val;
|
||||
ASSERT_FALSE(d.ReadType(&val));
|
||||
EXPECT_EQ(NT_UNASSIGNED, val);
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
d.Reset();
|
||||
EXPECT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanValue2) {
|
||||
wpi::raw_mem_istream is("\x01\x00", 2);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean, *val);
|
||||
|
||||
auto v_false = Value::MakeBoolean(false);
|
||||
val = d.ReadValue(NT_BOOLEAN);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_false, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleValue2) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
|
||||
16);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double, *val);
|
||||
|
||||
val = d.ReadValue(NT_DOUBLE);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringValue2) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x00\x05hello\x00\x03"
|
||||
"bye\x55",
|
||||
13);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_STRING);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string, *val);
|
||||
|
||||
auto v_bye = Value::MakeString("bye"sv);
|
||||
val = d.ReadValue(NT_STRING);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_bye, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanArrayValue2) {
|
||||
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array, *val);
|
||||
|
||||
auto v_boolean_array2 = Value::MakeBooleanArray(std::vector<int>{1, 0});
|
||||
val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array2, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanArrayBigValue2) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
s.append(255, '\x00');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleArrayValue2) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
|
||||
18);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double_array, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleArrayBigValue2) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
s.append(255 * 8, '\x00');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringArrayValue2) {
|
||||
wpi::raw_mem_istream is("\x02\x00\x05hello\x00\x07goodbye\x55", 18);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_STRING_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string_array, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringArrayBigValue2) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
for (int i = 0; i < 255; ++i) {
|
||||
s.append("\x00\x01h", 3);
|
||||
}
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
auto val = d.ReadValue(NT_STRING_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadValueError2) {
|
||||
wpi::raw_mem_istream is("", 0);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
|
||||
d.Reset();
|
||||
ASSERT_FALSE(d.ReadValue(NT_RAW)); // not supported
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
|
||||
d.Reset();
|
||||
ASSERT_FALSE(d.ReadValue(NT_RPC)); // not supported
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanValue3) {
|
||||
wpi::raw_mem_istream is("\x01\x00", 2);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean, *val);
|
||||
|
||||
auto v_false = Value::MakeBoolean(false);
|
||||
val = d.ReadValue(NT_BOOLEAN);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_false, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleValue3) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
|
||||
16);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double, *val);
|
||||
|
||||
val = d.ReadValue(NT_DOUBLE);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringValue3) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x05hello\x03"
|
||||
"bye\x55",
|
||||
11);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_STRING);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string, *val);
|
||||
|
||||
auto v_bye = Value::MakeString("bye"sv);
|
||||
val = d.ReadValue(NT_STRING);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_bye, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadRawValue3) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x05hello\x03"
|
||||
"bye\x55",
|
||||
11);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_RAW);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_raw, *val);
|
||||
|
||||
auto v_bye = Value::MakeRaw("bye"sv);
|
||||
val = d.ReadValue(NT_RAW);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_bye, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_RAW));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanArrayValue3) {
|
||||
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array, *val);
|
||||
|
||||
auto v_boolean_array2 = Value::MakeBooleanArray(std::vector<int>{1, 0});
|
||||
val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array2, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadBooleanArrayBigValue3) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
s.append(255, '\x00');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_boolean_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_BOOLEAN_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleArrayValue3) {
|
||||
wpi::raw_mem_istream is(
|
||||
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
|
||||
18);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double_array, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadDoubleArrayBigValue3) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
s.append(255 * 8, '\x00');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_double_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_DOUBLE_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringArrayValue3) {
|
||||
wpi::raw_mem_istream is("\x02\x05hello\x07goodbye\x55", 16);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_STRING_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string_array, *val);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadStringArrayBigValue3) {
|
||||
std::string s;
|
||||
s.push_back('\xff');
|
||||
for (int i = 0; i < 255; ++i) {
|
||||
s.append("\x01h", 2);
|
||||
}
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
auto val = d.ReadValue(NT_STRING_ARRAY);
|
||||
ASSERT_TRUE(static_cast<bool>(val));
|
||||
EXPECT_EQ(*v_string_array_big, *val);
|
||||
|
||||
ASSERT_FALSE(d.ReadValue(NT_STRING_ARRAY));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadValueError3) {
|
||||
wpi::raw_mem_istream is("", 0);
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
|
||||
ASSERT_NE(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadString2) {
|
||||
std::string s;
|
||||
s.append("\x00\x05", 2);
|
||||
s += s_normal;
|
||||
s.append("\x00\x80", 2);
|
||||
s += s_long;
|
||||
s.append("\xff\xff", 2);
|
||||
s += s_big2;
|
||||
s.push_back('\x55');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0200u, logger);
|
||||
std::string outs;
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_normal, outs);
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_long, outs);
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_big2, outs);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadString(&outs));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
TEST_F(WireDecoderTest, ReadString3) {
|
||||
std::string s;
|
||||
s.push_back('\x05');
|
||||
s += s_normal;
|
||||
s.append("\x80\x01", 2);
|
||||
s += s_long;
|
||||
s.append("\x81\x80\x04", 3);
|
||||
s += s_big3;
|
||||
s.push_back('\x55');
|
||||
wpi::raw_mem_istream is(s.data(), s.size());
|
||||
wpi::Logger logger;
|
||||
WireDecoder d(is, 0x0300u, logger);
|
||||
std::string outs;
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_normal, outs);
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_long, outs);
|
||||
ASSERT_TRUE(d.ReadString(&outs));
|
||||
EXPECT_EQ(s_big3, outs);
|
||||
|
||||
unsigned int b;
|
||||
ASSERT_TRUE(d.Read8(&b));
|
||||
EXPECT_EQ(0x55u, b);
|
||||
|
||||
ASSERT_FALSE(d.ReadString(&outs));
|
||||
ASSERT_EQ(nullptr, d.error());
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,500 +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 <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "WireEncoder.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireEncoderTest : public ::testing::Test {
|
||||
protected:
|
||||
WireEncoderTest() {
|
||||
v_empty = std::make_shared<Value>();
|
||||
v_boolean = Value::MakeBoolean(true);
|
||||
v_double = Value::MakeDouble(1.0);
|
||||
v_string = Value::MakeString("hello"sv);
|
||||
v_raw = Value::MakeRaw("hello"sv);
|
||||
v_boolean_array = Value::MakeBooleanArray(std::vector<int>{0, 1, 0});
|
||||
v_boolean_array_big = Value::MakeBooleanArray(std::vector<int>(256));
|
||||
v_double_array = Value::MakeDoubleArray(std::vector<double>{0.5, 0.25});
|
||||
v_double_array_big = Value::MakeDoubleArray(std::vector<double>(256));
|
||||
|
||||
std::vector<std::string> sa;
|
||||
sa.push_back("hello");
|
||||
sa.push_back("goodbye");
|
||||
v_string_array = Value::MakeStringArray(std::move(sa));
|
||||
|
||||
sa.clear();
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
sa.push_back("h");
|
||||
}
|
||||
v_string_array_big = Value::MakeStringArray(std::move(sa));
|
||||
|
||||
s_normal = "hello";
|
||||
|
||||
s_long.clear();
|
||||
s_long.append(127, '*');
|
||||
s_long.push_back('x');
|
||||
|
||||
s_big.clear();
|
||||
s_big.append(65534, '*');
|
||||
s_big.append(3, 'x');
|
||||
}
|
||||
|
||||
std::shared_ptr<Value> v_empty;
|
||||
std::shared_ptr<Value> v_boolean, v_double, v_string, v_raw;
|
||||
std::shared_ptr<Value> v_boolean_array, v_boolean_array_big;
|
||||
std::shared_ptr<Value> v_double_array, v_double_array_big;
|
||||
std::shared_ptr<Value> v_string_array, v_string_array_big;
|
||||
|
||||
std::string s_normal, s_long, s_big;
|
||||
};
|
||||
|
||||
TEST_F(WireEncoderTest, Construct) {
|
||||
WireEncoder e(0x0300u);
|
||||
EXPECT_EQ(0u, e.size());
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
EXPECT_EQ(0x0300u, e.proto_rev());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, SetProtoRev) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.set_proto_rev(0x0200u);
|
||||
EXPECT_EQ(0x0200u, e.proto_rev());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, Write8) {
|
||||
size_t off = BUFSIZE - 1;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.Write8(5u);
|
||||
e.Write8(0x101u); // should be truncated
|
||||
e.Write8(0u);
|
||||
ASSERT_EQ(3u, e.size() - off);
|
||||
ASSERT_EQ("\x05\x01\x00"sv, wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, Write16) {
|
||||
size_t off = BUFSIZE - 2;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.Write16(5u);
|
||||
e.Write16(0x10001u); // should be truncated
|
||||
e.Write16(0x4567u);
|
||||
e.Write16(0u);
|
||||
ASSERT_EQ(8u, e.size() - off);
|
||||
ASSERT_EQ("\x00\x05\x00\x01\x45\x67\x00\x00"sv,
|
||||
wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, Write32) {
|
||||
size_t off = BUFSIZE - 4;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.Write32(5ul);
|
||||
e.Write32(1ul);
|
||||
e.Write32(0xabcdul);
|
||||
e.Write32(0x12345678ul);
|
||||
e.Write32(0ul);
|
||||
ASSERT_EQ(20u, e.size() - off);
|
||||
ASSERT_EQ(std::string_view("\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
|
||||
"\x12\x34\x56\x78\x00\x00\x00\x00",
|
||||
20),
|
||||
wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteDouble) {
|
||||
size_t off = BUFSIZE - 8;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.WriteDouble(0.0);
|
||||
e.WriteDouble(2.3e5);
|
||||
e.WriteDouble(std::numeric_limits<double>::infinity());
|
||||
e.WriteDouble(DBL_MIN);
|
||||
e.WriteDouble(DBL_MAX);
|
||||
ASSERT_EQ(40u, e.size() - off);
|
||||
// golden values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
ASSERT_EQ(std::string_view("\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x41\x0c\x13\x80\x00\x00\x00\x00"
|
||||
"\x7f\xf0\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x10\x00\x00\x00\x00\x00\x00"
|
||||
"\x7f\xef\xff\xff\xff\xff\xff\xff",
|
||||
40),
|
||||
wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteUleb128) {
|
||||
size_t off = BUFSIZE - 2;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.WriteUleb128(0ul);
|
||||
e.WriteUleb128(0x7ful);
|
||||
e.WriteUleb128(0x80ul);
|
||||
ASSERT_EQ(4u, e.size() - off);
|
||||
ASSERT_EQ("\x00\x7f\x80\x01"sv, wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteType) {
|
||||
size_t off = BUFSIZE - 1;
|
||||
WireEncoder e(0x0300u);
|
||||
for (size_t i = 0; i < off; ++i) {
|
||||
e.Write8(0u); // test across Reserve()
|
||||
}
|
||||
e.WriteType(NT_BOOLEAN);
|
||||
e.WriteType(NT_DOUBLE);
|
||||
e.WriteType(NT_STRING);
|
||||
e.WriteType(NT_RAW);
|
||||
e.WriteType(NT_BOOLEAN_ARRAY);
|
||||
e.WriteType(NT_DOUBLE_ARRAY);
|
||||
e.WriteType(NT_STRING_ARRAY);
|
||||
e.WriteType(NT_RPC);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(8u, e.size() - off);
|
||||
ASSERT_EQ("\x00\x01\x02\x03\x10\x11\x12\x20"sv,
|
||||
wpi::substr({e.data(), e.size()}, off));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteTypeError) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteType(NT_UNASSIGNED);
|
||||
EXPECT_EQ(0u, e.size());
|
||||
EXPECT_EQ(std::string("unrecognized type"), e.error());
|
||||
|
||||
e.Reset();
|
||||
e.WriteType(NT_RAW);
|
||||
EXPECT_EQ(0u, e.size());
|
||||
EXPECT_EQ(std::string("raw type not supported in protocol < 3.0"), e.error());
|
||||
|
||||
e.Reset();
|
||||
e.WriteType(NT_RPC);
|
||||
EXPECT_EQ(0u, e.size());
|
||||
EXPECT_EQ(std::string("RPC type not supported in protocol < 3.0"), e.error());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, Reset) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteType(NT_UNASSIGNED);
|
||||
EXPECT_NE(nullptr, e.error());
|
||||
e.Reset();
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
|
||||
e.Write8(0u);
|
||||
EXPECT_EQ(1u, e.size());
|
||||
e.Reset();
|
||||
EXPECT_EQ(0u, e.size());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, GetValueSize2) {
|
||||
WireEncoder e(0x0200u);
|
||||
EXPECT_EQ(0u, e.GetValueSize(*v_empty)); // empty
|
||||
EXPECT_EQ(1u, e.GetValueSize(*v_boolean));
|
||||
EXPECT_EQ(8u, e.GetValueSize(*v_double));
|
||||
EXPECT_EQ(7u, e.GetValueSize(*v_string));
|
||||
EXPECT_EQ(0u, e.GetValueSize(*v_raw)); // not supported
|
||||
|
||||
EXPECT_EQ(1u + 3u, e.GetValueSize(*v_boolean_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u, e.GetValueSize(*v_boolean_array_big));
|
||||
|
||||
EXPECT_EQ(1u + 2u * 8u, e.GetValueSize(*v_double_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u * 8u, e.GetValueSize(*v_double_array_big));
|
||||
|
||||
EXPECT_EQ(1u + 7u + 9u, e.GetValueSize(*v_string_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u * 3u, e.GetValueSize(*v_string_array_big));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteBooleanValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_boolean);
|
||||
auto v_false = Value::MakeBoolean(false);
|
||||
e.WriteValue(*v_false);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(2u, e.size());
|
||||
ASSERT_EQ("\x01\x00"sv, std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteDoubleValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_double);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(8u, e.size());
|
||||
ASSERT_EQ("\x3f\xf0\x00\x00\x00\x00\x00\x00"sv,
|
||||
std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteStringValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_string);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(7u, e.size());
|
||||
ASSERT_EQ("\x00\x05hello"sv, std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteBooleanArrayValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_boolean_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 3u, e.size());
|
||||
ASSERT_EQ("\x03\x00\x01\x00"sv, std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_boolean_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u, e.size());
|
||||
ASSERT_EQ("\xff\x00"sv, std::string_view(e.data(), 2));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteDoubleArrayValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_double_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 2u * 8u, e.size());
|
||||
ASSERT_EQ(std::string_view("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00",
|
||||
17),
|
||||
std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_double_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u * 8u, e.size());
|
||||
ASSERT_EQ("\xff\x00"sv, std::string_view(e.data(), 2));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteStringArrayValue2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_string_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 7u + 9u, e.size());
|
||||
ASSERT_EQ("\x02\x00\x05hello\x00\x07goodbye"sv,
|
||||
std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_string_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u * 3u, e.size());
|
||||
ASSERT_EQ("\xff\x00\x01"sv, std::string_view(e.data(), 3));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteValueError2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteValue(*v_empty); // empty
|
||||
ASSERT_EQ(0u, e.size());
|
||||
ASSERT_NE(nullptr, e.error());
|
||||
|
||||
e.Reset();
|
||||
e.WriteValue(*v_raw); // not supported
|
||||
ASSERT_EQ(0u, e.size());
|
||||
ASSERT_NE(nullptr, e.error());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, GetValueSize3) {
|
||||
WireEncoder e(0x0300u);
|
||||
EXPECT_EQ(0u, e.GetValueSize(*v_empty)); // empty
|
||||
EXPECT_EQ(1u, e.GetValueSize(*v_boolean));
|
||||
EXPECT_EQ(8u, e.GetValueSize(*v_double));
|
||||
EXPECT_EQ(6u, e.GetValueSize(*v_string));
|
||||
EXPECT_EQ(6u, e.GetValueSize(*v_raw));
|
||||
|
||||
EXPECT_EQ(1u + 3u, e.GetValueSize(*v_boolean_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u, e.GetValueSize(*v_boolean_array_big));
|
||||
|
||||
EXPECT_EQ(1u + 2u * 8u, e.GetValueSize(*v_double_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u * 8u, e.GetValueSize(*v_double_array_big));
|
||||
|
||||
EXPECT_EQ(1u + 6u + 8u, e.GetValueSize(*v_string_array));
|
||||
// truncated
|
||||
EXPECT_EQ(1u + 255u * 2u, e.GetValueSize(*v_string_array_big));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteBooleanValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_boolean);
|
||||
auto v_false = Value::MakeBoolean(false);
|
||||
e.WriteValue(*v_false);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(2u, e.size());
|
||||
ASSERT_EQ("\x01\x00"sv, std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteDoubleValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_double);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(8u, e.size());
|
||||
ASSERT_EQ("\x3f\xf0\x00\x00\x00\x00\x00\x00"sv,
|
||||
std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteStringValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_string);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(6u, e.size());
|
||||
ASSERT_EQ("\x05hello"sv, std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteRawValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_raw);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(6u, e.size());
|
||||
ASSERT_EQ("\x05hello"sv, std::string_view(e.data(), e.size()));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteBooleanArrayValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_boolean_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 3u, e.size());
|
||||
ASSERT_EQ("\x03\x00\x01\x00"sv, std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_boolean_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u, e.size());
|
||||
ASSERT_EQ("\xff\x00"sv, std::string_view(e.data(), 2));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteDoubleArrayValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_double_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 2u * 8u, e.size());
|
||||
ASSERT_EQ(std::string_view("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00",
|
||||
17),
|
||||
std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_double_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u * 8u, e.size());
|
||||
ASSERT_EQ("\xff\x00"sv, std::string_view(e.data(), 2));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteStringArrayValue3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_string_array);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 6u + 8u, e.size());
|
||||
ASSERT_EQ("\x02\x05hello\x07goodbye"sv, std::string_view(e.data(), e.size()));
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteValue(*v_string_array_big);
|
||||
ASSERT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(1u + 255u * 2u, e.size());
|
||||
ASSERT_EQ("\xff\x01"sv, std::string_view(e.data(), 2));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteValueError3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteValue(*v_empty); // empty
|
||||
ASSERT_EQ(0u, e.size());
|
||||
ASSERT_NE(nullptr, e.error());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, GetStringSize2) {
|
||||
// 2-byte length
|
||||
WireEncoder e(0x0200u);
|
||||
EXPECT_EQ(7u, e.GetStringSize(s_normal));
|
||||
EXPECT_EQ(130u, e.GetStringSize(s_long));
|
||||
// truncated
|
||||
EXPECT_EQ(65537u, e.GetStringSize(s_big));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteString2) {
|
||||
WireEncoder e(0x0200u);
|
||||
e.WriteString(s_normal);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
EXPECT_EQ(7u, e.size());
|
||||
EXPECT_EQ("\x00\x05hello"sv, std::string_view(e.data(), e.size()));
|
||||
|
||||
e.Reset();
|
||||
e.WriteString(s_long);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(130u, e.size());
|
||||
EXPECT_EQ("\x00\x80**"sv, std::string_view(e.data(), 4));
|
||||
EXPECT_EQ('*', e.data()[128]);
|
||||
EXPECT_EQ('x', e.data()[129]);
|
||||
|
||||
// truncated
|
||||
e.Reset();
|
||||
e.WriteString(s_big);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(65537u, e.size());
|
||||
EXPECT_EQ("\xff\xff**"sv, std::string_view(e.data(), 4));
|
||||
EXPECT_EQ('*', e.data()[65535]);
|
||||
EXPECT_EQ('x', e.data()[65536]);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, GetStringSize3) {
|
||||
// leb128-encoded length
|
||||
WireEncoder e(0x0300u);
|
||||
EXPECT_EQ(6u, e.GetStringSize(s_normal));
|
||||
EXPECT_EQ(130u, e.GetStringSize(s_long));
|
||||
EXPECT_EQ(65540u, e.GetStringSize(s_big));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTest, WriteString3) {
|
||||
WireEncoder e(0x0300u);
|
||||
e.WriteString(s_normal);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
EXPECT_EQ(6u, e.size());
|
||||
EXPECT_EQ("\x05hello"sv, std::string_view(e.data(), e.size()));
|
||||
|
||||
e.Reset();
|
||||
e.WriteString(s_long);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(130u, e.size());
|
||||
EXPECT_EQ("\x80\x01**"sv, std::string_view(e.data(), 4));
|
||||
EXPECT_EQ('*', e.data()[128]);
|
||||
EXPECT_EQ('x', e.data()[129]);
|
||||
|
||||
// NOT truncated
|
||||
e.Reset();
|
||||
e.WriteString(s_big);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(65540u, e.size());
|
||||
EXPECT_EQ("\x81\x80\x04*"sv, std::string_view(e.data(), 4));
|
||||
EXPECT_EQ('*', e.data()[65536]);
|
||||
EXPECT_EQ('x', e.data()[65537]);
|
||||
EXPECT_EQ('x', e.data()[65538]);
|
||||
EXPECT_EQ('x', e.data()[65539]);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -19,3 +19,15 @@ int main(int argc, char** argv) {
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void __ubsan_on_report(void) {
|
||||
FAIL() << "Encountered an undefined behavior sanitizer error";
|
||||
}
|
||||
void __asan_on_error(void) {
|
||||
FAIL() << "Encountered an address sanitizer error";
|
||||
}
|
||||
void __tsan_on_report(void) {
|
||||
FAIL() << "Encountered a thread sanitizer error";
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
86
ntcore/src/test/native/cpp/net/MockNetworkInterface.h
Normal file
86
ntcore/src/test/native/cpp/net/MockNetworkInterface.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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 <string>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "PubSubOptions.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "net/NetworkInterface.h"
|
||||
|
||||
namespace nt::net {
|
||||
|
||||
class MockLocalInterface : public LocalInterface {
|
||||
public:
|
||||
MOCK_METHOD(NT_Topic, NetworkAnnounce,
|
||||
(std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, NT_Publisher pubHandle),
|
||||
(override));
|
||||
MOCK_METHOD(void, NetworkUnannounce, (std::string_view name), (override));
|
||||
MOCK_METHOD(void, NetworkPropertiesUpdate,
|
||||
(std::string_view name, const wpi::json& update, bool ack),
|
||||
(override));
|
||||
MOCK_METHOD(void, NetworkSetValue, (NT_Topic topicHandle, const Value& value),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockNetworkStartupInterface : public NetworkStartupInterface {
|
||||
public:
|
||||
MOCK_METHOD(void, Publish,
|
||||
(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Subscribe,
|
||||
(NT_Subscriber subHandle, wpi::span<const std::string> prefixes,
|
||||
const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetValue, (NT_Publisher pubHandle, const Value& value),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockNetworkInterface : public NetworkInterface {
|
||||
public:
|
||||
MOCK_METHOD(void, Publish,
|
||||
(NT_Publisher pubHandle, NT_Topic topicHandle,
|
||||
std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Unpublish, (NT_Publisher pubHandle, NT_Topic topicHandle),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetProperties,
|
||||
(NT_Topic topicHandle, std::string_view name,
|
||||
const wpi::json& update),
|
||||
(override));
|
||||
MOCK_METHOD(void, Subscribe,
|
||||
(NT_Subscriber subHandle, wpi::span<const std::string> prefixes,
|
||||
const PubSubOptions& options),
|
||||
(override));
|
||||
MOCK_METHOD(void, Unsubscribe, (NT_Subscriber subHandle), (override));
|
||||
MOCK_METHOD(void, SetValue, (NT_Publisher pubHandle, const Value& value),
|
||||
(override));
|
||||
};
|
||||
|
||||
class MockLocalStorage : public ILocalStorage {
|
||||
public:
|
||||
MOCK_METHOD(NT_Topic, NetworkAnnounce,
|
||||
(std::string_view name, std::string_view typeStr,
|
||||
const wpi::json& properties, NT_Publisher pubHandle),
|
||||
(override));
|
||||
MOCK_METHOD(void, NetworkUnannounce, (std::string_view name), (override));
|
||||
MOCK_METHOD(void, NetworkPropertiesUpdate,
|
||||
(std::string_view name, const wpi::json& update, bool ack),
|
||||
(override));
|
||||
MOCK_METHOD(void, NetworkSetValue, (NT_Topic topicHandle, const Value& value),
|
||||
(override));
|
||||
MOCK_METHOD(void, StartNetwork, (NetworkStartupInterface & startup),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetNetwork, (NetworkInterface * network), (override));
|
||||
MOCK_METHOD(void, ClearNetwork, (), (override));
|
||||
};
|
||||
|
||||
} // namespace nt::net
|
||||
26
ntcore/src/test/native/cpp/net/MockWireConnection.cpp
Normal file
26
ntcore/src/test/native/cpp/net/MockWireConnection.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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 "MockWireConnection.h"
|
||||
|
||||
using namespace nt::net;
|
||||
|
||||
void MockWireConnection::StartSendText() {
|
||||
if (m_in_text) {
|
||||
m_text_os << ',';
|
||||
} else {
|
||||
m_text_os << '[';
|
||||
m_in_text = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MockWireConnection::FinishSendText() {
|
||||
if (m_in_text) {
|
||||
m_text_os << ']';
|
||||
m_in_text = false;
|
||||
}
|
||||
m_text_os.flush();
|
||||
Text(m_text);
|
||||
m_text.clear();
|
||||
}
|
||||
54
ntcore/src/test/native/cpp/net/MockWireConnection.h
Normal file
54
ntcore/src/test/native/cpp/net/MockWireConnection.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/span.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "net/WireConnection.h"
|
||||
|
||||
namespace nt::net {
|
||||
|
||||
class MockWireConnection : public WireConnection {
|
||||
public:
|
||||
MockWireConnection() : m_text_os{m_text}, m_binary_os{m_binary} {}
|
||||
|
||||
MOCK_METHOD(bool, Ready, (), (const, override));
|
||||
|
||||
TextWriter SendText() override { return {m_text_os, *this}; }
|
||||
BinaryWriter SendBinary() override { return {m_binary_os, *this}; }
|
||||
|
||||
MOCK_METHOD(void, Text, (std::string_view contents));
|
||||
MOCK_METHOD(void, Binary, (wpi::span<const uint8_t> contents));
|
||||
|
||||
MOCK_METHOD(void, Flush, (), (override));
|
||||
|
||||
MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
|
||||
|
||||
protected:
|
||||
void StartSendText() override;
|
||||
void FinishSendText() override;
|
||||
void StartSendBinary() override {}
|
||||
void FinishSendBinary() override {
|
||||
Binary(m_binary);
|
||||
m_binary.resize(0);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
wpi::raw_string_ostream m_text_os;
|
||||
std::vector<uint8_t> m_binary;
|
||||
wpi::raw_uvector_ostream m_binary_os;
|
||||
bool m_in_text{false};
|
||||
};
|
||||
|
||||
} // namespace nt::net
|
||||
214
ntcore/src/test/native/cpp/net/WireDecoderTest.cpp
Normal file
214
ntcore/src/test/native/cpp/net/WireDecoderTest.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// 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 <wpi/SmallString.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "../MockLogger.h"
|
||||
#include "../TestPrinters.h"
|
||||
#include "Handle.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net/Message.h"
|
||||
#include "net/WireDecoder.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
using testing::_;
|
||||
using testing::MockFunction;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockClientMessageHandler : public net::ClientMessageHandler {
|
||||
public:
|
||||
MOCK_METHOD4(ClientPublish,
|
||||
void(int64_t pubuid, std::string_view name,
|
||||
std::string_view typeStr, const wpi::json& properties));
|
||||
MOCK_METHOD1(ClientUnpublish, void(int64_t pubuid));
|
||||
MOCK_METHOD2(ClientSetProperties,
|
||||
void(std::string_view name, const wpi::json& update));
|
||||
MOCK_METHOD3(ClientSubscribe,
|
||||
void(int64_t subuid, wpi::span<const std::string> prefixes,
|
||||
const PubSubOptions& options));
|
||||
MOCK_METHOD1(ClientUnsubscribe, void(int64_t subuid));
|
||||
};
|
||||
|
||||
class MockServerMessageHandler : public net::ServerMessageHandler {
|
||||
public:
|
||||
MOCK_METHOD5(ServerAnnounce,
|
||||
void(std::string_view name, int64_t id, std::string_view typeStr,
|
||||
const wpi::json& properties,
|
||||
std::optional<int64_t> pubuid));
|
||||
MOCK_METHOD2(ServerUnannounce, void(std::string_view name, int64_t id));
|
||||
MOCK_METHOD3(ServerPropertiesUpdate,
|
||||
void(std::string_view name, const wpi::json& update, bool ack));
|
||||
};
|
||||
|
||||
class WireDecodeTextClientTest : public ::testing::Test {
|
||||
public:
|
||||
StrictMock<MockClientMessageHandler> handler;
|
||||
StrictMock<wpi::MockLogger> logger;
|
||||
};
|
||||
|
||||
class WireDecodeTextServerTest : public ::testing::Test {
|
||||
public:
|
||||
StrictMock<MockServerMessageHandler> handler;
|
||||
StrictMock<wpi::MockLogger> logger;
|
||||
};
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, EmptyArray) {
|
||||
net::WireDecodeText("[]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorEmpty) {
|
||||
EXPECT_CALL(
|
||||
logger,
|
||||
Call(_, _, _,
|
||||
"could not decode JSON message: [json.exception.parse_error.101] "
|
||||
"parse error at 1: syntax error - "
|
||||
"unexpected end of input; expected '[', '{', or a literal"sv));
|
||||
net::WireDecodeText("", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorBadJson1) {
|
||||
EXPECT_CALL(
|
||||
logger,
|
||||
Call(_, _, _,
|
||||
"could not decode JSON message: [json.exception.parse_error.101] "
|
||||
"parse error at 2: syntax error - "
|
||||
"unexpected end of input; expected '[', '{', or a literal"sv));
|
||||
net::WireDecodeText("[", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorBadJson2) {
|
||||
EXPECT_CALL(
|
||||
logger,
|
||||
Call(_, _, _,
|
||||
"could not decode JSON message: [json.exception.parse_error.101] "
|
||||
"parse error at 3: syntax error - "
|
||||
"unexpected end of input; expected string literal"sv));
|
||||
net::WireDecodeText("[{", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorNotArray) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "expected JSON array at top level"sv));
|
||||
net::WireDecodeText("{}", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorMessageNotObject) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: expected message to be an object"sv));
|
||||
net::WireDecodeText("[5]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorNoMethodKey) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no method key"sv));
|
||||
net::WireDecodeText("[{}]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorMethodNotString) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: method must be a string"sv));
|
||||
net::WireDecodeText("[{\"method\":5}]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorNoParamsKey) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no params key"sv));
|
||||
net::WireDecodeText("[{\"method\":\"a\"}]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorParamsNotObject) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: params must be an object"sv));
|
||||
net::WireDecodeText("[{\"method\":\"a\",\"params\":5}]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, ErrorUnknownMethod) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: unrecognized method 'a'"sv));
|
||||
net::WireDecodeText("[{\"method\":\"a\",\"params\":{}}]", handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, PublishPropsEmpty) {
|
||||
EXPECT_CALL(handler,
|
||||
ClientPublish(5, std::string_view{"test"},
|
||||
std::string_view{"double"}, wpi::json::object()));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
|
||||
EXPECT_CALL(handler,
|
||||
ClientPublish(5, std::string_view{"test"},
|
||||
std::string_view{"double"}, wpi::json::object()));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"pubuid\":5,\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, PublishProps) {
|
||||
wpi::json props = {{"k", 6}};
|
||||
EXPECT_CALL(handler, ClientPublish(5, std::string_view{"test"},
|
||||
std::string_view{"double"}, props));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":{\"k\":6},"
|
||||
"\"pubuid\":5,\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, PublishPropsError) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: properties must be an object"sv));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":[\"k\"],"
|
||||
"\"pubuid\":5,\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, PublishError) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no name key"sv));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"pubuid\":5,\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no type key"sv));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"pubuid\":5}}]",
|
||||
handler, logger);
|
||||
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no pubuid key"sv));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"type\":\"double\"}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, Unpublish) {
|
||||
EXPECT_CALL(handler, ClientUnpublish(5));
|
||||
net::WireDecodeText("[{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, UnpublishMultiple) {
|
||||
EXPECT_CALL(handler, ClientUnpublish(5));
|
||||
EXPECT_CALL(handler, ClientUnpublish(6));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}},{\"method\":"
|
||||
"\"unpublish\",\"params\":{\"pubuid\":6}}]",
|
||||
handler, logger);
|
||||
}
|
||||
|
||||
TEST_F(WireDecodeTextClientTest, UnpublishError) {
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: no pubuid key"sv));
|
||||
net::WireDecodeText("[{\"method\":\"unpublish\",\"params\":{}}]", handler,
|
||||
logger);
|
||||
|
||||
EXPECT_CALL(logger, Call(_, _, _, "0: pubuid must be a number"sv));
|
||||
net::WireDecodeText(
|
||||
"[{\"method\":\"unpublish\",\"params\":{\"pubuid\":\"5\"}}]", handler,
|
||||
logger);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
292
ntcore/src/test/native/cpp/net/WireEncoderTest.cpp
Normal file
292
ntcore/src/test/native/cpp/net/WireEncoderTest.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
// 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 <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "../SpanMatcher.h"
|
||||
#include "../TestPrinters.h"
|
||||
#include "Handle.h"
|
||||
#include "PubSubOptions.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net/Message.h"
|
||||
#include "net/WireEncoder.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireEncoderTextTest : public ::testing::Test {
|
||||
protected:
|
||||
std::string out;
|
||||
wpi::raw_string_ostream os{out};
|
||||
wpi::json GetJson() { return wpi::json::parse(os.str()); }
|
||||
};
|
||||
|
||||
class WireEncoderBinaryTest : public ::testing::Test {
|
||||
protected:
|
||||
std::vector<uint8_t> out;
|
||||
wpi::raw_uvector_ostream os{out};
|
||||
};
|
||||
|
||||
TEST_F(WireEncoderTextTest, PublishPropsEmpty) {
|
||||
net::WireEncodePublish(os, 5, "test", "double", wpi::json::object());
|
||||
ASSERT_EQ(
|
||||
os.str(),
|
||||
"{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":{},\"pubuid\":5,\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, PublishProps) {
|
||||
net::WireEncodePublish(os, 5, "test", "double", {{"k", 6}});
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":{\"k\":6},"
|
||||
"\"pubuid\":5,\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, Unpublish) {
|
||||
net::WireEncodeUnpublish(os, 5);
|
||||
ASSERT_EQ(os.str(), "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SetProperties) {
|
||||
net::WireEncodeSetProperties(os, "test", {{"k", 6}});
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"setproperties\",\"params\":{"
|
||||
"\"name\":\"test\",\"update\":{\"k\":6}}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, Subscribe) {
|
||||
net::WireEncodeSubscribe(os, 5, std::vector<std::string_view>{{"a", "b"}},
|
||||
PubSubOptions{});
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribeSendAll) {
|
||||
PubSubOptions options;
|
||||
options.sendAll = true;
|
||||
net::WireEncodeSubscribe(os, 5, std::vector<std::string_view>{{"a", "b"}},
|
||||
options);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{\"all\":true},\"topics\":[\"a\",\"b\"],"
|
||||
"\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribePeriodic) {
|
||||
PubSubOptions options;
|
||||
options.periodic = 0.5;
|
||||
net::WireEncodeSubscribe(os, 5, std::vector<std::string_view>{{"a", "b"}},
|
||||
options);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{\"periodic\":0.5},\"topics\":[\"a\",\"b\"],"
|
||||
"\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, SubscribeAllOptions) {
|
||||
PubSubOptions options;
|
||||
options.sendAll = true;
|
||||
options.periodic = 0.5;
|
||||
net::WireEncodeSubscribe(os, 5, std::vector<std::string_view>{{"a", "b"}},
|
||||
options);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{\"all\":true,\"periodic\":0.5},"
|
||||
"\"topics\":[\"a\",\"b\"],\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, Unsubscribe) {
|
||||
net::WireEncodeUnsubscribe(os, 5);
|
||||
ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, Announce) {
|
||||
net::WireEncodeAnnounce(os, "test", 5, "double", wpi::json::object(),
|
||||
std::nullopt);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{},\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, AnnounceProperties) {
|
||||
net::WireEncodeAnnounce(os, "test", 5, "double", {{"k", 6}}, std::nullopt);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{\"k\":6},\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, AnnouncePubuid) {
|
||||
net::WireEncodeAnnounce(os, "test", 5, "double", wpi::json::object(), 6);
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, Unannounce) {
|
||||
net::WireEncodeUnannounce(os, "test", 5);
|
||||
ASSERT_EQ(
|
||||
os.str(),
|
||||
"{\"method\":\"unannounce\",\"params\":{\"id\":5,\"name\":\"test\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessagePublish) {
|
||||
net::ClientMessage msg{net::PublishMsg{
|
||||
Handle{0, 5, Handle::kPublisher}, 0, "test", "double", {{"k", 6}}, {}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"publish\",\"params\":{"
|
||||
"\"name\":\"test\",\"properties\":{\"k\":6},"
|
||||
"\"pubuid\":5,\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageUnpublish) {
|
||||
net::ClientMessage msg{
|
||||
net::UnpublishMsg{Handle{0, 5, Handle::kPublisher}, 0}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(), "{\"method\":\"unpublish\",\"params\":{\"pubuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageSetProperties) {
|
||||
net::ClientMessage msg{net::SetPropertiesMsg{0, "test", {{"k", 6}}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"setproperties\",\"params\":{"
|
||||
"\"name\":\"test\",\"update\":{\"k\":6}}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageSubscribe) {
|
||||
net::ClientMessage msg{
|
||||
net::SubscribeMsg{Handle{0, 5, Handle::kSubscriber}, {"a", "b"}, {}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"subscribe\",\"params\":{"
|
||||
"\"options\":{},\"topics\":[\"a\",\"b\"],\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageUnsubscribe) {
|
||||
net::ClientMessage msg{
|
||||
net::UnsubscribeMsg{Handle{0, 5, Handle::kSubscriber}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(), "{\"method\":\"unsubscribe\",\"params\":{\"subuid\":5}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageAnnounce) {
|
||||
net::ServerMessage msg{
|
||||
net::AnnounceMsg{"test", 5, "double", std::nullopt, wpi::json::object()}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{},\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageAnnounceProperties) {
|
||||
net::ServerMessage msg{
|
||||
net::AnnounceMsg{"test", 5, "double", std::nullopt, {{"k", 6}}}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{\"k\":6},\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageAnnouncePubuid) {
|
||||
net::ServerMessage msg{
|
||||
net::AnnounceMsg{"test", 5, "double", 6, wpi::json::object()}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(os.str(),
|
||||
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
|
||||
"\"properties\":{},\"pubuid\":6,\"type\":\"double\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, MessageUnannounce) {
|
||||
net::ServerMessage msg{net::UnannounceMsg{"test", 5}};
|
||||
ASSERT_TRUE(net::WireEncodeText(os, msg));
|
||||
ASSERT_EQ(
|
||||
os.str(),
|
||||
"{\"method\":\"unannounce\",\"params\":{\"id\":5,\"name\":\"test\"}}");
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, ServerMessageEmpty) {
|
||||
ASSERT_FALSE(net::WireEncodeText(os, net::ServerMessage{}));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderTextTest, ServerMessageValue) {
|
||||
net::ServerMessage msg{net::ServerValueMsg{}};
|
||||
ASSERT_FALSE(net::WireEncodeText(os, msg));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, Boolean) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeBoolean(true));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x00\xc3"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, Integer) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeInteger(7));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x02\x07"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, Float) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeFloat(2.5));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x03\xca\x40\x20\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, Double) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeDouble(2.5));
|
||||
ASSERT_THAT(
|
||||
out,
|
||||
wpi::SpanEq("\x94\x05\x06\x01\xcb\x40\x04\x00\x00\x00\x00\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, String) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeString("hello"));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x04\xa5hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, Raw) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeRaw("hello"_us));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x05\xc4\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, BooleanArray) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeBooleanArray({true, false, true}));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x10\x93\xc3\xc2\xc3"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, IntegerArray) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeIntegerArray({1, 2, 4}));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x12\x93\x01\x02\x04"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, FloatArray) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeFloatArray({1, 2, 3}));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\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) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeDoubleArray({1, 2, 3}));
|
||||
ASSERT_THAT(out, wpi::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));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoderBinaryTest, StringArray) {
|
||||
net::WireEncodeBinary(os, 5, 6, Value::MakeStringArray({"hello", "bye"}));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x94\x05\x06\x14\x92\xa5hello\xa3"
|
||||
"bye"_us));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -2,37 +2,31 @@
|
||||
// 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 "MessageMatcher.h"
|
||||
#include "MessageMatcher3.h"
|
||||
|
||||
namespace nt {
|
||||
namespace nt::net3 {
|
||||
|
||||
bool MessageMatcher::MatchAndExplain(
|
||||
std::shared_ptr<Message> msg,
|
||||
::testing::MatchResultListener* listener) const {
|
||||
Message3 msg, ::testing::MatchResultListener* listener) const {
|
||||
bool match = true;
|
||||
if (!msg) {
|
||||
return false;
|
||||
}
|
||||
if (msg->str() != goodmsg->str()) {
|
||||
if (msg.str() != goodmsg.str()) {
|
||||
*listener << "str mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if ((!msg->value() && goodmsg->value()) ||
|
||||
(msg->value() && !goodmsg->value()) ||
|
||||
(msg->value() && goodmsg->value() &&
|
||||
*msg->value() != *goodmsg->value())) {
|
||||
if ((!msg.value() && goodmsg.value()) || (msg.value() && !goodmsg.value()) ||
|
||||
(msg.value() && goodmsg.value() && msg.value() != goodmsg.value())) {
|
||||
*listener << "value mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (msg->id() != goodmsg->id()) {
|
||||
if (msg.id() != goodmsg.id()) {
|
||||
*listener << "id mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (msg->flags() != goodmsg->flags()) {
|
||||
if (msg.flags() != goodmsg.flags()) {
|
||||
*listener << "flags mismatch";
|
||||
match = false;
|
||||
}
|
||||
if (msg->seq_num_uid() != goodmsg->seq_num_uid()) {
|
||||
if (msg.seq_num_uid() != goodmsg.seq_num_uid()) {
|
||||
*listener << "seq_num_uid mismatch";
|
||||
match = false;
|
||||
}
|
||||
@@ -48,4 +42,4 @@ void MessageMatcher::DescribeNegationTo(::std::ostream* os) const {
|
||||
PrintTo(goodmsg, os);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
} // namespace nt::net3
|
||||
34
ntcore/src/test/native/cpp/net3/MessageMatcher3.h
Normal file
34
ntcore/src/test/native/cpp/net3/MessageMatcher3.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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 <memory>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
#include "../TestPrinters.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "net3/Message3.h"
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
class MessageMatcher : public ::testing::MatcherInterface<Message3> {
|
||||
public:
|
||||
explicit MessageMatcher(Message3 goodmsg_) : goodmsg(std::move(goodmsg_)) {}
|
||||
|
||||
bool MatchAndExplain(Message3 msg,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
Message3 goodmsg;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<Message3> MessageEq(Message3 goodmsg) {
|
||||
return ::testing::MakeMatcher(new MessageMatcher(std::move(goodmsg)));
|
||||
}
|
||||
|
||||
} // namespace nt::net3
|
||||
44
ntcore/src/test/native/cpp/net3/MockWireConnection3.h
Normal file
44
ntcore/src/test/native/cpp/net3/MockWireConnection3.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/span.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "net3/WireConnection3.h"
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
class MockWireConnection3 : public WireConnection3 {
|
||||
public:
|
||||
MockWireConnection3() : m_os{m_data} {}
|
||||
|
||||
MOCK_METHOD(bool, Ready, (), (const, override));
|
||||
|
||||
Writer Send() override { return {m_os, *this}; }
|
||||
|
||||
MOCK_METHOD(void, Data, (wpi::span<const uint8_t> data));
|
||||
|
||||
MOCK_METHOD(void, Flush, (), (override));
|
||||
|
||||
MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
|
||||
|
||||
protected:
|
||||
void FinishSend() override {
|
||||
Data(m_data);
|
||||
m_data.resize(0);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> m_data;
|
||||
wpi::raw_uvector_ostream m_os;
|
||||
};
|
||||
|
||||
} // namespace nt::net3
|
||||
276
ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp
Normal file
276
ntcore/src/test/native/cpp/net3/WireDecoder3Test.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "../SpanMatcher.h"
|
||||
#include "../TestPrinters.h"
|
||||
#include "../ValueMatcher.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net3/WireDecoder3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
using testing::_;
|
||||
using testing::MockFunction;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockMessageHandler3 : public net3::MessageHandler3 {
|
||||
public:
|
||||
MOCK_METHOD0(KeepAlive, void());
|
||||
MOCK_METHOD0(ServerHelloDone, void());
|
||||
MOCK_METHOD0(ClientHelloDone, void());
|
||||
MOCK_METHOD0(ClearEntries, void());
|
||||
MOCK_METHOD1(ProtoUnsup, void(unsigned int proto_rev));
|
||||
MOCK_METHOD2(ClientHello,
|
||||
void(std::string_view self_id, unsigned int proto_rev));
|
||||
MOCK_METHOD2(ServerHello, void(unsigned int flags, std::string_view self_id));
|
||||
MOCK_METHOD5(EntryAssign, void(std::string_view name, unsigned int id,
|
||||
unsigned int seq_num, const Value& value,
|
||||
unsigned int flags));
|
||||
MOCK_METHOD3(EntryUpdate,
|
||||
void(unsigned int id, unsigned int seq_num, const Value& value));
|
||||
MOCK_METHOD2(FlagsUpdate, void(unsigned int id, unsigned int flags));
|
||||
MOCK_METHOD1(EntryDelete, void(unsigned int id));
|
||||
MOCK_METHOD3(ExecuteRpc, void(unsigned int id, unsigned int uid,
|
||||
wpi::span<const uint8_t> params));
|
||||
MOCK_METHOD3(RpcResponse, void(unsigned int id, unsigned int uid,
|
||||
wpi::span<const uint8_t> result));
|
||||
};
|
||||
|
||||
class WireDecoder3Test : public ::testing::Test {
|
||||
protected:
|
||||
StrictMock<MockMessageHandler3> handler;
|
||||
net3::WireDecoder3 decoder{handler};
|
||||
|
||||
void DecodeComplete(wpi::span<const uint8_t> in) {
|
||||
decoder.Execute(&in);
|
||||
EXPECT_TRUE(in.empty());
|
||||
ASSERT_EQ(decoder.GetError(), "");
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(WireDecoder3Test, KeepAlive) {
|
||||
EXPECT_CALL(handler, KeepAlive());
|
||||
DecodeComplete("\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClientHello) {
|
||||
EXPECT_CALL(handler, ClientHello(std::string_view{"hello"}, 0x0300u));
|
||||
DecodeComplete("\x01\x03\x00\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ProtoUnsup) {
|
||||
EXPECT_CALL(handler, ProtoUnsup(0x0300u));
|
||||
EXPECT_CALL(handler, ProtoUnsup(0x0200u));
|
||||
DecodeComplete("\x02\x03\x00\x02\x02\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ServerHelloDone) {
|
||||
EXPECT_CALL(handler, ServerHelloDone());
|
||||
DecodeComplete("\x03"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ServerHello) {
|
||||
EXPECT_CALL(handler, ServerHello(0x03, std::string_view{"hello"}));
|
||||
DecodeComplete("\x04\x03\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClientHelloDone) {
|
||||
EXPECT_CALL(handler, ClientHelloDone());
|
||||
DecodeComplete("\x05"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, FlagsUpdate) {
|
||||
EXPECT_CALL(handler, FlagsUpdate(0x5678, 0x03));
|
||||
DecodeComplete("\x12\x56\x78\x03"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryDelete) {
|
||||
EXPECT_CALL(handler, EntryDelete(0x5678));
|
||||
DecodeComplete("\x13\x56\x78"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClearEntries) {
|
||||
EXPECT_CALL(handler, ClearEntries());
|
||||
DecodeComplete("\x14\xd0\x6c\xb2\x7a"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClearEntriesInvalid) {
|
||||
auto in = "\x14\xd0\x6c\xb2\x7b"_us;
|
||||
decoder.Execute(&in);
|
||||
EXPECT_EQ(decoder.GetError(), "received incorrect CLEAR_ENTRIES magic value");
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ExecuteRpc) {
|
||||
EXPECT_CALL(handler, ExecuteRpc(0x5678, 0x1234, wpi::SpanEq("hello"_us)));
|
||||
DecodeComplete("\x20\x56\x78\x12\x34\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, RpcResponse) {
|
||||
EXPECT_CALL(handler, RpcResponse(0x5678, 0x1234, wpi::SpanEq("hello"_us)));
|
||||
DecodeComplete("\x21\x56\x78\x12\x34\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, UnknownMessage) {
|
||||
auto in = "\x23"_us;
|
||||
decoder.Execute(&in);
|
||||
EXPECT_EQ(decoder.GetError(), "unrecognized message type: 35");
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryAssignBoolean) {
|
||||
EXPECT_CALL(handler, EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeBoolean(true), 0x9a));
|
||||
DecodeComplete("\x10\x04test\x00\x56\x78\x12\x34\x9a\x01"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryAssignDouble) {
|
||||
EXPECT_CALL(handler, EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5), 0x9a));
|
||||
DecodeComplete(
|
||||
"\x10\x04test\x01\x56\x78\x12\x34"
|
||||
"\x9a\x41\x0c\x13\x80\x00\x00\x00\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBoolean) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeBoolean(true)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x00\x01"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDouble) {
|
||||
// values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(0.0)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x00\x00\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(2.3e5)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x41\x0c\x13\x80\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(
|
||||
handler,
|
||||
EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(std::numeric_limits<double>::infinity())));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x7f\xf0\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(DBL_MIN)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x00\x10\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(DBL_MAX)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x7f\xef\xff\xff\xff\xff\xff\xff"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateString) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString("hello"sv)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x02\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateString2) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x7f};
|
||||
in.insert(in.end(), 127, '*');
|
||||
std::string out(127, '*');
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x80, 0x01};
|
||||
in.insert(in.end(), 127, '*');
|
||||
in.push_back('x');
|
||||
|
||||
std::string out(127, '*');
|
||||
out.push_back('x');
|
||||
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringHuge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x81, 0x80, 0x04};
|
||||
in.insert(in.end(), 65534, '*');
|
||||
in.insert(in.end(), 3, 'x');
|
||||
|
||||
std::string out(65534, '*');
|
||||
out.append(3, 'x');
|
||||
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateRaw) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeRaw("hello"_us)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x03\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBooleanArray) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray({false, true, false})));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x10\x03\x00\x01\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBooleanArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
in.insert(in.end(), 255, 0);
|
||||
std::vector<int> out(255, 0);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDoubleArray) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeDoubleArray({0.5, 0.25})));
|
||||
DecodeComplete(
|
||||
"\x11\x56\x78\x12\x34\x11\x02"
|
||||
"\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDoubleArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
in.insert(in.end(), 255 * 8, 0);
|
||||
std::vector<double> out(255, 0.0);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringArray) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray({"hello", "bye"})));
|
||||
DecodeComplete(
|
||||
"\x11\x56\x78\x12\x34\x12\x02\x05hello\x03"
|
||||
"bye"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
in.insert(in.end(), 255, 0);
|
||||
std::vector<std::string> out(255, "");
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateRpc) {
|
||||
// RPC values are decoded as raw
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeRaw("hello"_us)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x20\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateTypeError) {
|
||||
auto in = "\x11\x56\x78\x12\x34\x30\x11"_us;
|
||||
decoder.Execute(&in);
|
||||
ASSERT_EQ(decoder.GetError(), "unrecognized value type");
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
283
ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp
Normal file
283
ntcore/src/test/native/cpp/net3/WireEncoder3Test.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
// 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 <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "../SpanMatcher.h"
|
||||
#include "../TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "net3/Message3.h"
|
||||
#include "net3/WireEncoder3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireEncoder3Test : public ::testing::Test {
|
||||
protected:
|
||||
std::vector<uint8_t> out;
|
||||
wpi::raw_uvector_ostream os{out};
|
||||
};
|
||||
|
||||
TEST_F(WireEncoder3Test, Unknown) {
|
||||
net3::WireEncode(os, net3::Message3{});
|
||||
ASSERT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, KeepAlive) {
|
||||
net3::WireEncode(os, net3::Message3::KeepAlive());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClientHello) {
|
||||
net3::WireEncode(os, net3::Message3::ClientHello("hello"));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x01\x03\x00\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ProtoUnsup) {
|
||||
net3::WireEncode(os, net3::Message3::ProtoUnsup());
|
||||
net3::WireEncode(os, net3::Message3::ProtoUnsup(0x0200u));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x02\x03\x00\x02\x02\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ServerHelloDone) {
|
||||
net3::WireEncode(os, net3::Message3::ServerHelloDone());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x03"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ServerHello) {
|
||||
net3::WireEncode(os, net3::Message3::ServerHello(0x03, "hello"));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x04\x03\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClientHelloDone) {
|
||||
net3::WireEncode(os, net3::Message3::ClientHelloDone());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x05"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, FlagsUpdate) {
|
||||
net3::WireEncode(os, net3::Message3::FlagsUpdate(0x5678, 0x03));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x12\x56\x78\x03"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryDelete) {
|
||||
net3::WireEncode(os, net3::Message3::EntryDelete(0x5678));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x13\x56\x78"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClearEntries) {
|
||||
net3::WireEncode(os, net3::Message3::ClearEntries());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x14\xd0\x6c\xb2\x7a"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ExecuteRpc) {
|
||||
net3::WireEncode(os, net3::Message3::ExecuteRpc(0x5678, 0x1234, "hello"_us));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x20\x56\x78\x12\x34\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, RpcResponse) {
|
||||
net3::WireEncode(os, net3::Message3::RpcResponse(0x5678, 0x1234, "hello"_us));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x21\x56\x78\x12\x34\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryAssignBoolean) {
|
||||
net3::WireEncode(os,
|
||||
net3::Message3::EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeBoolean(true), 0x9a));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x10\x04test\x00\x56\x78\x12\x34\x9a\x01"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryAssignDouble) {
|
||||
net3::WireEncode(os,
|
||||
net3::Message3::EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5), 0x9a));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x10\x04test\x01\x56\x78\x12\x34"
|
||||
"\x9a\x41\x0c\x13\x80\x00\x00\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBoolean) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBoolean(true)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x00\x01"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDouble) {
|
||||
// values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234, Value::MakeDouble(0.0)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x00\x00\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x41\x0c\x13\x80\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234,
|
||||
Value::MakeDouble(std::numeric_limits<double>::infinity())));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x7f\xf0\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(DBL_MIN)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x00\x10\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(DBL_MAX)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x7f\xef\xff\xff\xff\xff\xff\xff"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateString) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString("hello"sv)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x02\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateString2) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x7f};
|
||||
ex.insert(ex.end(), 127, '*');
|
||||
std::string in(127, '*');
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x80, 0x01};
|
||||
ex.insert(ex.end(), 127, '*');
|
||||
ex.push_back('x');
|
||||
|
||||
std::string in(127, '*');
|
||||
in.push_back('x');
|
||||
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringHuge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x81, 0x80, 0x04};
|
||||
ex.insert(ex.end(), 65534, '*');
|
||||
ex.insert(ex.end(), 3, 'x');
|
||||
|
||||
std::string in(65534, '*');
|
||||
in.append(3, 'x');
|
||||
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateRaw) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeRaw("hello"_us)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x03\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeBooleanArray({false, true, false})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x10\x03\x00\x01\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<int> in(255, 0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<int> in(256, 0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray({0.5, 0.25})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x11\x02"
|
||||
"\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
ex.insert(ex.end(), 255 * 8, 0);
|
||||
std::vector<double> in(255, 0.0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
ex.insert(ex.end(), 255 * 8, 0);
|
||||
std::vector<double> in(256, 0.0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeStringArray({"hello", "bye"})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x12\x02\x05hello\x03"
|
||||
"bye"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<std::string> in(255, "");
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<std::string> in(256, "");
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
Reference in New Issue
Block a user