Remove wpiutil and update to the new build system (#210)

This commit is contained in:
Thad House
2017-08-03 14:14:40 -07:00
committed by Peter Johnson
parent f43675e2bd
commit 5df7463663
168 changed files with 670 additions and 16084 deletions

View File

@@ -0,0 +1,27 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "networktables/NetworkTable.h"
#include "gtest/gtest.h"
class NetworkTableTest : public ::testing::Test {};
TEST_F(NetworkTableTest, ContainsKey) {
auto nt = NetworkTable::GetTable("containskey");
ASSERT_FALSE(nt->ContainsKey("testkey"));
nt->PutNumber("testkey", 5);
ASSERT_TRUE(nt->ContainsKey("testkey"));
}
TEST_F(NetworkTableTest, LeadingSlash) {
auto nt = NetworkTable::GetTable("leadingslash");
auto nt2 = NetworkTable::GetTable("/leadingslash");
ASSERT_FALSE(nt->ContainsKey("testkey"));
nt2->PutNumber("testkey", 5);
ASSERT_TRUE(nt->ContainsKey("testkey"));
}

View File

@@ -0,0 +1,873 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "Storage.h"
#include "StorageTest.h"
#include <sstream>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
namespace nt {
class StorageTestEmpty : public StorageTest,
public ::testing::TestWithParam<bool> {
public:
StorageTestEmpty() { HookOutgoing(GetParam()); }
};
class StorageTestPopulateOne : public StorageTestEmpty {
public:
StorageTestPopulateOne() {
storage.SetEntryTypeValue("foo", Value::MakeBoolean(true));
outgoing.clear();
}
};
class StorageTestPopulated : public StorageTestEmpty {
public:
StorageTestPopulated() {
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();
}
};
class StorageTestPersistent : public StorageTestEmpty {
public:
StorageTestPersistent() {
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));
outgoing.clear();
}
};
class MockLoadWarn {
public:
MOCK_METHOD2(Warn, void(std::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());
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, GetEntryValueExist) {
auto value = Value::MakeBoolean(true);
storage.SetEntryTypeValue("foo", value);
outgoing.clear();
EXPECT_EQ(value, storage.GetEntryValue("foo"));
}
TEST_P(StorageTestEmpty, SetEntryTypeValueAssignNew) {
// brand new entry
auto value = Value::MakeBoolean(true);
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());
}
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);
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) {
// 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);
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);
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
}
}
TEST_P(StorageTestEmpty, SetEntryTypeValueEmptyName) {
auto value = Value::MakeBoolean(true);
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);
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
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) {
// 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);
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);
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
}
}
TEST_P(StorageTestEmpty, SetEntryValueEmptyName) {
auto value = Value::MakeBoolean(true);
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);
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) {
// existing entry
auto value = Value::MakeBoolean(true);
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) {
// 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);
EXPECT_TRUE(outgoing.empty());
}
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());
EXPECT_TRUE(outgoing.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());
EXPECT_TRUE(outgoing.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());
EXPECT_TRUE(outgoing.empty());
}
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());
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) {
// 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);
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, SetEntryFlagsDifferentValue) {
// update with different value results in flags update message
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) {
storage.SetEntryFlags("foo", 1u);
outgoing.clear();
EXPECT_EQ(1u, storage.GetEntryFlags("foo"));
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestEmpty, DeleteEntryNotExist) {
storage.DeleteEntry("foo");
EXPECT_TRUE(outgoing.empty());
}
TEST_P(StorageTestPopulated, DeleteEntryExist) {
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());
}
TEST_P(StorageTestPopulated, DeleteAllEntries) {
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;
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);
ASSERT_EQ(4u, info.size());
}
TEST_P(StorageTestPopulated, GetEntryInfoPrefix) {
auto info = storage.GetEntryInfo("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("", 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("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) {
std::ostringstream oss;
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;
std::ostringstream oss;
storage.SavePersistent(oss, false);
std::string out = oss.str();
//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 =
[&](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_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_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); };
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); };
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); };
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(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); };
std::istringstream iss(
"[NetworkTables Storage 3.0]\ndouble \"foo2\"=0.0\n");
EXPECT_TRUE(storage.LoadPersistent(iss, warn_func));
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); };
GetEntry("foo2")->flags = NT_PERSISTENT;
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(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
}
}
TEST_P(StorageTestPopulated, LoadPersistentUpdateValueFlags) {
MockLoadWarn warn;
auto warn_func =
[&](std::size_t line, const char* msg) { warn.Warn(line, msg); };
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(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
}
}
TEST_P(StorageTestEmpty, LoadPersistent) {
MockLoadWarn warn;
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";
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";
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),
*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 =
[&](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_TRUE(storage.LoadPersistent(iss, warn_func));
EXPECT_TRUE(entries().empty());
EXPECT_TRUE(idmap().empty());
EXPECT_TRUE(outgoing.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

View File

@@ -0,0 +1,55 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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_STORAGETEST_H_
#define NT_TEST_STORAGETEST_H_
#include <functional>
#include <memory>
#include <vector>
#include "Storage.h"
namespace nt {
class StorageTest {
public:
StorageTest() : 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();
}
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 storage;
Storage::Entry tmp_entry;
std::vector<OutgoingData> outgoing;
};
} // namespace nt
#endif // NT_TEST_STORAGETEST_H_

View File

@@ -0,0 +1,365 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "nt_Value.h"
#include "Value_internal.h"
#include "gtest/gtest.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

View File

@@ -0,0 +1,602 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "WireDecoder.h"
#include "gtest/gtest.h"
#include <cfloat>
#include <climits>
#include <string>
#include "llvm/StringRef.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);
WireDecoder d(is, 0x0300u);
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);
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);
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);
WireDecoder d(is, 0x0300u);
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);
WireDecoder d(is, 0x0300u);
unsigned long 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);
WireDecoder d(is, 0x0300u);
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);
WireDecoder d(is, 0x0300u);
unsigned long 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);
WireDecoder d(is, 0x0300u);
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);
WireDecoder d(is, 0x0200u);
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);
WireDecoder d(is, 0x0200u);
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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
auto v_false = Value::MakeBoolean(false);
val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
auto v_bye = Value::MakeString(llvm::StringRef("bye"));
val = d.ReadValue(NT_STRING);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(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(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());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(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());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(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());
WireDecoder d(is, 0x0200u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_boolean, *val);
auto v_false = Value::MakeBoolean(false);
val = d.ReadValue(NT_BOOLEAN);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_double, *val);
val = d.ReadValue(NT_DOUBLE);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_string, *val);
auto v_bye = Value::MakeString(llvm::StringRef("bye"));
val = d.ReadValue(NT_STRING);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_RAW);
ASSERT_TRUE(bool(val));
EXPECT_EQ(*v_raw, *val);
auto v_bye = Value::MakeRaw(llvm::StringRef("bye"));
val = d.ReadValue(NT_RAW);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(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(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());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_BOOLEAN_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(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());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_DOUBLE_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(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());
WireDecoder d(is, 0x0300u);
auto val = d.ReadValue(NT_STRING_ARRAY);
ASSERT_TRUE(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);
WireDecoder d(is, 0x0200u);
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());
WireDecoder d(is, 0x0200u);
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());
WireDecoder d(is, 0x0300u);
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

View File

@@ -0,0 +1,499 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "WireEncoder.h"
#include "gtest/gtest.h"
#include <cfloat>
#include <climits>
#include <string>
#include "llvm/StringRef.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) {
std::size_t off = BUFSIZE-1;
WireEncoder e(0x0300u);
for(std::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) {
std::size_t off = BUFSIZE-2;
WireEncoder e(0x0300u);
for(std::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) {
std::size_t off = BUFSIZE-4;
WireEncoder e(0x0300u);
for(std::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) {
std::size_t off = BUFSIZE-8;
WireEncoder e(0x0300u);
for(std::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) {
std::size_t off = BUFSIZE-2;
WireEncoder e(0x0300u);
for(std::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) {
std::size_t off = BUFSIZE-1;
WireEncoder e(0x0300u);
for(std::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

View File

@@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. 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 "gtest/gtest.h"
#include "Log.h"
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;
}