Implement independent instances.

Previously, most of the classes were implemented as singletons so only one
instance was possible.

This change adds an instance handle-based API.  In Java, this API is located
in a different package than the old API (edu.wpi.first.networktables).

Backwards compatibility with ITable and the old NetworkTable API is largely
maintained, but a handful of classes have moved to the new package in Java
(ConnectionInfo and PersistentException), and the old JNI has been completed
replaced.

Also:
- Move SetTeam implementation to Dispatcher.
- Consistently pass time through Java and C++ Value API.
- Rename nt_Value.h to NetworkTableValue.h for consistency with Java.
- Improve documentation
- Make C++ and Java APIs more consistent
- Document RPC functions and support RPC in Java.
- Add polling features for entry and connection listeners and use them to
  move callback threads to Java level.
- Remove thread start and stop hooks (as polling is available).
- Make Notifiers, RpcServer, Dispatcher, and Storage mockable.
- Set NOTIFY_NEW on immediate entry notifications.
- Make GetTable("/") and GetTable("") equivalent.
- Generate local notification for flags update when loading persistent file.

And many unit test updates/changes:
- Use InitGoogleMock instead of InitGoogleTest in test main.
- Move test printers to TestPrinter.h/cpp.
- Provide printers for StringRef, EntryNotifier, and Handle.
- StorageTest: Check notifications.
- Add entry notifier unit tests.
- Storage: Add test for incoming entry assignment.
- Update connection listener tests.
- Add entry listener unit tests.

Fixes #11, #140, #189, #190, #192, #193, #221
This commit is contained in:
Peter Johnson
2017-04-23 10:26:17 -07:00
parent 8125a179fb
commit 5ab20bb27c
118 changed files with 15638 additions and 4640 deletions

View File

@@ -0,0 +1,109 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 "ntcore_cpp.h"
#include <chrono>
#include <thread>
#include "TestPrinters.h"
#include "gtest/gtest.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);
}

View File

@@ -0,0 +1,165 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 "ntcore_cpp.h"
#include <chrono>
#include <thread>
#include "TestPrinters.h"
#include "ValueMatcher.h"
#include "gtest/gtest.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);
}

View File

