mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Prepare ntcore for merge into allwpilib.
This commit is contained in:
108
ntcore/src/test/native/cpp/ConnectionListenerTest.cpp
Normal file
108
ntcore/src/test/native/cpp/ConnectionListenerTest.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
class ConnectionListenerTest : public ::testing::Test {
|
||||
public:
|
||||
ConnectionListenerTest()
|
||||
: server_inst(nt::CreateInstance()), client_inst(nt::CreateInstance()) {
|
||||
nt::SetNetworkIdentity(server_inst, "server");
|
||||
nt::SetNetworkIdentity(client_inst, "client");
|
||||
}
|
||||
|
||||
~ConnectionListenerTest() override {
|
||||
nt::DestroyInstance(server_inst);
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect();
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void ConnectionListenerTest::Connect() {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", "127.0.0.1",
|
||||
10000);
|
||||
nt::StartClient(client_inst, "127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
while ((nt::GetNetworkMode(client_inst) & NT_NET_MODE_STARTING) != 0)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
TEST_F(ConnectionListenerTest, Polled) {
|
||||
// set up the poller
|
||||
NT_ConnectionListenerPoller poller =
|
||||
nt::CreateConnectionListenerPoller(server_inst);
|
||||
ASSERT_NE(poller, 0u);
|
||||
NT_ConnectionListener handle = nt::AddPolledConnectionListener(poller, false);
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
Connect();
|
||||
|
||||
// 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_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_TRUE(result[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
nt::StopClient(client_inst);
|
||||
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_EQ(result.size(), 1u);
|
||||
EXPECT_EQ(handle, result[0].listener);
|
||||
EXPECT_FALSE(result[0].connected);
|
||||
|
||||
// trigger a disconnect event
|
||||
}
|
||||
|
||||
TEST_F(ConnectionListenerTest, Threaded) {
|
||||
std::vector<nt::ConnectionNotification> result;
|
||||
auto handle = nt::AddConnectionListener(
|
||||
server_inst,
|
||||
[&](const nt::ConnectionNotification& event) { result.push_back(event); },
|
||||
false);
|
||||
|
||||
// trigger a connect event
|
||||
Connect();
|
||||
|
||||
ASSERT_TRUE(nt::WaitForConnectionListenerQueue(server_inst, 1.0));
|
||||
|
||||
// get the event
|
||||
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);
|
||||
}
|
||||
164
ntcore/src/test/native/cpp/EntryListenerTest.cpp
Normal file
164
ntcore/src/test/native/cpp/EntryListenerTest.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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();
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void EntryListenerTest::Connect() {
|
||||
nt::StartServer(server_inst, "entrylistenertest.ini", "127.0.0.1", 10000);
|
||||
nt::StartClient(client_inst, "127.0.0.1", 10000);
|
||||
|
||||
// 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, EntryNewRemote) {
|
||||
Connect();
|
||||
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, PrefixNewRemote) {
|
||||
Connect();
|
||||
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);
|
||||
}
|
||||
314
ntcore/src/test/native/cpp/EntryNotifierTest.cpp
Normal file
314
ntcore/src/test/native/cpp/EntryNotifierTest.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <support/Logger.h>
|
||||
|
||||
#include "EntryNotifier.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::_;
|
||||
|
||||
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(StringRef(result.name).startswith("/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
|
||||
52
ntcore/src/test/native/cpp/MessageMatcher.cpp
Normal file
52
ntcore/src/test/native/cpp/MessageMatcher.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "MessageMatcher.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
bool MessageMatcher::MatchAndExplain(
|
||||
std::shared_ptr<Message> msg,
|
||||
::testing::MatchResultListener* listener) const {
|
||||
bool match = true;
|
||||
if (!msg) return false;
|
||||
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())) {
|
||||
*listener << "value mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (msg->id() != goodmsg->id()) {
|
||||
*listener << "id mismatch ";
|
||||
match = false;
|
||||
}
|
||||
if (msg->flags() != goodmsg->flags()) {
|
||||
*listener << "flags mismatch";
|
||||
match = false;
|
||||
}
|
||||
if (msg->seq_num_uid() != goodmsg->seq_num_uid()) {
|
||||
*listener << "seq_num_uid mismatch";
|
||||
match = false;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void MessageMatcher::DescribeTo(::std::ostream* os) const {
|
||||
PrintTo(goodmsg, os);
|
||||
}
|
||||
|
||||
void MessageMatcher::DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "is not equal to ";
|
||||
PrintTo(goodmsg, os);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
42
ntcore/src/test/native/cpp/MessageMatcher.h
Normal file
42
ntcore/src/test/native/cpp/MessageMatcher.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_MESSAGEMATCHER_H_
|
||||
#define NTCORE_MESSAGEMATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#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(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_
|
||||
30
ntcore/src/test/native/cpp/MockConnectionNotifier.h
Normal file
30
ntcore/src/test/native/cpp/MockConnectionNotifier.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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_
|
||||
27
ntcore/src/test/native/cpp/MockDispatcher.h
Normal file
27
ntcore/src/test/native/cpp/MockDispatcher.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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_
|
||||
43
ntcore/src/test/native/cpp/MockEntryNotifier.h
Normal file
43
ntcore/src/test/native/cpp/MockEntryNotifier.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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,
|
||||
llvm::StringRef 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, llvm::StringRef 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, StringRef name,
|
||||
std::shared_ptr<Value> value, unsigned int flags,
|
||||
unsigned int only_listener));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKENTRYNOTIFIER_H_
|
||||
34
ntcore/src/test/native/cpp/MockNetworkConnection.h
Normal file
34
ntcore/src/test/native/cpp/MockNetworkConnection.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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_
|
||||
29
ntcore/src/test/native/cpp/MockRpcServer.h
Normal file
29
ntcore/src/test/native/cpp/MockRpcServer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the 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,
|
||||
StringRef name, StringRef params,
|
||||
const ConnectionInfo& conn, SendResponseFunc send_response,
|
||||
unsigned int rpc_uid));
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_MOCKRPCSERVER_H_
|
||||
91
ntcore/src/test/native/cpp/NetworkTableTest.cpp
Normal file
91
ntcore/src/test/native/cpp/NetworkTableTest.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "networktables/NetworkTable.h"
|
||||
#include "networktables/NetworkTableInstance.h"
|
||||
|
||||
class NetworkTableTest : public ::testing::Test {};
|
||||
|
||||
TEST_F(NetworkTableTest, BasenameKey) {
|
||||
EXPECT_EQ("simple", NetworkTable::BasenameKey("simple"));
|
||||
EXPECT_EQ("simple", NetworkTable::BasenameKey("one/two/many/simple"));
|
||||
EXPECT_EQ("simple",
|
||||
NetworkTable::BasenameKey("//////an/////awful/key////simple"));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, NormalizeKeySlash) {
|
||||
EXPECT_EQ("/", NetworkTable::NormalizeKey("///"));
|
||||
EXPECT_EQ("/no/normal/req", NetworkTable::NormalizeKey("/no/normal/req"));
|
||||
EXPECT_EQ("/no/leading/slash",
|
||||
NetworkTable::NormalizeKey("no/leading/slash"));
|
||||
EXPECT_EQ("/what/an/awful/key/",
|
||||
NetworkTable::NormalizeKey("//////what////an/awful/////key///"));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, NormalizeKeyNoSlash) {
|
||||
EXPECT_EQ("a", NetworkTable::NormalizeKey("a", false));
|
||||
EXPECT_EQ("a", NetworkTable::NormalizeKey("///a", false));
|
||||
EXPECT_EQ("leading/slash",
|
||||
NetworkTable::NormalizeKey("/leading/slash", false));
|
||||
EXPECT_EQ("no/leading/slash",
|
||||
NetworkTable::NormalizeKey("no/leading/slash", false));
|
||||
EXPECT_EQ(
|
||||
"what/an/awful/key/",
|
||||
NetworkTable::NormalizeKey("//////what////an/awful/////key///", false));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, GetHierarchyEmpty) {
|
||||
std::vector<std::string> expected{"/"};
|
||||
ASSERT_EQ(expected, NetworkTable::GetHierarchy(""));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, GetHierarchyRoot) {
|
||||
std::vector<std::string> expected{"/"};
|
||||
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/"));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, GetHierarchyNormal) {
|
||||
std::vector<std::string> expected{"/", "/foo", "/foo/bar", "/foo/bar/baz"};
|
||||
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/baz"));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, GetHierarchyTrailingSlash) {
|
||||
std::vector<std::string> expected{"/", "/foo", "/foo/bar", "/foo/bar/"};
|
||||
ASSERT_EQ(expected, NetworkTable::GetHierarchy("/foo/bar/"));
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, ContainsKey) {
|
||||
auto inst = nt::NetworkTableInstance::Create();
|
||||
auto nt = inst.GetTable("containskey");
|
||||
ASSERT_FALSE(nt->ContainsKey("testkey"));
|
||||
nt->PutNumber("testkey", 5);
|
||||
ASSERT_TRUE(nt->ContainsKey("testkey"));
|
||||
ASSERT_TRUE(inst.GetEntry("/containskey/testkey").Exists());
|
||||
ASSERT_FALSE(inst.GetEntry("containskey/testkey").Exists());
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, LeadingSlash) {
|
||||
auto inst = nt::NetworkTableInstance::Create();
|
||||
auto nt = inst.GetTable("leadingslash");
|
||||
auto nt2 = inst.GetTable("/leadingslash");
|
||||
ASSERT_FALSE(nt->ContainsKey("testkey"));
|
||||
nt2->PutNumber("testkey", 5);
|
||||
ASSERT_TRUE(nt->ContainsKey("testkey"));
|
||||
ASSERT_TRUE(inst.GetEntry("/leadingslash/testkey").Exists());
|
||||
}
|
||||
|
||||
TEST_F(NetworkTableTest, EmptyOrNoSlash) {
|
||||
auto inst = nt::NetworkTableInstance::Create();
|
||||
auto nt = inst.GetTable("/");
|
||||
auto nt2 = inst.GetTable("");
|
||||
ASSERT_FALSE(nt->ContainsKey("testkey"));
|
||||
nt2->PutNumber("testkey", 5);
|
||||
ASSERT_TRUE(nt->ContainsKey("testkey"));
|
||||
ASSERT_TRUE(inst.GetEntry("/testkey").Exists());
|
||||
}
|
||||
991
ntcore/src/test/native/cpp/StorageTest.cpp
Normal file
991
ntcore/src/test/native/cpp/StorageTest.cpp
Normal file
@@ -0,0 +1,991 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "StorageTest.h"
|
||||
|
||||
#include <llvm/raw_ostream.h>
|
||||
#include <support/raw_istream.h>
|
||||
|
||||
#include "MessageMatcher.h"
|
||||
#include "MockNetworkConnection.h"
|
||||
#include "Storage.h"
|
||||
#include "TestPrinters.h"
|
||||
#include "ValueMatcher.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::_;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class StorageTestEmpty : public StorageTest,
|
||||
public ::testing::TestWithParam<bool> {
|
||||
public:
|
||||
StorageTestEmpty() {
|
||||
HookOutgoing(GetParam());
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
}
|
||||
};
|
||||
|
||||
class StorageTestPopulateOne : public StorageTestEmpty {
|
||||
public:
|
||||
StorageTestPopulateOne() {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(false));
|
||||
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
}
|
||||
};
|
||||
|
||||
class StorageTestPopulated : public StorageTestEmpty {
|
||||
public:
|
||||
StorageTestPopulated() {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(false));
|
||||
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
|
||||
storage.SetEntryTypeValue("foo2", Value::MakeDouble(0.0));
|
||||
storage.SetEntryTypeValue("bar", Value::MakeDouble(1.0));
|
||||
storage.SetEntryTypeValue("bar2", Value::MakeBoolean(false));
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
}
|
||||
};
|
||||
|
||||
class StorageTestPersistent : public StorageTestEmpty {
|
||||
public:
|
||||
StorageTestPersistent() {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(false));
|
||||
storage.SetEntryTypeValue("boolean/true", Value::MakeBoolean(true));
|
||||
storage.SetEntryTypeValue("boolean/false", Value::MakeBoolean(false));
|
||||
storage.SetEntryTypeValue("double/neg", Value::MakeDouble(-1.5));
|
||||
storage.SetEntryTypeValue("double/zero", Value::MakeDouble(0.0));
|
||||
storage.SetEntryTypeValue("double/big", Value::MakeDouble(1.3e8));
|
||||
storage.SetEntryTypeValue("string/empty", Value::MakeString(""));
|
||||
storage.SetEntryTypeValue("string/normal", Value::MakeString("hello"));
|
||||
storage.SetEntryTypeValue("string/special",
|
||||
Value::MakeString(StringRef("\0\3\5\n", 4)));
|
||||
storage.SetEntryTypeValue("raw/empty", Value::MakeRaw(""));
|
||||
storage.SetEntryTypeValue("raw/normal", Value::MakeRaw("hello"));
|
||||
storage.SetEntryTypeValue("raw/special",
|
||||
Value::MakeRaw(StringRef("\0\3\5\n", 4)));
|
||||
storage.SetEntryTypeValue("booleanarr/empty",
|
||||
Value::MakeBooleanArray(std::vector<int>{}));
|
||||
storage.SetEntryTypeValue("booleanarr/one",
|
||||
Value::MakeBooleanArray(std::vector<int>{1}));
|
||||
storage.SetEntryTypeValue("booleanarr/two",
|
||||
Value::MakeBooleanArray(std::vector<int>{1, 0}));
|
||||
storage.SetEntryTypeValue("doublearr/empty",
|
||||
Value::MakeDoubleArray(std::vector<double>{}));
|
||||
storage.SetEntryTypeValue("doublearr/one",
|
||||
Value::MakeDoubleArray(std::vector<double>{0.5}));
|
||||
storage.SetEntryTypeValue(
|
||||
"doublearr/two",
|
||||
Value::MakeDoubleArray(std::vector<double>{0.5, -0.25}));
|
||||
storage.SetEntryTypeValue(
|
||||
"stringarr/empty", Value::MakeStringArray(std::vector<std::string>{}));
|
||||
storage.SetEntryTypeValue(
|
||||
"stringarr/one",
|
||||
Value::MakeStringArray(std::vector<std::string>{"hello"}));
|
||||
storage.SetEntryTypeValue(
|
||||
"stringarr/two",
|
||||
Value::MakeStringArray(std::vector<std::string>{"hello", "world\n"}));
|
||||
storage.SetEntryTypeValue(StringRef("\0\3\5\n", 4),
|
||||
Value::MakeBoolean(true));
|
||||
storage.SetEntryTypeValue("=", Value::MakeBoolean(true));
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
EXPECT_CALL(notifier, local_notifiers())
|
||||
.Times(AnyNumber())
|
||||
.WillRepeatedly(Return(true));
|
||||
}
|
||||
};
|
||||
|
||||
class MockLoadWarn {
|
||||
public:
|
||||
MOCK_METHOD2(Warn, void(size_t line, llvm::StringRef msg));
|
||||
};
|
||||
|
||||
TEST_P(StorageTestEmpty, Construct) {
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, StorageEntryInit) {
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_FALSE(entry->value);
|
||||
EXPECT_EQ(0u, entry->flags);
|
||||
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
|
||||
EXPECT_EQ(0xffffu, entry->id);
|
||||
EXPECT_EQ(SequenceNumber(), entry->seq_num);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, GetEntryValueNotExist) {
|
||||
EXPECT_FALSE(storage.GetEntryValue("foo"));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, GetEntryValueExist) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _));
|
||||
storage.SetEntryTypeValue("foo", value);
|
||||
EXPECT_EQ(value, storage.GetEntryValue("foo"));
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryTypeValueAssignNew) {
|
||||
// brand new entry
|
||||
auto value = Value::MakeBoolean(true);
|
||||
// id assigned if server
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign(
|
||||
"foo", GetParam() ? 0 : 0xffff, 1, value, 0)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), value,
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
storage.SetEntryTypeValue("foo", value);
|
||||
EXPECT_EQ(value, GetEntry("foo")->value);
|
||||
if (GetParam()) {
|
||||
ASSERT_EQ(1u, idmap().size());
|
||||
EXPECT_EQ(value, idmap()[0]->value);
|
||||
} else {
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetEntryTypeValueAssignTypeChange) {
|
||||
// update with different type results in assignment message
|
||||
auto value = Value::MakeDouble(0.0);
|
||||
|
||||
// id assigned if server; seq_num incremented
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign(
|
||||
"foo", GetParam() ? 0 : 0xffff, 2, value, 0)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(0, StringRef("foo"), value,
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
storage.SetEntryTypeValue("foo", value);
|
||||
EXPECT_EQ(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetEntryTypeValueEqualValue) {
|
||||
// update with same type and same value: change value contents but no update
|
||||
// message is issued (minimizing bandwidth usage)
|
||||
auto value = Value::MakeBoolean(true);
|
||||
storage.SetEntryTypeValue("foo", value);
|
||||
EXPECT_EQ(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, SetEntryTypeValueDifferentValue) {
|
||||
// update with same type and different value results in value update message
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned if server; seq_num incremented
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryUpdate(1, 2, value)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), value,
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
storage.SetEntryTypeValue("foo2", value);
|
||||
EXPECT_EQ(value, GetEntry("foo2")->value);
|
||||
|
||||
if (!GetParam()) {
|
||||
// seq_num should still be incremented
|
||||
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyName) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
storage.SetEntryTypeValue("", value);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyValue) {
|
||||
storage.SetEntryTypeValue("foo", nullptr);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryValueAssignNew) {
|
||||
// brand new entry
|
||||
auto value = Value::MakeBoolean(true);
|
||||
|
||||
// id assigned if server
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign(
|
||||
"foo", GetParam() ? 0 : 0xffff, 1, value, 0)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), value,
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
EXPECT_TRUE(storage.SetEntryValue("foo", value));
|
||||
EXPECT_EQ(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetEntryValueAssignTypeChange) {
|
||||
// update with different type results in error and no message or notification
|
||||
auto value = Value::MakeDouble(0.0);
|
||||
EXPECT_FALSE(storage.SetEntryValue("foo", value));
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_NE(value, entry->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetEntryValueEqualValue) {
|
||||
// update with same type and same value: change value contents but no update
|
||||
// message is issued (minimizing bandwidth usage)
|
||||
auto value = Value::MakeBoolean(true);
|
||||
EXPECT_TRUE(storage.SetEntryValue("foo", value));
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_EQ(value, entry->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, SetEntryValueDifferentValue) {
|
||||
// update with same type and different value results in value update message
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned if server; seq_num incremented
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryUpdate(1, 2, value)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), value,
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
EXPECT_TRUE(storage.SetEntryValue("foo2", value));
|
||||
auto entry = GetEntry("foo2");
|
||||
EXPECT_EQ(value, entry->value);
|
||||
|
||||
if (!GetParam()) {
|
||||
// seq_num should still be incremented
|
||||
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryValueEmptyName) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
EXPECT_TRUE(storage.SetEntryValue("", value));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryValueEmptyValue) {
|
||||
EXPECT_TRUE(storage.SetEntryValue("foo", nullptr));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetDefaultEntryAssignNew) {
|
||||
// brand new entry
|
||||
auto value = Value::MakeBoolean(true);
|
||||
|
||||
// id assigned if server
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign(
|
||||
"foo", GetParam() ? 0 : 0xffff, 1, value, 0)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), value,
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
auto ret_val = storage.SetDefaultEntryValue("foo", value);
|
||||
EXPECT_TRUE(ret_val);
|
||||
EXPECT_EQ(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsSameType) {
|
||||
// existing entry
|
||||
auto value = Value::MakeBoolean(true);
|
||||
auto ret_val = storage.SetDefaultEntryValue("foo", value);
|
||||
EXPECT_TRUE(ret_val);
|
||||
EXPECT_NE(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsDifferentType) {
|
||||
// existing entry is boolean
|
||||
auto value = Value::MakeDouble(2.0);
|
||||
auto ret_val = storage.SetDefaultEntryValue("foo", value);
|
||||
EXPECT_FALSE(ret_val);
|
||||
// should not have updated value in table if it already existed.
|
||||
EXPECT_NE(value, GetEntry("foo")->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyName) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
auto ret_val = storage.SetDefaultEntryValue("", value);
|
||||
EXPECT_FALSE(ret_val);
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_FALSE(entry->value);
|
||||
EXPECT_EQ(0u, entry->flags);
|
||||
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
|
||||
EXPECT_EQ(0xffffu, entry->id);
|
||||
EXPECT_EQ(SequenceNumber(), entry->seq_num);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyValue) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
auto ret_val = storage.SetDefaultEntryValue("", nullptr);
|
||||
EXPECT_FALSE(ret_val);
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_FALSE(entry->value);
|
||||
EXPECT_EQ(0u, entry->flags);
|
||||
EXPECT_EQ("foobar", entry->name); // since GetEntry uses the tmp_entry.
|
||||
EXPECT_EQ(0xffffu, entry->id);
|
||||
EXPECT_EQ(SequenceNumber(), entry->seq_num);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyName) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
auto ret_val = storage.SetDefaultEntryValue("", value);
|
||||
EXPECT_FALSE(ret_val);
|
||||
// assert that no entries get added
|
||||
EXPECT_EQ(4u, entries().size());
|
||||
if (GetParam())
|
||||
EXPECT_EQ(4u, idmap().size());
|
||||
else
|
||||
EXPECT_EQ(0u, idmap().size());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyValue) {
|
||||
auto value = Value::MakeBoolean(true);
|
||||
auto ret_val = storage.SetDefaultEntryValue("", nullptr);
|
||||
EXPECT_FALSE(ret_val);
|
||||
// assert that no entries get added
|
||||
EXPECT_EQ(4u, entries().size());
|
||||
if (GetParam())
|
||||
EXPECT_EQ(4u, idmap().size());
|
||||
else
|
||||
EXPECT_EQ(0u, idmap().size());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryFlagsNew) {
|
||||
// flags setting doesn't create an entry
|
||||
storage.SetEntryFlags("foo", 0u);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, SetEntryFlagsEqualValue) {
|
||||
// update with same value: no update message is issued (minimizing bandwidth
|
||||
// usage)
|
||||
storage.SetEntryFlags("foo", 0u);
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_EQ(0u, entry->flags);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, SetEntryFlagsDifferentValue) {
|
||||
// update with different value results in flags update message
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned as this is the server
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::FlagsUpdate(1, 1)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), _,
|
||||
NT_NOTIFY_FLAGS | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
storage.SetEntryFlags("foo2", 1u);
|
||||
EXPECT_EQ(1u, GetEntry("foo2")->flags);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, SetEntryFlagsEmptyName) {
|
||||
storage.SetEntryFlags("", 0u);
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, GetEntryFlagsNotExist) {
|
||||
EXPECT_EQ(0u, storage.GetEntryFlags("foo"));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, GetEntryFlagsExist) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _));
|
||||
storage.SetEntryFlags("foo", 1u);
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
EXPECT_EQ(1u, storage.GetEntryFlags("foo"));
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, DeleteEntryNotExist) { storage.DeleteEntry("foo"); }
|
||||
|
||||
TEST_P(StorageTestPopulated, DeleteEntryExist) {
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned as this is the server
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::EntryDelete(1)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), ValueEq(Value::MakeDouble(0)),
|
||||
NT_NOTIFY_DELETE | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
storage.DeleteEntry("foo2");
|
||||
ASSERT_EQ(1u, entries().count("foo2"));
|
||||
EXPECT_EQ(nullptr, entries()["foo2"]->value);
|
||||
EXPECT_EQ(0xffffu, entries()["foo2"]->id);
|
||||
EXPECT_FALSE(entries()["foo2"]->local_write);
|
||||
if (GetParam()) {
|
||||
ASSERT_TRUE(idmap().size() >= 2);
|
||||
EXPECT_FALSE(idmap()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, DeleteAllEntriesEmpty) {
|
||||
storage.DeleteAllEntries();
|
||||
ASSERT_TRUE(entries().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, DeleteAllEntries) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::ClearEntries()),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, NT_NOTIFY_DELETE | NT_NOTIFY_LOCAL,
|
||||
UINT_MAX))
|
||||
.Times(4);
|
||||
|
||||
storage.DeleteAllEntries();
|
||||
ASSERT_EQ(1u, entries().count("foo2"));
|
||||
EXPECT_EQ(nullptr, entries()["foo2"]->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, DeleteAllEntriesPersistent) {
|
||||
GetEntry("foo2")->flags = NT_PERSISTENT;
|
||||
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::ClearEntries()),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, NT_NOTIFY_DELETE | NT_NOTIFY_LOCAL,
|
||||
UINT_MAX))
|
||||
.Times(3);
|
||||
|
||||
storage.DeleteAllEntries();
|
||||
ASSERT_EQ(1u, entries().count("foo2"));
|
||||
EXPECT_NE(nullptr, entries()["foo2"]->value);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, GetEntryInfoAll) {
|
||||
auto info = storage.GetEntryInfo(0, "", 0u);
|
||||
ASSERT_EQ(4u, info.size());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, GetEntryInfoPrefix) {
|
||||
auto info = storage.GetEntryInfo(0, "foo", 0u);
|
||||
ASSERT_EQ(2u, info.size());
|
||||
if (info[0].name == "foo") {
|
||||
EXPECT_EQ("foo", info[0].name);
|
||||
EXPECT_EQ(NT_BOOLEAN, info[0].type);
|
||||
EXPECT_EQ("foo2", info[1].name);
|
||||
EXPECT_EQ(NT_DOUBLE, info[1].type);
|
||||
} else {
|
||||
EXPECT_EQ("foo2", info[0].name);
|
||||
EXPECT_EQ(NT_DOUBLE, info[0].type);
|
||||
EXPECT_EQ("foo", info[1].name);
|
||||
EXPECT_EQ(NT_BOOLEAN, info[1].type);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, GetEntryInfoTypes) {
|
||||
auto info = storage.GetEntryInfo(0, "", NT_DOUBLE);
|
||||
ASSERT_EQ(2u, info.size());
|
||||
EXPECT_EQ(NT_DOUBLE, info[0].type);
|
||||
EXPECT_EQ(NT_DOUBLE, info[1].type);
|
||||
if (info[0].name == "foo2") {
|
||||
EXPECT_EQ("foo2", info[0].name);
|
||||
EXPECT_EQ("bar", info[1].name);
|
||||
} else {
|
||||
EXPECT_EQ("bar", info[0].name);
|
||||
EXPECT_EQ("foo2", info[1].name);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, GetEntryInfoPrefixTypes) {
|
||||
auto info = storage.GetEntryInfo(0, "bar", NT_BOOLEAN);
|
||||
ASSERT_EQ(1u, info.size());
|
||||
EXPECT_EQ("bar2", info[0].name);
|
||||
EXPECT_EQ(NT_BOOLEAN, info[0].type);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPersistent, SavePersistentEmpty) {
|
||||
llvm::SmallString<256> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
storage.SavePersistent(oss, false);
|
||||
ASSERT_EQ("[NetworkTables Storage 3.0]\n", oss.str());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPersistent, SavePersistent) {
|
||||
for (auto& i : entries()) i.getValue()->flags = NT_PERSISTENT;
|
||||
llvm::SmallString<256> buf;
|
||||
llvm::raw_svector_ostream oss(buf);
|
||||
storage.SavePersistent(oss, false);
|
||||
llvm::StringRef out = oss.str();
|
||||
// std::fputs(out.c_str(), stderr);
|
||||
llvm::StringRef line, rem = out;
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("[NetworkTables Storage 3.0]", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("boolean \"\\x00\\x03\\x05\\n\"=true", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("boolean \"\\x3D\"=true", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("boolean \"boolean/false\"=false", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("boolean \"boolean/true\"=true", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array boolean \"booleanarr/empty\"=", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array boolean \"booleanarr/one\"=true", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array boolean \"booleanarr/two\"=true,false", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
ASSERT_EQ("double \"double/big\"=1.3e+008", line);
|
||||
#else
|
||||
ASSERT_EQ("double \"double/big\"=1.3e+08", line);
|
||||
#endif
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("double \"double/neg\"=-1.5", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("double \"double/zero\"=0", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array double \"doublearr/empty\"=", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array double \"doublearr/one\"=0.5", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array double \"doublearr/two\"=0.5,-0.25", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("raw \"raw/empty\"=", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("raw \"raw/normal\"=aGVsbG8=", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("raw \"raw/special\"=AAMFCg==", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("string \"string/empty\"=\"\"", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("string \"string/normal\"=\"hello\"", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("string \"string/special\"=\"\\x00\\x03\\x05\\n\"", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array string \"stringarr/empty\"=", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array string \"stringarr/one\"=\"hello\"", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("array string \"stringarr/two\"=\"hello\",\"world\\n\"", line);
|
||||
std::tie(line, rem) = rem.split('\n');
|
||||
ASSERT_EQ("", line);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistentBadHeader) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
wpi::raw_mem_istream iss("");
|
||||
EXPECT_CALL(
|
||||
warn,
|
||||
Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
|
||||
EXPECT_FALSE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
|
||||
wpi::raw_mem_istream iss2("[NetworkTables");
|
||||
EXPECT_CALL(
|
||||
warn,
|
||||
Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
|
||||
|
||||
EXPECT_FALSE(storage.LoadEntries(iss2, "", true, warn_func));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistentCommentHeader) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"\n; comment\n# comment\n[NetworkTables Storage 3.0]\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistentEmptyName) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
wpi::raw_mem_istream iss("[NetworkTables Storage 3.0]\nboolean \"\"=true\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistentAssign) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
auto value = Value::MakeBoolean(true);
|
||||
|
||||
// id assigned if server
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::EntryAssign(
|
||||
"foo", GetParam() ? 0 : 0xffff, 1,
|
||||
value, NT_PERSISTENT)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"),
|
||||
ValueEq(Value::MakeBoolean(true)),
|
||||
NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"[NetworkTables Storage 3.0]\nboolean \"foo\"=true\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
auto entry = GetEntry("foo");
|
||||
EXPECT_EQ(*value, *entry->value);
|
||||
EXPECT_EQ(NT_PERSISTENT, entry->flags);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, LoadPersistentUpdateFlags) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned as this is server
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::FlagsUpdate(1, NT_PERSISTENT)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), ValueEq(Value::MakeDouble(0)),
|
||||
NT_NOTIFY_FLAGS | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=0.0\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
auto entry = GetEntry("foo2");
|
||||
EXPECT_EQ(*Value::MakeDouble(0.0), *entry->value);
|
||||
EXPECT_EQ(NT_PERSISTENT, entry->flags);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, LoadPersistentUpdateValue) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
GetEntry("foo2")->flags = NT_PERSISTENT;
|
||||
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned as this is the server; seq_num incremented
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryUpdate(1, 2, value)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), ValueEq(Value::MakeDouble(1)),
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL, UINT_MAX));
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
auto entry = GetEntry("foo2");
|
||||
EXPECT_EQ(*value, *entry->value);
|
||||
EXPECT_EQ(NT_PERSISTENT, entry->flags);
|
||||
|
||||
if (!GetParam()) {
|
||||
// seq_num should still be incremented
|
||||
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulated, LoadPersistentUpdateValueFlags) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
|
||||
// client shouldn't send an update as id not assigned yet
|
||||
if (GetParam()) {
|
||||
// id assigned as this is the server; seq_num incremented
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryUpdate(1, 2, value)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::FlagsUpdate(1, NT_PERSISTENT)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(1, StringRef("foo2"), ValueEq(Value::MakeDouble(1)),
|
||||
NT_NOTIFY_FLAGS | NT_NOTIFY_UPDATE | NT_NOTIFY_LOCAL,
|
||||
UINT_MAX));
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
auto entry = GetEntry("foo2");
|
||||
EXPECT_EQ(*value, *entry->value);
|
||||
EXPECT_EQ(NT_PERSISTENT, entry->flags);
|
||||
|
||||
if (!GetParam()) {
|
||||
// seq_num should still be incremented
|
||||
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistent) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
std::string in = "[NetworkTables Storage 3.0]\n";
|
||||
in += "boolean \"\\x00\\x03\\x05\\n\"=true\n";
|
||||
in += "boolean \"\\x3D\"=true\n";
|
||||
in += "boolean \"boolean/false\"=false\n";
|
||||
in += "boolean \"boolean/true\"=true\n";
|
||||
in += "array boolean \"booleanarr/empty\"=\n";
|
||||
in += "array boolean \"booleanarr/one\"=true\n";
|
||||
in += "array boolean \"booleanarr/two\"=true,false\n";
|
||||
in += "double \"double/big\"=1.3e+08\n";
|
||||
in += "double \"double/neg\"=-1.5\n";
|
||||
in += "double \"double/zero\"=0\n";
|
||||
in += "array double \"doublearr/empty\"=\n";
|
||||
in += "array double \"doublearr/one\"=0.5\n";
|
||||
in += "array double \"doublearr/two\"=0.5,-0.25\n";
|
||||
in += "raw \"raw/empty\"=\n";
|
||||
in += "raw \"raw/normal\"=aGVsbG8=\n";
|
||||
in += "raw \"raw/special\"=AAMFCg==\n";
|
||||
in += "string \"string/empty\"=\"\"\n";
|
||||
in += "string \"string/normal\"=\"hello\"\n";
|
||||
in += "string \"string/special\"=\"\\x00\\x03\\x05\\n\"\n";
|
||||
in += "array string \"stringarr/empty\"=\n";
|
||||
in += "array string \"stringarr/one\"=\"hello\"\n";
|
||||
in += "array string \"stringarr/two\"=\"hello\",\"world\\n\"\n";
|
||||
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(22);
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(_, _, _, NT_NOTIFY_NEW | NT_NOTIFY_LOCAL, UINT_MAX))
|
||||
.Times(22);
|
||||
|
||||
wpi::raw_mem_istream iss(in);
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
ASSERT_EQ(22u, entries().size());
|
||||
|
||||
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("boolean/true"));
|
||||
EXPECT_EQ(*Value::MakeBoolean(false),
|
||||
*storage.GetEntryValue("boolean/false"));
|
||||
EXPECT_EQ(*Value::MakeDouble(-1.5), *storage.GetEntryValue("double/neg"));
|
||||
EXPECT_EQ(*Value::MakeDouble(0.0), *storage.GetEntryValue("double/zero"));
|
||||
EXPECT_EQ(*Value::MakeDouble(1.3e8), *storage.GetEntryValue("double/big"));
|
||||
EXPECT_EQ(*Value::MakeString(""), *storage.GetEntryValue("string/empty"));
|
||||
EXPECT_EQ(*Value::MakeString("hello"),
|
||||
*storage.GetEntryValue("string/normal"));
|
||||
EXPECT_EQ(*Value::MakeString(StringRef("\0\3\5\n", 4)),
|
||||
*storage.GetEntryValue("string/special"));
|
||||
EXPECT_EQ(*Value::MakeRaw(""), *storage.GetEntryValue("raw/empty"));
|
||||
EXPECT_EQ(*Value::MakeRaw("hello"), *storage.GetEntryValue("raw/normal"));
|
||||
EXPECT_EQ(*Value::MakeRaw(StringRef("\0\3\5\n", 4)),
|
||||
*storage.GetEntryValue("raw/special"));
|
||||
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{}),
|
||||
*storage.GetEntryValue("booleanarr/empty"));
|
||||
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{1}),
|
||||
*storage.GetEntryValue("booleanarr/one"));
|
||||
EXPECT_EQ(*Value::MakeBooleanArray(std::vector<int>{1, 0}),
|
||||
*storage.GetEntryValue("booleanarr/two"));
|
||||
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{}),
|
||||
*storage.GetEntryValue("doublearr/empty"));
|
||||
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{0.5}),
|
||||
*storage.GetEntryValue("doublearr/one"));
|
||||
EXPECT_EQ(*Value::MakeDoubleArray(std::vector<double>{0.5, -0.25}),
|
||||
*storage.GetEntryValue("doublearr/two"));
|
||||
EXPECT_EQ(*Value::MakeStringArray(std::vector<std::string>{}),
|
||||
*storage.GetEntryValue("stringarr/empty"));
|
||||
EXPECT_EQ(*Value::MakeStringArray(std::vector<std::string>{"hello"}),
|
||||
*storage.GetEntryValue("stringarr/one"));
|
||||
EXPECT_EQ(
|
||||
*Value::MakeStringArray(std::vector<std::string>{"hello", "world\n"}),
|
||||
*storage.GetEntryValue("stringarr/two"));
|
||||
EXPECT_EQ(*Value::MakeBoolean(true),
|
||||
*storage.GetEntryValue(StringRef("\0\3\5\n", 4)));
|
||||
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("="));
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, LoadPersistentWarn) {
|
||||
MockLoadWarn warn;
|
||||
auto warn_func = [&](size_t line, const char* msg) { warn.Warn(line, msg); };
|
||||
|
||||
wpi::raw_mem_istream iss(
|
||||
"[NetworkTables Storage 3.0]\nboolean \"foo\"=foo\n");
|
||||
EXPECT_CALL(
|
||||
warn, Warn(2, llvm::StringRef(
|
||||
"unrecognized boolean value, not 'true' or 'false'")));
|
||||
EXPECT_TRUE(storage.LoadEntries(iss, "", true, warn_func));
|
||||
|
||||
EXPECT_TRUE(entries().empty());
|
||||
EXPECT_TRUE(idmap().empty());
|
||||
}
|
||||
|
||||
TEST_P(StorageTestEmpty, ProcessIncomingEntryAssign) {
|
||||
auto conn = std::make_shared<MockNetworkConnection>();
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
if (GetParam()) {
|
||||
// id assign message reply generated on the server; sent to everyone
|
||||
EXPECT_CALL(
|
||||
dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign("foo", 0, 0, value, 0)),
|
||||
IsNull(), IsNull()));
|
||||
}
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), ValueEq(value),
|
||||
NT_NOTIFY_NEW, UINT_MAX));
|
||||
|
||||
storage.ProcessIncoming(
|
||||
Message::EntryAssign("foo", GetParam() ? 0xffff : 0, 0, value, 0),
|
||||
conn.get(), conn);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, ProcessIncomingEntryAssign) {
|
||||
auto conn = std::make_shared<MockNetworkConnection>();
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
EXPECT_CALL(*conn, proto_rev()).WillRepeatedly(Return(0x0300u));
|
||||
if (GetParam()) {
|
||||
// server broadcasts new value to all *other* connections
|
||||
EXPECT_CALL(
|
||||
dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign("foo", 0, 1, value, 0)),
|
||||
IsNull(), conn.get()));
|
||||
}
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), ValueEq(value),
|
||||
NT_NOTIFY_UPDATE, UINT_MAX));
|
||||
|
||||
storage.ProcessIncoming(Message::EntryAssign("foo", 0, 1, value, 0),
|
||||
conn.get(), conn);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, ProcessIncomingEntryAssignIgnore) {
|
||||
auto conn = std::make_shared<MockNetworkConnection>();
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
storage.ProcessIncoming(Message::EntryAssign("foo", 0xffff, 1, value, 0),
|
||||
conn.get(), conn);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, ProcessIncomingEntryAssignWithFlags) {
|
||||
auto conn = std::make_shared<MockNetworkConnection>();
|
||||
auto value = Value::MakeDouble(1.0);
|
||||
EXPECT_CALL(*conn, proto_rev()).WillRepeatedly(Return(0x0300u));
|
||||
if (GetParam()) {
|
||||
// server broadcasts new value/flags to all *other* connections
|
||||
EXPECT_CALL(
|
||||
dispatcher,
|
||||
QueueOutgoing(MessageEq(Message::EntryAssign("foo", 0, 1, value, 0x2)),
|
||||
IsNull(), conn.get()));
|
||||
EXPECT_CALL(notifier,
|
||||
NotifyEntry(0, StringRef("foo"), ValueEq(value),
|
||||
NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS, UINT_MAX));
|
||||
} else {
|
||||
// client forces flags back when an assign message is received for an
|
||||
// existing entry with different flags
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::FlagsUpdate(0, 0)),
|
||||
IsNull(), IsNull()));
|
||||
EXPECT_CALL(notifier, NotifyEntry(0, StringRef("foo"), ValueEq(value),
|
||||
NT_NOTIFY_UPDATE, UINT_MAX));
|
||||
}
|
||||
|
||||
storage.ProcessIncoming(Message::EntryAssign("foo", 0, 1, value, 0x2),
|
||||
conn.get(), conn);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, DeleteCheckHandle) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
auto handle = storage.GetEntry("foo");
|
||||
storage.DeleteEntry("foo");
|
||||
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
|
||||
auto handle2 = storage.GetEntry("foo");
|
||||
ASSERT_EQ(handle, handle2);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, DeletedEntryFlags) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
auto handle = storage.GetEntry("foo");
|
||||
storage.SetEntryFlags("foo", 2);
|
||||
storage.DeleteEntry("foo");
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
|
||||
EXPECT_EQ(storage.GetEntryFlags("foo"), 0u);
|
||||
EXPECT_EQ(storage.GetEntryFlags(handle), 0u);
|
||||
storage.SetEntryFlags("foo", 4);
|
||||
storage.SetEntryFlags(handle, 4);
|
||||
EXPECT_EQ(storage.GetEntryFlags("foo"), 0u);
|
||||
EXPECT_EQ(storage.GetEntryFlags(handle), 0u);
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, DeletedDeleteAllEntries) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
storage.DeleteEntry("foo");
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(MessageEq(Message::ClearEntries()),
|
||||
IsNull(), IsNull()));
|
||||
storage.DeleteAllEntries();
|
||||
}
|
||||
|
||||
TEST_P(StorageTestPopulateOne, DeletedGetEntries) {
|
||||
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
|
||||
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _)).Times(AnyNumber());
|
||||
storage.DeleteEntry("foo");
|
||||
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
|
||||
::testing::Mock::VerifyAndClearExpectations(¬ifier);
|
||||
|
||||
EXPECT_TRUE(storage.GetEntries("", 0).empty());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(StorageTestsEmpty, StorageTestEmpty,
|
||||
::testing::Bool(), );
|
||||
INSTANTIATE_TEST_CASE_P(StorageTestsPopulateOne, StorageTestPopulateOne,
|
||||
::testing::Bool(), );
|
||||
INSTANTIATE_TEST_CASE_P(StorageTestsPopulated, StorageTestPopulated,
|
||||
::testing::Bool(), );
|
||||
INSTANTIATE_TEST_CASE_P(StorageTestsPersistent, StorageTestPersistent,
|
||||
::testing::Bool(), );
|
||||
|
||||
} // namespace nt
|
||||
47
ntcore/src/test/native/cpp/StorageTest.h
Normal file
47
ntcore/src/test/native/cpp/StorageTest.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_STORAGETEST_H_
|
||||
#define NTCORE_STORAGETEST_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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") {}
|
||||
|
||||
Storage::EntriesMap& entries() { return storage.m_entries; }
|
||||
Storage::IdMap& idmap() { return storage.m_idmap; }
|
||||
|
||||
Storage::Entry* GetEntry(StringRef name) {
|
||||
auto i = storage.m_entries.find(name);
|
||||
return i == storage.m_entries.end() ? &tmp_entry : i->getValue();
|
||||
}
|
||||
|
||||
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_
|
||||
156
ntcore/src/test/native/cpp/TestPrinters.cpp
Normal file
156
ntcore/src/test/native/cpp/TestPrinters.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "TestPrinters.h"
|
||||
|
||||
#include "Handle.h"
|
||||
#include "Message.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
void PrintTo(const EntryNotification& event, std::ostream* os) {
|
||||
*os << "EntryNotification{listener=";
|
||||
PrintTo(Handle{event.listener}, os);
|
||||
*os << ", entry=";
|
||||
PrintTo(Handle{event.entry}, os);
|
||||
*os << ", name=\"" << event.name << "\", flags=" << event.flags << ", value=";
|
||||
PrintTo(event.value, os);
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << "Handle{";
|
||||
switch (handle.GetType()) {
|
||||
case Handle::kConnectionListener:
|
||||
*os << "kConnectionListener";
|
||||
break;
|
||||
case Handle::kConnectionListenerPoller:
|
||||
*os << "kConnectionListenerPoller";
|
||||
break;
|
||||
case Handle::kEntry:
|
||||
*os << "kEntry";
|
||||
break;
|
||||
case Handle::kEntryListener:
|
||||
*os << "kEntryListener";
|
||||
break;
|
||||
case Handle::kEntryListenerPoller:
|
||||
*os << "kEntryListenerPoller";
|
||||
break;
|
||||
case Handle::kInstance:
|
||||
*os << "kInstance";
|
||||
break;
|
||||
case Handle::kLogger:
|
||||
*os << "kLogger";
|
||||
break;
|
||||
case Handle::kLoggerPoller:
|
||||
*os << "kLoggerPoller";
|
||||
break;
|
||||
case Handle::kRpcCall:
|
||||
*os << "kRpcCall";
|
||||
break;
|
||||
case Handle::kRpcCallPoller:
|
||||
*os << "kRpcCallPoller";
|
||||
break;
|
||||
default:
|
||||
*os << "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
*os << ", " << handle.GetInst() << ", " << handle.GetIndex() << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const Message& msg, std::ostream* os) {
|
||||
*os << "Message{";
|
||||
switch (msg.type()) {
|
||||
case Message::kKeepAlive:
|
||||
*os << "kKeepAlive";
|
||||
break;
|
||||
case Message::kClientHello:
|
||||
*os << "kClientHello";
|
||||
break;
|
||||
case Message::kProtoUnsup:
|
||||
*os << "kProtoUnsup";
|
||||
break;
|
||||
case Message::kServerHelloDone:
|
||||
*os << "kServerHelloDone";
|
||||
break;
|
||||
case Message::kServerHello:
|
||||
*os << "kServerHello";
|
||||
break;
|
||||
case Message::kClientHelloDone:
|
||||
*os << "kClientHelloDone";
|
||||
break;
|
||||
case Message::kEntryAssign:
|
||||
*os << "kEntryAssign";
|
||||
break;
|
||||
case Message::kEntryUpdate:
|
||||
*os << "kEntryUpdate";
|
||||
break;
|
||||
case Message::kFlagsUpdate:
|
||||
*os << "kFlagsUpdate";
|
||||
break;
|
||||
case Message::kEntryDelete:
|
||||
*os << "kEntryDelete";
|
||||
break;
|
||||
case Message::kClearEntries:
|
||||
*os << "kClearEntries";
|
||||
break;
|
||||
case Message::kExecuteRpc:
|
||||
*os << "kExecuteRpc";
|
||||
break;
|
||||
case Message::kRpcResponse:
|
||||
*os << "kRpcResponse";
|
||||
break;
|
||||
default:
|
||||
*os << "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
*os << ": str=\"" << msg.str() << "\", id=" << msg.id()
|
||||
<< ", flags=" << msg.flags() << ", seq_num_uid=" << msg.seq_num_uid()
|
||||
<< ", value=";
|
||||
PrintTo(msg.value(), os);
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const Value& value, std::ostream* os) {
|
||||
*os << "Value{";
|
||||
switch (value.type()) {
|
||||
case NT_UNASSIGNED:
|
||||
break;
|
||||
case NT_BOOLEAN:
|
||||
*os << (value.GetBoolean() ? "true" : "false");
|
||||
break;
|
||||
case NT_DOUBLE:
|
||||
*os << value.GetDouble();
|
||||
break;
|
||||
case NT_STRING:
|
||||
*os << '"' << value.GetString().str() << '"';
|
||||
break;
|
||||
case NT_RAW:
|
||||
*os << ::testing::PrintToString(value.GetRaw());
|
||||
break;
|
||||
case NT_BOOLEAN_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetBooleanArray());
|
||||
break;
|
||||
case NT_DOUBLE_ARRAY:
|
||||
*os << ::testing::PrintToString(value.GetDoubleArray());
|
||||
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;
|
||||
}
|
||||
*os << '}';
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
54
ntcore/src/test/native/cpp/TestPrinters.h
Normal file
54
ntcore/src/test/native/cpp/TestPrinters.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_TESTPRINTERS_H_
|
||||
#define NTCORE_TESTPRINTERS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
inline void PrintTo(StringRef str, ::std::ostream* os) {
|
||||
::testing::internal::PrintStringTo(str.str(), os);
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
namespace nt {
|
||||
|
||||
class EntryNotification;
|
||||
class Handle;
|
||||
class Message;
|
||||
class Value;
|
||||
|
||||
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 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 << '}';
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_TESTPRINTERS_H_
|
||||
33
ntcore/src/test/native/cpp/ValueMatcher.cpp
Normal file
33
ntcore/src/test/native/cpp/ValueMatcher.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "ValueMatcher.h"
|
||||
|
||||
#include "TestPrinters.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
bool ValueMatcher::MatchAndExplain(
|
||||
std::shared_ptr<Value> val,
|
||||
::testing::MatchResultListener* listener) const {
|
||||
if ((!val && goodval) || (val && !goodval) ||
|
||||
(val && goodval && *val != *goodval)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ValueMatcher::DescribeTo(::std::ostream* os) const {
|
||||
PrintTo(goodval, os);
|
||||
}
|
||||
|
||||
void ValueMatcher::DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "is not equal to ";
|
||||
PrintTo(goodval, os);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
40
ntcore/src/test/native/cpp/ValueMatcher.h
Normal file
40
ntcore/src/test/native/cpp/ValueMatcher.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2017-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef NTCORE_VALUEMATCHER_H_
|
||||
#define NTCORE_VALUEMATCHER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ValueMatcher
|
||||
: public ::testing::MatcherInterface<std::shared_ptr<Value>> {
|
||||
public:
|
||||
explicit ValueMatcher(std::shared_ptr<Value> goodval_) : goodval(goodval_) {}
|
||||
|
||||
bool MatchAndExplain(std::shared_ptr<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;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<std::shared_ptr<Value>> ValueEq(
|
||||
std::shared_ptr<Value> goodval) {
|
||||
return ::testing::MakeMatcher(new ValueMatcher(goodval));
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#endif // NTCORE_VALUEMATCHER_H_
|
||||
365
ntcore/src/test/native/cpp/ValueTest.cpp
Normal file
365
ntcore/src/test/native/cpp/ValueTest.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "Value_internal.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class ValueTest : public ::testing::Test {};
|
||||
|
||||
typedef ValueTest ValueDeathTest;
|
||||
|
||||
TEST_F(ValueTest, ConstructEmpty) {
|
||||
Value v;
|
||||
ASSERT_EQ(NT_UNASSIGNED, v.type());
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, Boolean) {
|
||||
auto v = Value::MakeBoolean(false);
|
||||
ASSERT_EQ(NT_BOOLEAN, v->type());
|
||||
ASSERT_FALSE(v->GetBoolean());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN, cv.type);
|
||||
ASSERT_EQ(0, cv.data.v_boolean);
|
||||
|
||||
v = Value::MakeBoolean(true);
|
||||
ASSERT_EQ(NT_BOOLEAN, v->type());
|
||||
ASSERT_TRUE(v->GetBoolean());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN, cv.type);
|
||||
ASSERT_EQ(1, cv.data.v_boolean);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, Double) {
|
||||
auto v = Value::MakeDouble(0.5);
|
||||
ASSERT_EQ(NT_DOUBLE, v->type());
|
||||
ASSERT_EQ(0.5, v->GetDouble());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE, cv.type);
|
||||
ASSERT_EQ(0.5, cv.data.v_double);
|
||||
|
||||
v = Value::MakeDouble(0.25);
|
||||
ASSERT_EQ(NT_DOUBLE, v->type());
|
||||
ASSERT_EQ(0.25, v->GetDouble());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE, cv.type);
|
||||
ASSERT_EQ(0.25, cv.data.v_double);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, String) {
|
||||
auto v = Value::MakeString("hello");
|
||||
ASSERT_EQ(NT_STRING, v->type());
|
||||
ASSERT_EQ("hello", v->GetString());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_STRING, cv.type);
|
||||
ASSERT_EQ(llvm::StringRef("hello"), 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());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_STRING, cv.type);
|
||||
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.v_string.str);
|
||||
ASSERT_EQ(7u, cv.data.v_string.len);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, Raw) {
|
||||
auto v = Value::MakeRaw("hello");
|
||||
ASSERT_EQ(NT_RAW, v->type());
|
||||
ASSERT_EQ("hello", v->GetRaw());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_RAW, cv.type);
|
||||
ASSERT_EQ(llvm::StringRef("hello"), cv.data.v_string.str);
|
||||
ASSERT_EQ(5u, cv.data.v_string.len);
|
||||
|
||||
v = Value::MakeRaw("goodbye");
|
||||
ASSERT_EQ(NT_RAW, v->type());
|
||||
ASSERT_EQ("goodbye", v->GetRaw());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_RAW, cv.type);
|
||||
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.v_string.str);
|
||||
ASSERT_EQ(7u, cv.data.v_string.len);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
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(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_boolean.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
|
||||
ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]);
|
||||
|
||||
// assign with same size
|
||||
vec = {0, 1, 0};
|
||||
v = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
|
||||
ASSERT_EQ(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_boolean.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
|
||||
ASSERT_EQ(vec[2], cv.data.arr_boolean.arr[2]);
|
||||
|
||||
// assign with different size
|
||||
vec = {1, 0};
|
||||
v = Value::MakeBooleanArray(vec);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, v->type());
|
||||
ASSERT_EQ(llvm::ArrayRef<int>(vec), v->GetBooleanArray());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_BOOLEAN_ARRAY, cv.type);
|
||||
ASSERT_EQ(2u, cv.data.arr_boolean.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_boolean.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_boolean.arr[1]);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
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(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_double.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
|
||||
ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]);
|
||||
|
||||
// assign with same size
|
||||
vec = {0.25, 0.5, 0.25};
|
||||
v = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
|
||||
ASSERT_EQ(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_double.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
|
||||
ASSERT_EQ(vec[2], cv.data.arr_double.arr[2]);
|
||||
|
||||
// assign with different size
|
||||
vec = {0.5, 0.25};
|
||||
v = Value::MakeDoubleArray(vec);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, v->type());
|
||||
ASSERT_EQ(llvm::ArrayRef<double>(vec), v->GetDoubleArray());
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_DOUBLE_ARRAY, cv.type);
|
||||
ASSERT_EQ(2u, cv.data.arr_double.size);
|
||||
ASSERT_EQ(vec[0], cv.data.arr_double.arr[0]);
|
||||
ASSERT_EQ(vec[1], cv.data.arr_double.arr[1]);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, StringArray) {
|
||||
std::vector<std::string> vec;
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodbye");
|
||||
vec.push_back("string");
|
||||
auto v = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_EQ(NT_STRING_ARRAY, v->type());
|
||||
ASSERT_EQ(3u, v->GetStringArray().size());
|
||||
ASSERT_EQ(llvm::StringRef("hello"), v->GetStringArray()[0]);
|
||||
ASSERT_EQ(llvm::StringRef("goodbye"), v->GetStringArray()[1]);
|
||||
ASSERT_EQ(llvm::StringRef("string"), v->GetStringArray()[2]);
|
||||
NT_Value cv;
|
||||
NT_InitValue(&cv);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_string.size);
|
||||
ASSERT_EQ(llvm::StringRef("hello"), cv.data.arr_string.arr[0].str);
|
||||
ASSERT_EQ(llvm::StringRef("goodbye"), cv.data.arr_string.arr[1].str);
|
||||
ASSERT_EQ(llvm::StringRef("string"), cv.data.arr_string.arr[2].str);
|
||||
|
||||
// assign with same size
|
||||
vec.clear();
|
||||
vec.push_back("s1");
|
||||
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(llvm::StringRef("s1"), v->GetStringArray()[0]);
|
||||
ASSERT_EQ(llvm::StringRef("str2"), v->GetStringArray()[1]);
|
||||
ASSERT_EQ(llvm::StringRef("string3"), v->GetStringArray()[2]);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(3u, cv.data.arr_string.size);
|
||||
ASSERT_EQ(llvm::StringRef("s1"), cv.data.arr_string.arr[0].str);
|
||||
ASSERT_EQ(llvm::StringRef("str2"), cv.data.arr_string.arr[1].str);
|
||||
ASSERT_EQ(llvm::StringRef("string3"), cv.data.arr_string.arr[2].str);
|
||||
|
||||
// assign with different size
|
||||
vec.clear();
|
||||
vec.push_back("short");
|
||||
vec.push_back("er");
|
||||
v = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_EQ(NT_STRING_ARRAY, v->type());
|
||||
ASSERT_EQ(2u, v->GetStringArray().size());
|
||||
ASSERT_EQ(llvm::StringRef("short"), v->GetStringArray()[0]);
|
||||
ASSERT_EQ(llvm::StringRef("er"), v->GetStringArray()[1]);
|
||||
ConvertToC(*v, &cv);
|
||||
ASSERT_EQ(NT_STRING_ARRAY, cv.type);
|
||||
ASSERT_EQ(2u, cv.data.arr_string.size);
|
||||
ASSERT_EQ(llvm::StringRef("short"), cv.data.arr_string.arr[0].str);
|
||||
ASSERT_EQ(llvm::StringRef("er"), cv.data.arr_string.arr[1].str);
|
||||
|
||||
NT_DisposeValue(&cv);
|
||||
}
|
||||
|
||||
TEST_F(ValueDeathTest, GetAssertions) {
|
||||
Value v;
|
||||
ASSERT_DEATH(v.GetBoolean(), "type == NT_BOOLEAN");
|
||||
ASSERT_DEATH(v.GetDouble(), "type == NT_DOUBLE");
|
||||
ASSERT_DEATH(v.GetString(), "type == NT_STRING");
|
||||
ASSERT_DEATH(v.GetRaw(), "type == NT_RAW");
|
||||
ASSERT_DEATH(v.GetBooleanArray(), "type == NT_BOOLEAN_ARRAY");
|
||||
ASSERT_DEATH(v.GetDoubleArray(), "type == NT_DOUBLE_ARRAY");
|
||||
ASSERT_DEATH(v.GetStringArray(), "type == NT_STRING_ARRAY");
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, UnassignedComparison) {
|
||||
Value v1, v2;
|
||||
ASSERT_EQ(v1, v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, MixedComparison) {
|
||||
Value v1;
|
||||
auto v2 = Value::MakeBoolean(true);
|
||||
ASSERT_NE(v1, *v2); // unassigned vs boolean
|
||||
auto v3 = Value::MakeDouble(0.5);
|
||||
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);
|
||||
v2 = Value::MakeBoolean(false);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, DoubleComparison) {
|
||||
auto v1 = Value::MakeDouble(0.25);
|
||||
auto v2 = Value::MakeDouble(0.25);
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
v2 = Value::MakeDouble(0.5);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, StringComparison) {
|
||||
auto v1 = Value::MakeString("hello");
|
||||
auto v2 = Value::MakeString("hello");
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
v2 = Value::MakeString("world"); // different contents
|
||||
ASSERT_NE(*v1, *v2);
|
||||
v2 = Value::MakeString("goodbye"); // different size
|
||||
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);
|
||||
|
||||
// different contents
|
||||
vec = {1, 1, 1};
|
||||
v2 = Value::MakeBooleanArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
|
||||
// different size
|
||||
vec = {1, 0};
|
||||
v2 = Value::MakeBooleanArray(vec);
|
||||
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);
|
||||
|
||||
// different contents
|
||||
vec = {0.5, 0.5, 0.5};
|
||||
v2 = Value::MakeDoubleArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
|
||||
// different size
|
||||
vec = {0.5, 0.25};
|
||||
v2 = Value::MakeDoubleArray(vec);
|
||||
ASSERT_NE(*v1, *v2);
|
||||
}
|
||||
|
||||
TEST_F(ValueTest, StringArrayComparison) {
|
||||
std::vector<std::string> vec;
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodbye");
|
||||
vec.push_back("string");
|
||||
auto v1 = Value::MakeStringArray(vec);
|
||||
vec.clear();
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodbye");
|
||||
vec.push_back("string");
|
||||
auto v2 = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_EQ(*v1, *v2);
|
||||
|
||||
// different contents
|
||||
vec.clear();
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodby2");
|
||||
vec.push_back("string");
|
||||
v2 = Value::MakeStringArray(std::move(vec));
|
||||
ASSERT_NE(*v1, *v2);
|
||||
|
||||
// different sized contents
|
||||
vec.clear();
|
||||
vec.push_back("hello");
|
||||
vec.push_back("goodbye2");
|
||||
vec.push_back("string");
|
||||
v2 = Value::MakeStringArray(vec);
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
643
ntcore/src/test/native/cpp/WireDecoderTest.cpp
Normal file
643
ntcore/src/test/native/cpp/WireDecoderTest.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "WireDecoder.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireDecoderTest : public ::testing::Test {
|
||||
protected:
|
||||
WireDecoderTest() {
|
||||
v_boolean = Value::MakeBoolean(true);
|
||||
v_double = Value::MakeDouble(1.0);
|
||||
v_string = Value::MakeString(llvm::StringRef("hello"));
|
||||
v_raw = Value::MakeRaw(llvm::StringRef("hello"));
|
||||
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(llvm::StringRef("bye"));
|
||||
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(llvm::StringRef("bye"));
|
||||
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(llvm::StringRef("bye"));
|
||||
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
|
||||
498
ntcore/src/test/native/cpp/WireEncoderTest.cpp
Normal file
498
ntcore/src/test/native/cpp/WireEncoderTest.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#include <llvm/StringRef.h>
|
||||
|
||||
#include "TestPrinters.h"
|
||||
#include "WireEncoder.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
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(llvm::StringRef("hello"));
|
||||
v_raw = Value::MakeRaw(llvm::StringRef("hello"));
|
||||
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(llvm::StringRef("\x05\x01\x00", 3),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\x00\x05\x00\x01\x45\x67\x00\x00", 8),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
|
||||
"\x12\x34\x56\x78\x00\x00\x00\x00",
|
||||
20),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\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),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\x00\x7f\x80\x01", 4),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\x00\x01\x02\x03\x10\x11\x12\x20", 8),
|
||||
llvm::StringRef(e.data(), e.size()).substr(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(llvm::StringRef("\x01\x00", 2),
|
||||
llvm::StringRef(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(llvm::StringRef("\x3f\xf0\x00\x00\x00\x00\x00\x00", 8),
|
||||
llvm::StringRef(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(llvm::StringRef("\x00\x05hello", 7),
|
||||
llvm::StringRef(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(llvm::StringRef("\x03\x00\x01\x00", 4),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x00", 2), llvm::StringRef(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(llvm::StringRef("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00",
|
||||
17),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x00", 2), llvm::StringRef(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(llvm::StringRef("\x02\x00\x05hello\x00\x07goodbye", 17),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x00\x01", 3), llvm::StringRef(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(llvm::StringRef("\x01\x00", 2),
|
||||
llvm::StringRef(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(llvm::StringRef("\x3f\xf0\x00\x00\x00\x00\x00\x00", 8),
|
||||
llvm::StringRef(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(llvm::StringRef("\x05hello", 6),
|
||||
llvm::StringRef(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(llvm::StringRef("\x05hello", 6),
|
||||
llvm::StringRef(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(llvm::StringRef("\x03\x00\x01\x00", 4),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x00", 2), llvm::StringRef(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(llvm::StringRef("\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00",
|
||||
17),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x00", 2), llvm::StringRef(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(llvm::StringRef("\x02\x05hello\x07goodbye", 15),
|
||||
llvm::StringRef(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(llvm::StringRef("\xff\x01", 2), llvm::StringRef(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(llvm::StringRef("\x00\x05hello", 7),
|
||||
llvm::StringRef(e.data(), e.size()));
|
||||
|
||||
e.Reset();
|
||||
e.WriteString(s_long);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(130u, e.size());
|
||||
EXPECT_EQ(llvm::StringRef("\x00\x80**", 4), llvm::StringRef(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(llvm::StringRef("\xff\xff**", 4), llvm::StringRef(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(llvm::StringRef("\x05hello", 6),
|
||||
llvm::StringRef(e.data(), e.size()));
|
||||
|
||||
e.Reset();
|
||||
e.WriteString(s_long);
|
||||
EXPECT_EQ(nullptr, e.error());
|
||||
ASSERT_EQ(130u, e.size());
|
||||
EXPECT_EQ(llvm::StringRef("\x80\x01**", 4), llvm::StringRef(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(llvm::StringRef("\x81\x80\x04*", 4), llvm::StringRef(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
|
||||
23
ntcore/src/test/native/cpp/main.cpp
Normal file
23
ntcore/src/test/native/cpp/main.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "ntcore.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
nt::AddLogger(nt::GetDefaultInstance(),
|
||||
[](const nt::LogMessage& msg) {
|
||||
std::fputs(msg.message.c_str(), stderr);
|
||||
std::fputc('\n', stderr);
|
||||
},
|
||||
0, UINT_MAX);
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user