@@ -0,0 +1,316 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 "EntryNotifier.h"
#include "gtest/gtest.h"
#include "support/Logger.h"
#include "TestPrinters.h"
#include "ValueMatcher.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::IsNull;
using ::testing::Return;
namespace nt {
class EntryNotifierTest : public ::testing::Test {
public:
EntryNotifierTest() : notifier(1, logger) { notifier.Start(); }
void GenerateNotifications();
protected:
wpi::Logger logger;
EntryNotifier notifier;
};
void EntryNotifierTest::GenerateNotifications() {
// All flags combos that can be generated by Storage
static const unsigned int flags[] = {
// "normal" notifications
NT_NOTIFY_NEW, NT_NOTIFY_DELETE, NT_NOTIFY_UPDATE, NT_NOTIFY_FLAGS,
NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS,
// immediate notifications are always "new"
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_NEW,
// local notifications can be of any flag combo
NT_NOTIFY_LOCAL | NT_NOTIFY_NEW, NT_NOTIFY_LOCAL | NT_NOTIFY_DELETE,
NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE, NT_NOTIFY_LOCAL | NT_NOTIFY_FLAGS,
NT_NOTIFY_LOCAL | NT_NOTIFY_UPDATE | NT_NOTIFY_FLAGS};
// Generate across keys
static const char* keys[] = {"/foo/bar", "/baz", "/boo"};
auto val = Value::MakeDouble(1);
// Provide unique key indexes for each key
unsigned int keyindex = 5;
for (auto key : keys) {
for (auto flag : flags) {
notifier.NotifyEntry(keyindex, key, val, flag);
}
++keyindex;
}
}
TEST_F(EntryNotifierTest, PollEntryMultiple) {
auto poller1 = notifier.CreatePoller();
auto poller2 = notifier.CreatePoller();
auto poller3 = notifier.CreatePoller();
auto h1 = notifier.AddPolled(poller1, 6, NT_NOTIFY_NEW);
auto h2 = notifier.AddPolled(poller2, 6, NT_NOTIFY_NEW);
auto h3 = notifier.AddPolled(poller3, 6, NT_NOTIFY_UPDATE);
ASSERT_FALSE(notifier.local_notifiers());
GenerateNotifications();
ASSERT_TRUE(notifier.WaitForQueue(1.0));
bool timed_out = false;
auto results1 = notifier.Poll(poller1, 0, &timed_out);
ASSERT_FALSE(timed_out);
auto results2 = notifier.Poll(poller2, 0, &timed_out);
ASSERT_FALSE(timed_out);
auto results3 = notifier.Poll(poller3, 0, &timed_out);
ASSERT_FALSE(timed_out);
ASSERT_EQ(results1.size(), 2u);
for (const auto& result : results1) {
SCOPED_TRACE(::testing::PrintToString(result));
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h1);
}
ASSERT_EQ(results2.size(), 2u);
for (const auto& result : results2) {
SCOPED_TRACE(::testing::PrintToString(result));
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h2);
}
ASSERT_EQ(results3.size(), 2u);
for (const auto& result : results3) {
SCOPED_TRACE(::testing::PrintToString(result));
EXPECT_EQ(Handle{result.listener}.GetIndex(), (int)h3);
}
}
TEST_F(EntryNotifierTest, PollEntryBasic) {
auto poller = notifier.CreatePoller();
auto g1 = notifier.AddPolled(poller, 6, NT_NOTIFY_NEW);
auto g2 = notifier.AddPolled(poller, 6, NT_NOTIFY_DELETE);
auto g3 = notifier.AddPolled(poller, 6, NT_NOTIFY_UPDATE);
auto g4 = notifier.AddPolled(poller, 6, NT_NOTIFY_FLAGS);
ASSERT_FALSE(notifier.local_notifiers());
GenerateNotifications();
ASSERT_TRUE(notifier.WaitForQueue(1.0));
bool timed_out = false;
auto results = notifier.Poll(poller, 0, &timed_out);
ASSERT_FALSE(timed_out);
int g1count = 0;
int g2count = 0;
int g3count = 0;
int g4count = 0;
for (const auto& result : results) {
SCOPED_TRACE(::testing::PrintToString(result));
EXPECT_EQ(result.name, "/baz");
EXPECT_THAT(result.value, ValueEq(Value::MakeDouble(1)));
EXPECT_EQ(Handle{result.entry}.GetType(), Handle::kEntry);
EXPECT_EQ(Handle{result.entry}.GetInst(), 1);
EXPECT_EQ(Handle{result.entry}.GetIndex(), 6);
EXPECT_EQ(Handle{result.listener}.GetType(), Handle::kEntryListener);
EXPECT_EQ(Handle{result.listener}.GetInst(), 1);
if (Handle{result.listener}.GetIndex() == (int)g1) {
++g1count;
EXPECT_TRUE((result.flags & NT_NOTIFY_NEW) != 0);
} else if (Handle{result.listener}.GetIndex() == (int)g2) {
++g2count;
EXPECT_TRUE((result.flags & NT_NOTIFY_DELETE) != 0);
} else if (Handle{result.listener}.GetIndex() == (int)g3) {
++g3count;
EXPECT_TRUE((result.flags & NT_NOTIFY_UPDATE) != 0);
} else if (Handle{result.listener}.GetIndex() == (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() == (int)g1) {
++g1count;
EXPECT_TRUE((result.flags & NT_NOTIFY_NEW) != 0);
} else if (Handle{result.listener}.GetIndex() == (int)g2) {
++g2count;
EXPECT_TRUE((result.flags & NT_NOTIFY_DELETE) != 0);
} else if (Handle{result.listener}.GetIndex() == (int)g3) {
++g3count;
EXPECT_TRUE((result.flags & NT_NOTIFY_UPDATE) != 0);
} else if (Handle{result.listener}.GetIndex() == (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

View File

@@ -0,0 +1,52 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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

View File

@@ -0,0 +1,43 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_MESSAGEMATCHER_H_
#define NT_TEST_MESSAGEMATCHER_H_
#include <memory>
#include <ostream>
#include "gmock/gmock.h"
#include "TestPrinters.h"
#include "Message.h"
namespace nt {
class MessageMatcher
: public ::testing::MatcherInterface<std::shared_ptr<Message>> {
public:
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 // NT_TEST_MESSAGEMATCHER_H_

View File

@@ -0,0 +1,26 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_MOCKCONNECTIONNOTIFIER_H_
#define NT_TEST_MOCKCONNECTIONNOTIFIER_H_
#include "gmock/gmock.h"
#include "IConnectionNotifier.h"
namespace nt {
class MockConnectionNotifier : public IConnectionNotifier {
public:
MOCK_METHOD3(NotifyConnection,
void(bool connected, const ConnectionInfo& conn_info,
unsigned int only_listener));
};
} // namespace nt
#endif // NT_TEST_MOCKCONNECTIONNOTIFIER_H_

View File

@@ -0,0 +1,26 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_MOCKDISPATCHER_H_
#define NT_TEST_MOCKDISPATCHER_H_
#include "gmock/gmock.h"
#include "IDispatcher.h"
namespace nt {
class MockDispatcher : public IDispatcher {
public:
MOCK_METHOD3(QueueOutgoing,
void(std::shared_ptr<Message> msg, NetworkConnection* only,
NetworkConnection* except));
};
} // namespace nt
#endif // NT_TEST_MOCKDISPATCHER_H_

View File

@@ -0,0 +1,28 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_MOCKENTRYNOTIFIER_H_
#define NT_TEST_MOCKENTRYNOTIFIER_H_
#include "gmock/gmock.h"
#include "IEntryNotifier.h"
namespace nt {
class MockEntryNotifier : public IEntryNotifier {
public:
MOCK_CONST_METHOD0(local_notifiers, bool());
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 // NT_TEST_MOCKENTRYNOTIFIER_H_

View File

@@ -0,0 +1,30 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_MOCKRPCSERVER_H_
#define NT_TEST_MOCKRPCSERVER_H_
#include "gmock/gmock.h"
#include "IRpcServer.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 // NT_TEST_MOCKRPCSERVER_H_

View File

@@ -6,22 +6,39 @@
/*----------------------------------------------------------------------------*/
#include "networktables/NetworkTable.h"
#include "networktables/NetworkTableInstance.h"
#include "gtest/gtest.h"
#include "TestPrinters.h"
class NetworkTableTest : public ::testing::Test {};
TEST_F(NetworkTableTest, ContainsKey) {
auto nt = NetworkTable::GetTable("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 nt = NetworkTable::GetTable("leadingslash");
auto nt2 = NetworkTable::GetTable("/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());
}

View File

@@ -5,44 +5,81 @@
/* the project. */
/*----------------------------------------------------------------------------*/
#include "Storage.h"
#include "StorageTest.h"
#include "Storage.h"
#include <sstream>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "MessageMatcher.h"
#include "TestPrinters.h"
#include "ValueMatcher.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::IsNull;
using ::testing::Return;
namespace nt {
class StorageTestEmpty : public StorageTest,
public ::testing::TestWithParam<bool> {
public:
StorageTestEmpty() { HookOutgoing(GetParam()); }
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));
outgoing.clear();
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
::testing::Mock::VerifyAndClearExpectations(&notifier);
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));
outgoing.clear();
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
::testing::Mock::VerifyAndClearExpectations(&notifier);
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));
@@ -80,7 +117,11 @@ class StorageTestPersistent : public StorageTestEmpty {
storage.SetEntryTypeValue(StringRef("\0\3\5\n", 4),
Value::MakeBoolean(true));
storage.SetEntryTypeValue("=", Value::MakeBoolean(true));
outgoing.clear();
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
::testing::Mock::VerifyAndClearExpectations(&notifier);
EXPECT_CALL(notifier, local_notifiers())
.Times(AnyNumber())
.WillRepeatedly(Return(true));
}
};
@@ -107,19 +148,27 @@ TEST_P(StorageTestEmpty, GetEntryValueNotExist) {
EXPECT_FALSE(storage.GetEntryValue("foo"));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, GetEntryValueExist) {
auto value = Value::MakeBoolean(true);
EXPECT_CALL(dispatcher, QueueOutgoing(_, IsNull(), IsNull()));
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _));
storage.SetEntryTypeValue("foo", value);
outgoing.clear();
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()) {
@@ -128,41 +177,23 @@ TEST_P(StorageTestEmpty, SetEntryTypeValueAssignNew) {
} else {
EXPECT_TRUE(idmap().empty());
}
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(1u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
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);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetEntryTypeValueEqualValue) {
@@ -171,28 +202,28 @@ TEST_P(StorageTestPopulateOne, SetEntryTypeValueEqualValue) {
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("foo", value);
EXPECT_EQ(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
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()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
if (!GetParam()) {
// seq_num should still be incremented
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
}
}
@@ -201,44 +232,36 @@ TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyName) {
storage.SetEntryTypeValue("", value);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyValue) {
storage.SetEntryTypeValue("foo", nullptr);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.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);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(0u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetEntryValueAssignTypeChange) {
// update with different type results in error and no message
// 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);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetEntryValueEqualValue) {
@@ -248,29 +271,30 @@ TEST_P(StorageTestPopulateOne, SetEntryValueEqualValue) {
EXPECT_TRUE(storage.SetEntryValue("foo", value));
auto entry = GetEntry("foo");
EXPECT_EQ(value, entry->value);
EXPECT_TRUE(outgoing.empty());
}
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()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(value, msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
if (!GetParam()) {
// seq_num should still be incremented
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value());
}
}
@@ -279,36 +303,29 @@ TEST_P(StorageTestEmpty, SetEntryValueEmptyName) {
EXPECT_TRUE(storage.SetEntryValue("", value));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryValueEmptyValue) {
EXPECT_TRUE(storage.SetEntryValue("foo", nullptr));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.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);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(0u, msg->seq_num_uid());
EXPECT_EQ(value, msg->value());
EXPECT_EQ(0u, msg->flags());
}
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsSameType) {
@@ -317,8 +334,6 @@ TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsSameType) {
auto ret_val = storage.SetDefaultEntryValue("foo", value);
EXPECT_TRUE(ret_val);
EXPECT_NE(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsDifferentType) {
@@ -328,8 +343,6 @@ TEST_P(StorageTestPopulateOne, SetDefaultEntryExistsDifferentType) {
EXPECT_FALSE(ret_val);
// should not have updated value in table if it already existed.
EXPECT_NE(value, GetEntry("foo")->value);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyName) {
@@ -344,7 +357,6 @@ TEST_P(StorageTestEmpty, SetDefaultEntryEmptyName) {
EXPECT_EQ(SequenceNumber(), entry->seq_num);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetDefaultEntryEmptyValue) {
@@ -359,7 +371,6 @@ TEST_P(StorageTestEmpty, SetDefaultEntryEmptyValue) {
EXPECT_EQ(SequenceNumber(), entry->seq_num);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyName) {
@@ -372,7 +383,6 @@ TEST_P(StorageTestPopulated, SetDefaultEntryEmptyName) {
EXPECT_EQ(4u, idmap().size());
else
EXPECT_EQ(0u, idmap().size());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetDefaultEntryEmptyValue) {
@@ -385,16 +395,13 @@ TEST_P(StorageTestPopulated, SetDefaultEntryEmptyValue) {
EXPECT_EQ(4u, idmap().size());
else
EXPECT_EQ(0u, idmap().size());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, SetEntryFlagsNew) {
// flags setting doesn't create an entry
storage.SetEntryFlags("foo", 0u);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, SetEntryFlagsEqualValue) {
@@ -403,111 +410,101 @@ TEST_P(StorageTestPopulateOne, SetEntryFlagsEqualValue) {
storage.SetEntryFlags("foo", 0u);
auto entry = GetEntry("foo");
EXPECT_EQ(0u, entry->flags);
EXPECT_TRUE(outgoing.empty());
}
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);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(1u, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestEmpty, SetEntryFlagsEmptyName) {
storage.SetEntryFlags("", 0u);
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, GetEntryFlagsNotExist) {
EXPECT_EQ(0u, storage.GetEntryFlags("foo"));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulateOne, GetEntryFlagsExist) {
EXPECT_CALL(dispatcher, QueueOutgoing(_, _, _)).Times(AnyNumber());
EXPECT_CALL(notifier, NotifyEntry(_, _, _, _, _));
storage.SetEntryFlags("foo", 1u);
outgoing.clear();
::testing::Mock::VerifyAndClearExpectations(&dispatcher);
EXPECT_EQ(1u, storage.GetEntryFlags("foo"));
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, DeleteEntryNotExist) {
storage.DeleteEntry("foo");
EXPECT_TRUE(outgoing.empty());
}
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");
EXPECT_TRUE(entries().count("foo2") == 0);
if (GetParam()) {
ASSERT_TRUE(idmap().size() >= 2);
EXPECT_FALSE(idmap()[1]);
}
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryDelete, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestEmpty, DeleteAllEntriesEmpty) {
storage.DeleteAllEntries();
EXPECT_TRUE(outgoing.empty());
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_TRUE(entries().empty());
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kClearEntries, msg->type());
}
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().size());
EXPECT_EQ(1u, entries().count("foo2"));
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kClearEntries, msg->type());
}
TEST_P(StorageTestPopulated, GetEntryInfoAll) {
auto info = storage.GetEntryInfo("", 0u);
auto info = storage.GetEntryInfo(0, "", 0u);
ASSERT_EQ(4u, info.size());
}
TEST_P(StorageTestPopulated, GetEntryInfoPrefix) {
auto info = storage.GetEntryInfo("foo", 0u);
auto info = storage.GetEntryInfo(0, "foo", 0u);
ASSERT_EQ(2u, info.size());
if (info[0].name == "foo") {
EXPECT_EQ("foo", info[0].name);
@@ -523,7 +520,7 @@ TEST_P(StorageTestPopulated, GetEntryInfoPrefix) {
}
TEST_P(StorageTestPopulated, GetEntryInfoTypes) {
auto info = storage.GetEntryInfo("", NT_DOUBLE);
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);
@@ -537,7 +534,7 @@ TEST_P(StorageTestPopulated, GetEntryInfoTypes) {
}
TEST_P(StorageTestPopulated, GetEntryInfoPrefixTypes) {
auto info = storage.GetEntryInfo("bar", NT_BOOLEAN);
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);
@@ -612,78 +609,92 @@ TEST_P(StorageTestPersistent, SavePersistent) {
TEST_P(StorageTestEmpty, LoadPersistentBadHeader) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::size_t line, const char* msg) {
warn.Warn(line, msg);
};
std::istringstream iss("");
EXPECT_CALL(warn, Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_CALL(
warn,
Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_FALSE(storage.LoadPersistent(iss, warn_func));
std::istringstream iss2("[NetworkTables");
EXPECT_CALL(warn, Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_CALL(
warn,
Warn(1, llvm::StringRef("header line mismatch, ignoring rest of file")));
EXPECT_FALSE(storage.LoadPersistent(iss2, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentCommentHeader) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::size_t line, const char* msg) {
warn.Warn(line, msg);
};
std::istringstream iss(
"\n; comment\n# comment\n[NetworkTables Storage 3.0]\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentEmptyName) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::size_t line, const char* msg) {
warn.Warn(line, msg);
};
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"\"=true\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, LoadPersistentAssign) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::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));
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"foo\"=true\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo");
EXPECT_EQ(*Value::MakeBoolean(true), *entry->value);
EXPECT_EQ(*value, *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryAssign, msg->type());
EXPECT_EQ("foo", msg->str());
if (GetParam())
EXPECT_EQ(0u, msg->id()); // assigned as server
else
EXPECT_EQ(0xffffu, msg->id()); // not assigned as client
EXPECT_EQ(1u, msg->seq_num_uid());
EXPECT_EQ(*Value::MakeBoolean(true), *msg->value());
EXPECT_EQ(NT_PERSISTENT, msg->flags());
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateFlags) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::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));
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=0.0\n");
@@ -691,90 +702,83 @@ TEST_P(StorageTestPopulated, LoadPersistentUpdateFlags) {
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(0.0), *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(NT_PERSISTENT, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
}
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateValue) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::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));
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(1.0), *entry->value);
EXPECT_EQ(*value, *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(1u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(*Value::MakeDouble(1.0), *msg->value());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
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 =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::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));
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=1.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
auto entry = GetEntry("foo2");
EXPECT_EQ(*Value::MakeDouble(1.0), *entry->value);
EXPECT_EQ(*value, *entry->value);
EXPECT_EQ(NT_PERSISTENT, entry->flags);
if (GetParam()) {
ASSERT_EQ(2u, outgoing.size());
EXPECT_FALSE(outgoing[0].only);
EXPECT_FALSE(outgoing[0].except);
auto msg = outgoing[0].msg;
EXPECT_EQ(Message::kEntryUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(2u, msg->seq_num_uid()); // incremented
EXPECT_EQ(*Value::MakeDouble(1.0), *msg->value());
EXPECT_FALSE(outgoing[1].only);
EXPECT_FALSE(outgoing[1].except);
msg = outgoing[1].msg;
EXPECT_EQ(Message::kFlagsUpdate, msg->type());
EXPECT_EQ(1u, msg->id()); // assigned as server
EXPECT_EQ(NT_PERSISTENT, msg->flags());
} else {
// shouldn't send an update id not assigned yet (happens on client only)
EXPECT_TRUE(outgoing.empty());
EXPECT_EQ(2u, GetEntry("foo2")->seq_num.value()); // still should be incremented
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 =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::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";
@@ -800,10 +804,14 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
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);
std::istringstream iss(in);
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
ASSERT_EQ(22u, entries().size());
EXPECT_EQ(22u, outgoing.size());
EXPECT_EQ(*Value::MakeBoolean(true), *storage.GetEntryValue("boolean/true"));
EXPECT_EQ(*Value::MakeBoolean(false),
@@ -816,10 +824,8 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
*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(""), *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>{}),
@@ -848,18 +854,37 @@ TEST_P(StorageTestEmpty, LoadPersistent) {
TEST_P(StorageTestEmpty, LoadPersistentWarn) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
auto warn_func = [&](std::size_t line, const char* msg) {
warn.Warn(line, msg);
};
std::istringstream iss(
"[NetworkTables Storage 3.0]\nboolean \"foo\"=foo\n");
EXPECT_CALL(warn,
Warn(2, llvm::StringRef("unrecognized boolean value, not 'true' or 'false'")));
EXPECT_CALL(
warn, Warn(2, llvm::StringRef(
"unrecognized boolean value, not 'true' or 'false'")));
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, ProcessIncomingEntryAssign) {
std::shared_ptr<NetworkConnection> conn;
auto value = Value::MakeDouble(1.0);
if (GetParam()) {
// id assign message reply generated on the server
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);
}
INSTANTIATE_TEST_CASE_P(StorageTestsEmpty, StorageTestEmpty,

View File

@@ -12,42 +12,37 @@
#include <memory>
#include <vector>
#include "Log.h"
#include "Storage.h"
#include "MockDispatcher.h"
#include "MockEntryNotifier.h"
#include "MockRpcServer.h"
namespace nt {
class StorageTest {
public:
StorageTest() : tmp_entry("foobar") {}
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().get();
return i == storage.m_entries.end() ? &tmp_entry : i->getValue();
}
void HookOutgoing(bool server) {
using namespace std::placeholders;
storage.SetOutgoing(
std::bind(&StorageTest::QueueOutgoing, this, _1, _2, _3), server);
}
struct OutgoingData {
std::shared_ptr<Message> msg;
NetworkConnection* only;
NetworkConnection* except;
};
void QueueOutgoing(std::shared_ptr<Message> msg, NetworkConnection* only,
NetworkConnection* except) {
outgoing.emplace_back(OutgoingData{msg, only, except});
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;
std::vector<OutgoingData> outgoing;
};
} // namespace nt

View File

@@ -0,0 +1,157 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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

View File

@@ -0,0 +1,54 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_TESTPRINTERS_H_
#define NT_TEST_TESTPRINTERS_H_
#include <memory>
#include <ostream>
#include "gtest/gtest.h"
#include "llvm/StringRef.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 // NT_TEST_TESTPRINTERS_H_

View File

@@ -0,0 +1,33 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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

View File

@@ -0,0 +1,41 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2017. 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 NT_TEST_VALUEMATCHER_H_
#define NT_TEST_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:
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 // NT_TEST_VALUEMATCHER_H_

View File

@@ -5,10 +5,11 @@
/* the project. */
/*----------------------------------------------------------------------------*/
#include "nt_Value.h"
#include "networktables/NetworkTableValue.h"
#include "Value_internal.h"
#include "gtest/gtest.h"
#include "TestPrinters.h"
namespace nt {

View File

@@ -8,6 +8,7 @@
#include "WireDecoder.h"
#include "gtest/gtest.h"
#include "TestPrinters.h"
#include <cfloat>
#include <climits>
@@ -64,21 +65,24 @@ class WireDecoderTest : public ::testing::Test {
TEST_F(WireDecoderTest, Construct) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0300u);
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);
WireDecoder d(is, 0x0300u);
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);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
unsigned int val;
ASSERT_TRUE(d.Read8(&val));
EXPECT_EQ(5u, val);
@@ -92,7 +96,8 @@ TEST_F(WireDecoderTest, Read8) {
TEST_F(WireDecoderTest, Read16) {
wpi::raw_mem_istream is("\x00\x05\x00\x01\x45\x67\x00\x00", 8);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
unsigned int val;
ASSERT_TRUE(d.Read16(&val));
EXPECT_EQ(5u, val);
@@ -111,7 +116,8 @@ TEST_F(WireDecoderTest, Read32) {
"\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\xab\xcd"
"\x12\x34\x56\x78\x00\x00\x00\x00",
20);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
unsigned long val;
ASSERT_TRUE(d.Read32(&val));
EXPECT_EQ(5ul, val);
@@ -137,7 +143,8 @@ TEST_F(WireDecoderTest, ReadDouble) {
"\x00\x10\x00\x00\x00\x00\x00\x00"
"\x7f\xef\xff\xff\xff\xff\xff\xff",
40);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
double val;
ASSERT_TRUE(d.ReadDouble(&val));
EXPECT_EQ(0.0, val);
@@ -155,7 +162,8 @@ TEST_F(WireDecoderTest, ReadDouble) {
TEST_F(WireDecoderTest, ReadUleb128) {
wpi::raw_mem_istream is("\x00\x7f\x80\x01\x80", 5);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
unsigned long val;
ASSERT_TRUE(d.ReadUleb128(&val));
EXPECT_EQ(0ul, val);
@@ -169,7 +177,8 @@ TEST_F(WireDecoderTest, ReadUleb128) {
TEST_F(WireDecoderTest, ReadType) {
wpi::raw_mem_istream is("\x00\x01\x02\x03\x10\x11\x12\x20", 8);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
NT_Type val;
ASSERT_TRUE(d.ReadType(&val));
EXPECT_EQ(NT_BOOLEAN, val);
@@ -193,7 +202,8 @@ TEST_F(WireDecoderTest, ReadType) {
TEST_F(WireDecoderTest, ReadTypeError) {
wpi::raw_mem_istream is("\x30", 1);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
NT_Type val;
ASSERT_FALSE(d.ReadType(&val));
EXPECT_EQ(NT_UNASSIGNED, val);
@@ -202,7 +212,8 @@ TEST_F(WireDecoderTest, ReadTypeError) {
TEST_F(WireDecoderTest, Reset) {
wpi::raw_mem_istream is("\x30", 1);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
NT_Type val;
ASSERT_FALSE(d.ReadType(&val));
EXPECT_EQ(NT_UNASSIGNED, val);
@@ -213,7 +224,8 @@ TEST_F(WireDecoderTest, Reset) {
TEST_F(WireDecoderTest, ReadBooleanValue2) {
wpi::raw_mem_istream is("\x01\x00", 2);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
@@ -232,7 +244,8 @@ TEST_F(WireDecoderTest, ReadDoubleValue2) {
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
16);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
@@ -247,7 +260,8 @@ TEST_F(WireDecoderTest, ReadDoubleValue2) {
TEST_F(WireDecoderTest, ReadStringValue2) {
wpi::raw_mem_istream is("\x00\x05hello\x00\x03" "bye\x55", 13);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
@@ -267,7 +281,8 @@ TEST_F(WireDecoderTest, ReadStringValue2) {
TEST_F(WireDecoderTest, ReadBooleanArrayValue2) {
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array, *val);
@@ -286,7 +301,8 @@ TEST_F(WireDecoderTest, ReadBooleanArrayBigValue2) {
s.push_back('\xff');
s.append(255, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array_big, *val);
@@ -300,7 +316,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayValue2) {
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
18);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array, *val);
@@ -318,7 +335,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayBigValue2) {
s.push_back('\xff');
s.append(255*8, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array_big, *val);
@@ -329,7 +347,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayBigValue2) {
TEST_F(WireDecoderTest, ReadStringArrayValue2) {
wpi::raw_mem_istream is("\x02\x00\x05hello\x00\x07goodbye\x55", 18);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array, *val);
@@ -348,7 +367,8 @@ TEST_F(WireDecoderTest, ReadStringArrayBigValue2) {
for (int i=0; i<255; ++i)
s.append("\x00\x01h", 3);
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array_big, *val);
@@ -359,7 +379,8 @@ TEST_F(WireDecoderTest, ReadStringArrayBigValue2) {
TEST_F(WireDecoderTest, ReadValueError2) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
ASSERT_NE(nullptr, d.error());
@@ -374,7 +395,8 @@ TEST_F(WireDecoderTest, ReadValueError2) {
TEST_F(WireDecoderTest, ReadBooleanValue3) {
wpi::raw_mem_istream is("\x01\x00", 2);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
@@ -393,7 +415,8 @@ TEST_F(WireDecoderTest, ReadDoubleValue3) {
"\x3f\xf0\x00\x00\x00\x00\x00\x00"
"\x3f\xf0\x00\x00\x00\x00\x00\x00",
16);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
@@ -408,7 +431,8 @@ TEST_F(WireDecoderTest, ReadDoubleValue3) {
TEST_F(WireDecoderTest, ReadStringValue3) {
wpi::raw_mem_istream is("\x05hello\x03" "bye\x55", 11);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
@@ -428,7 +452,8 @@ TEST_F(WireDecoderTest, ReadStringValue3) {
TEST_F(WireDecoderTest, ReadRawValue3) {
wpi::raw_mem_istream is("\x05hello\x03" "bye\x55", 11);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_RAW);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_raw, *val);
@@ -448,7 +473,8 @@ TEST_F(WireDecoderTest, ReadRawValue3) {
TEST_F(WireDecoderTest, ReadBooleanArrayValue3) {
wpi::raw_mem_istream is("\x03\x00\x01\x00\x02\x01\x00\xff", 8);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array, *val);
@@ -467,7 +493,8 @@ TEST_F(WireDecoderTest, ReadBooleanArrayBigValue3) {
s.push_back('\xff');
s.append(255, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean_array_big, *val);
@@ -481,7 +508,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayValue3) {
"\x02\x3f\xe0\x00\x00\x00\x00\x00\x00"
"\x3f\xd0\x00\x00\x00\x00\x00\x00\x55",
18);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array, *val);
@@ -499,7 +527,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayBigValue3) {
s.push_back('\xff');
s.append(255*8, '\x00');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double_array_big, *val);
@@ -510,7 +539,8 @@ TEST_F(WireDecoderTest, ReadDoubleArrayBigValue3) {
TEST_F(WireDecoderTest, ReadStringArrayValue3) {
wpi::raw_mem_istream is("\x02\x05hello\x07goodbye\x55", 16);
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array, *val);
@@ -529,7 +559,8 @@ TEST_F(WireDecoderTest, ReadStringArrayBigValue3) {
for (int i=0; i<255; ++i)
s.append("\x01h", 2);
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string_array_big, *val);
@@ -540,7 +571,8 @@ TEST_F(WireDecoderTest, ReadStringArrayBigValue3) {
TEST_F(WireDecoderTest, ReadValueError3) {
wpi::raw_mem_istream is("", 0);
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
ASSERT_FALSE(d.ReadValue(NT_UNASSIGNED)); // unassigned
ASSERT_NE(nullptr, d.error());
}
@@ -555,7 +587,8 @@ TEST_F(WireDecoderTest, ReadString2) {
s += s_big2;
s.push_back('\x55');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0200u);
wpi::Logger logger;
WireDecoder d(is, 0x0200u, logger);
std::string outs;
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_normal, outs);
@@ -582,7 +615,8 @@ TEST_F(WireDecoderTest, ReadString3) {
s += s_big3;
s.push_back('\x55');
wpi::raw_mem_istream is(s.data(), s.size());
WireDecoder d(is, 0x0300u);
wpi::Logger logger;
WireDecoder d(is, 0x0300u, logger);
std::string outs;
ASSERT_TRUE(d.ReadString(&outs));
EXPECT_EQ(s_normal, outs);

View File

@@ -8,6 +8,7 @@
#include "WireEncoder.h"
#include "gtest/gtest.h"
#include "TestPrinters.h"
#include <cfloat>
#include <climits>

View File

@@ -5,20 +5,20 @@
/* the project. */
/*----------------------------------------------------------------------------*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "Log.h"
#include <climits>
int main(int argc, char **argv)
{
nt::Logger::GetInstance().SetLogger(
[](unsigned int level, const char* file, unsigned int line,
const char* msg) {
std::fputs(msg, stderr);
std::fputc('\n', stderr);
});
nt::Logger::GetInstance().set_min_level(0);
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
#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;
}