mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-03 03:01:44 +00:00
[ntcore] Remove NT3 support (#7625)
- Remove StartClient3 - Rename StartClient4 to StartClient - Remove port3 parameter from StartServer - Remove 3-suffix constants - Remove 4 suffix from constants Also remove Shuffleboard build from CI.
This commit is contained in:
@@ -39,8 +39,8 @@ class ConnectionListenerTest {
|
||||
|
||||
/** Connect to the server. */
|
||||
private void connect(int port) {
|
||||
m_serverInst.startServer("connectionlistenertest.json", "127.0.0.1", 0, port);
|
||||
m_clientInst.startClient4("client");
|
||||
m_serverInst.startServer("connectionlistenertest.json", "127.0.0.1", port);
|
||||
m_clientInst.startClient("client");
|
||||
m_clientInst.setServer("127.0.0.1", port);
|
||||
|
||||
// wait for client to report it's connected, then wait another 0.1 sec
|
||||
@@ -113,7 +113,7 @@ class ConnectionListenerTest {
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"127.0.0.1", "127.0.0.1 ", " 127.0.0.1 "})
|
||||
void testThreaded(String address) {
|
||||
m_serverInst.startServer("connectionlistenertest.json", address, 0, threadedPort);
|
||||
m_serverInst.startServer("connectionlistenertest.json", address, threadedPort);
|
||||
List<NetworkTableEvent> events = new ArrayList<>();
|
||||
final int handle =
|
||||
m_serverInst.addConnectionListener(
|
||||
@@ -125,7 +125,7 @@ class ConnectionListenerTest {
|
||||
});
|
||||
|
||||
// trigger a connect event
|
||||
m_clientInst.startClient4("client");
|
||||
m_clientInst.startClient("client");
|
||||
m_clientInst.setServer(address, threadedPort);
|
||||
threadedPort++;
|
||||
|
||||
|
||||
@@ -31,13 +31,13 @@ class LoggerTest {
|
||||
List<NetworkTableEvent> msgs = new ArrayList<>();
|
||||
m_clientInst.addLogger(LogMessage.kInfo, 100, msgs::add);
|
||||
|
||||
m_clientInst.startClient4("client");
|
||||
m_clientInst.startClient("client");
|
||||
m_clientInst.setServer("127.0.0.1", 10000);
|
||||
|
||||
// wait for client to report it's started, then wait another 0.1 sec
|
||||
try {
|
||||
int count = 0;
|
||||
while (!m_clientInst.getNetworkMode().contains(NetworkTableInstance.NetworkMode.kClient4)) {
|
||||
while (!m_clientInst.getNetworkMode().contains(NetworkTableInstance.NetworkMode.kClient)) {
|
||||
Thread.sleep(100);
|
||||
count++;
|
||||
if (count > 30) {
|
||||
|
||||
@@ -37,7 +37,7 @@ class TimeSyncTest {
|
||||
try (var poller = new NetworkTableListenerPoller(m_inst)) {
|
||||
poller.addTimeSyncListener(false);
|
||||
|
||||
m_inst.startServer("timesynctest.json", "127.0.0.1", 0, 10030);
|
||||
m_inst.startServer("timesynctest.json", "127.0.0.1", 10030);
|
||||
var offset = m_inst.getServerTimeOffset();
|
||||
assertTrue(offset.isPresent());
|
||||
assertEquals(0L, offset.getAsLong());
|
||||
@@ -61,19 +61,8 @@ class TimeSyncTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClient3() {
|
||||
m_inst.startClient3("client");
|
||||
var offset = m_inst.getServerTimeOffset();
|
||||
assertFalse(offset.isPresent());
|
||||
|
||||
m_inst.stopClient();
|
||||
offset = m_inst.getServerTimeOffset();
|
||||
assertFalse(offset.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClient4() {
|
||||
m_inst.startClient4("client");
|
||||
void testClient() {
|
||||
m_inst.startClient("client");
|
||||
var offset = m_inst.getServerTimeOffset();
|
||||
assertFalse(offset.isPresent());
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ class TopicListenerTest {
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
m_serverInst.startServer("topiclistenertest.json", "127.0.0.1", 0, 10010);
|
||||
m_clientInst.startClient4("client");
|
||||
m_serverInst.startServer("topiclistenertest.json", "127.0.0.1", 10010);
|
||||
m_clientInst.startClient("client");
|
||||
m_clientInst.setServer("127.0.0.1", 10010);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
|
||||
@@ -23,18 +23,16 @@ class ConnectionListenerTest : public ::testing::Test {
|
||||
nt::DestroyInstance(client_inst);
|
||||
}
|
||||
|
||||
void Connect(const char* address, unsigned int port3, unsigned int port4);
|
||||
void Connect(const char* address, unsigned int port4);
|
||||
|
||||
protected:
|
||||
NT_Inst server_inst;
|
||||
NT_Inst client_inst;
|
||||
};
|
||||
|
||||
void ConnectionListenerTest::Connect(const char* address, unsigned int port3,
|
||||
unsigned int port4) {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", address, port3,
|
||||
port4);
|
||||
nt::StartClient4(client_inst, "client");
|
||||
void ConnectionListenerTest::Connect(const char* address, unsigned int port4) {
|
||||
nt::StartServer(server_inst, "connectionlistenertest.ini", address, port4);
|
||||
nt::StartClient(client_inst, "client");
|
||||
nt::SetServer(client_inst, address, port4);
|
||||
|
||||
// wait for client to report it's connected, then wait another 0.1 sec
|
||||
@@ -57,7 +55,7 @@ TEST_F(ConnectionListenerTest, Polled) {
|
||||
ASSERT_NE(handle, 0u);
|
||||
|
||||
// trigger a connect event
|
||||
Connect("127.0.0.1", 0, 10020);
|
||||
Connect("127.0.0.1", 10020);
|
||||
|
||||
// get the event
|
||||
bool timed_out = false;
|
||||
@@ -98,7 +96,7 @@ TEST_P(ConnectionListenerVariantTest, Threaded) {
|
||||
});
|
||||
|
||||
// trigger a connect event
|
||||
Connect(GetParam().first, 0, 20001 + GetParam().second);
|
||||
Connect(GetParam().first, 20001 + GetParam().second);
|
||||
|
||||
bool timed_out = false;
|
||||
ASSERT_TRUE(wpi::WaitForObject(handle, 1.0, &timed_out));
|
||||
|
||||
@@ -27,7 +27,7 @@ class LoggerTest : public ::testing::Test {
|
||||
|
||||
void LoggerTest::Generate() {
|
||||
// generate info message
|
||||
nt::StartClient4(m_inst, "");
|
||||
nt::StartClient(m_inst, "");
|
||||
|
||||
// generate error message
|
||||
nt::Publish(nt::Handle(nt::Handle{m_inst}.GetInst(), 5, nt::Handle::kTopic),
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "Handle.h"
|
||||
#include "PubSubOptions.h"
|
||||
#include "net/Message.h"
|
||||
#include "net3/Message3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
@@ -54,59 +53,6 @@ void PrintTo(const Handle& handle, std::ostream* os) {
|
||||
*os << ", " << handle.GetInst() << ", " << handle.GetIndex() << '}';
|
||||
}
|
||||
|
||||
void PrintTo(const net3::Message3& msg, std::ostream* os) {
|
||||
*os << "Message{";
|
||||
switch (msg.type()) {
|
||||
case net3::Message3::kKeepAlive:
|
||||
*os << "kKeepAlive";
|
||||
break;
|
||||
case net3::Message3::kClientHello:
|
||||
*os << "kClientHello";
|
||||
break;
|
||||
case net3::Message3::kProtoUnsup:
|
||||
*os << "kProtoUnsup";
|
||||
break;
|
||||
case net3::Message3::kServerHelloDone:
|
||||
*os << "kServerHelloDone";
|
||||
break;
|
||||
case net3::Message3::kServerHello:
|
||||
*os << "kServerHello";
|
||||
break;
|
||||
case net3::Message3::kClientHelloDone:
|
||||
*os << "kClientHelloDone";
|
||||
break;
|
||||
case net3::Message3::kEntryAssign:
|
||||
*os << "kEntryAssign";
|
||||
break;
|
||||
case net3::Message3::kEntryUpdate:
|
||||
*os << "kEntryUpdate";
|
||||
break;
|
||||
case net3::Message3::kFlagsUpdate:
|
||||
*os << "kFlagsUpdate";
|
||||
break;
|
||||
case net3::Message3::kEntryDelete:
|
||||
*os << "kEntryDelete";
|
||||
break;
|
||||
case net3::Message3::kClearEntries:
|
||||
*os << "kClearEntries";
|
||||
break;
|
||||
case net3::Message3::kExecuteRpc:
|
||||
*os << "kExecuteRpc";
|
||||
break;
|
||||
case net3::Message3::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()) {
|
||||
|
||||
@@ -26,7 +26,7 @@ TEST_F(TimeSyncTest, TestServer) {
|
||||
nt::NetworkTableListenerPoller poller{m_inst};
|
||||
poller.AddTimeSyncListener(false);
|
||||
|
||||
m_inst.StartServer("timesynctest.json", "127.0.0.1", 0, 10030);
|
||||
m_inst.StartServer("timesynctest.json", "127.0.0.1", 10030);
|
||||
auto offset = m_inst.GetServerTimeOffset();
|
||||
ASSERT_TRUE(offset);
|
||||
ASSERT_EQ(0, *offset);
|
||||
@@ -50,18 +50,8 @@ TEST_F(TimeSyncTest, TestServer) {
|
||||
ASSERT_FALSE(data->valid);
|
||||
}
|
||||
|
||||
TEST_F(TimeSyncTest, TestClient3) {
|
||||
m_inst.StartClient3("client");
|
||||
auto offset = m_inst.GetServerTimeOffset();
|
||||
ASSERT_FALSE(offset);
|
||||
|
||||
m_inst.StopClient();
|
||||
offset = m_inst.GetServerTimeOffset();
|
||||
ASSERT_FALSE(offset);
|
||||
}
|
||||
|
||||
TEST_F(TimeSyncTest, TestClient4) {
|
||||
m_inst.StartClient4("client");
|
||||
TEST_F(TimeSyncTest, TestClient) {
|
||||
m_inst.StartClient("client");
|
||||
auto offset = m_inst.GetServerTimeOffset();
|
||||
ASSERT_FALSE(offset);
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ class TopicListenerTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
void TopicListenerTest::Connect(unsigned int port) {
|
||||
nt::StartServer(m_serverInst, "topiclistenertest.json", "127.0.0.1", 0, port);
|
||||
nt::StartClient4(m_clientInst, "client");
|
||||
nt::StartServer(m_serverInst, "topiclistenertest.json", "127.0.0.1", port);
|
||||
nt::StartClient(m_clientInst, "client");
|
||||
nt::SetServer(m_clientInst, "127.0.0.1", port);
|
||||
|
||||
// Use connection listener to ensure we've connected
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "MessageMatcher3.h"
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
bool MessageMatcher::MatchAndExplain(
|
||||
Message3 msg, ::testing::MatchResultListener* listener) const {
|
||||
bool match = true;
|
||||
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::net3
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
#include "../TestPrinters.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "net3/Message3.h"
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
class MessageMatcher : public ::testing::MatcherInterface<Message3> {
|
||||
public:
|
||||
explicit MessageMatcher(Message3 goodmsg_) : goodmsg(std::move(goodmsg_)) {}
|
||||
|
||||
bool MatchAndExplain(Message3 msg,
|
||||
::testing::MatchResultListener* listener) const override;
|
||||
void DescribeTo(::std::ostream* os) const override;
|
||||
void DescribeNegationTo(::std::ostream* os) const override;
|
||||
|
||||
private:
|
||||
Message3 goodmsg;
|
||||
};
|
||||
|
||||
inline ::testing::Matcher<Message3> MessageEq(Message3 goodmsg) {
|
||||
return ::testing::MakeMatcher(new MessageMatcher(std::move(goodmsg)));
|
||||
}
|
||||
|
||||
} // namespace nt::net3
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "net3/WireConnection3.h"
|
||||
|
||||
namespace nt::net3 {
|
||||
|
||||
class MockWireConnection3 : public WireConnection3 {
|
||||
public:
|
||||
MockWireConnection3() : m_os{m_data} {}
|
||||
|
||||
MOCK_METHOD(bool, Ready, (), (const, override));
|
||||
|
||||
Writer Send() override { return {m_os, *this}; }
|
||||
|
||||
MOCK_METHOD(void, Data, (std::span<const uint8_t> data));
|
||||
|
||||
MOCK_METHOD(void, Flush, (), (override));
|
||||
|
||||
MOCK_METHOD(uint64_t, GetLastFlushTime, (), (const, override));
|
||||
|
||||
MOCK_METHOD(void, StopRead, (), (override));
|
||||
MOCK_METHOD(void, StartRead, (), (override));
|
||||
|
||||
MOCK_METHOD(void, Disconnect, (std::string_view reason), (override));
|
||||
|
||||
protected:
|
||||
void FinishSend() override {
|
||||
Data(m_data);
|
||||
m_data.resize(0);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> m_data;
|
||||
wpi::raw_uvector_ostream m_os;
|
||||
};
|
||||
|
||||
} // namespace nt::net3
|
||||
@@ -1,279 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <wpi/SpanMatcher.h>
|
||||
|
||||
#include "../TestPrinters.h"
|
||||
#include "../ValueMatcher.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "net3/WireDecoder3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
using testing::_;
|
||||
using testing::MockFunction;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class MockMessageHandler3 : public net3::MessageHandler3 {
|
||||
public:
|
||||
MOCK_METHOD0(KeepAlive, void());
|
||||
MOCK_METHOD0(ServerHelloDone, void());
|
||||
MOCK_METHOD0(ClientHelloDone, void());
|
||||
MOCK_METHOD0(ClearEntries, void());
|
||||
MOCK_METHOD1(ProtoUnsup, void(unsigned int proto_rev));
|
||||
MOCK_METHOD2(ClientHello,
|
||||
void(std::string_view self_id, unsigned int proto_rev));
|
||||
MOCK_METHOD2(ServerHello, void(unsigned int flags, std::string_view self_id));
|
||||
MOCK_METHOD5(EntryAssign, void(std::string_view name, unsigned int id,
|
||||
unsigned int seq_num, const Value& value,
|
||||
unsigned int flags));
|
||||
MOCK_METHOD3(EntryUpdate,
|
||||
void(unsigned int id, unsigned int seq_num, const Value& value));
|
||||
MOCK_METHOD2(FlagsUpdate, void(unsigned int id, unsigned int flags));
|
||||
MOCK_METHOD1(EntryDelete, void(unsigned int id));
|
||||
MOCK_METHOD3(ExecuteRpc, void(unsigned int id, unsigned int uid,
|
||||
std::span<const uint8_t> params));
|
||||
MOCK_METHOD3(RpcResponse, void(unsigned int id, unsigned int uid,
|
||||
std::span<const uint8_t> result));
|
||||
};
|
||||
|
||||
class WireDecoder3Test : public ::testing::Test {
|
||||
protected:
|
||||
StrictMock<MockMessageHandler3> handler;
|
||||
net3::WireDecoder3 decoder{handler};
|
||||
|
||||
void DecodeComplete(std::span<const uint8_t> in) {
|
||||
decoder.Execute(&in);
|
||||
EXPECT_TRUE(in.empty());
|
||||
ASSERT_EQ(decoder.GetError(), "");
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(WireDecoder3Test, KeepAlive) {
|
||||
EXPECT_CALL(handler, KeepAlive());
|
||||
DecodeComplete("\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClientHello) {
|
||||
EXPECT_CALL(handler, ClientHello(std::string_view{"hello"}, 0x0300u));
|
||||
DecodeComplete("\x01\x03\x00\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ProtoUnsup) {
|
||||
EXPECT_CALL(handler, ProtoUnsup(0x0300u));
|
||||
EXPECT_CALL(handler, ProtoUnsup(0x0200u));
|
||||
DecodeComplete("\x02\x03\x00\x02\x02\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ServerHelloDone) {
|
||||
EXPECT_CALL(handler, ServerHelloDone());
|
||||
DecodeComplete("\x03"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ServerHello) {
|
||||
EXPECT_CALL(handler, ServerHello(0x03, std::string_view{"hello"}));
|
||||
DecodeComplete("\x04\x03\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClientHelloDone) {
|
||||
EXPECT_CALL(handler, ClientHelloDone());
|
||||
DecodeComplete("\x05"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, FlagsUpdate) {
|
||||
EXPECT_CALL(handler, FlagsUpdate(0x5678, 0x03));
|
||||
DecodeComplete("\x12\x56\x78\x03"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryDelete) {
|
||||
EXPECT_CALL(handler, EntryDelete(0x5678));
|
||||
DecodeComplete("\x13\x56\x78"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClearEntries) {
|
||||
EXPECT_CALL(handler, ClearEntries());
|
||||
DecodeComplete("\x14\xd0\x6c\xb2\x7a"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ClearEntriesInvalid) {
|
||||
auto in = "\x14\xd0\x6c\xb2\x7b"_us;
|
||||
decoder.Execute(&in);
|
||||
EXPECT_EQ(decoder.GetError(), "received incorrect CLEAR_ENTRIES magic value");
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, ExecuteRpc) {
|
||||
EXPECT_CALL(handler, ExecuteRpc(0x5678, 0x1234, wpi::SpanEq("hello"_us)));
|
||||
DecodeComplete("\x20\x56\x78\x12\x34\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, RpcResponse) {
|
||||
EXPECT_CALL(handler, RpcResponse(0x5678, 0x1234, wpi::SpanEq("hello"_us)));
|
||||
DecodeComplete("\x21\x56\x78\x12\x34\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, UnknownMessage) {
|
||||
auto in = "\x23"_us;
|
||||
decoder.Execute(&in);
|
||||
EXPECT_EQ(decoder.GetError(), "unrecognized message type: 35");
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryAssignBoolean) {
|
||||
EXPECT_CALL(handler, EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeBoolean(true), 0x9a));
|
||||
DecodeComplete("\x10\x04test\x00\x56\x78\x12\x34\x9a\x01"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryAssignDouble) {
|
||||
EXPECT_CALL(handler, EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5), 0x9a));
|
||||
DecodeComplete(
|
||||
"\x10\x04test\x01\x56\x78\x12\x34"
|
||||
"\x9a\x41\x0c\x13\x80\x00\x00\x00\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBoolean) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeBoolean(true)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x00\x01"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDouble) {
|
||||
// values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(0.0)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x00\x00\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(2.3e5)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x41\x0c\x13\x80\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(
|
||||
handler,
|
||||
EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(std::numeric_limits<double>::infinity())));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x7f\xf0\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(DBL_MIN)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x00\x10\x00\x00\x00\x00\x00\x00"_us);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeDouble(DBL_MAX)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x01\x7f\xef\xff\xff\xff\xff\xff\xff"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateString) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString("hello"sv)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x02\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateString2) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x7f};
|
||||
in.insert(in.end(), 127, '*');
|
||||
std::string out(127, '*');
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x80, 0x01};
|
||||
in.insert(in.end(), 127, '*');
|
||||
in.push_back('x');
|
||||
|
||||
std::string out(127, '*');
|
||||
out.push_back('x');
|
||||
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringHuge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x81, 0x80, 0x04};
|
||||
in.insert(in.end(), 65534, '*');
|
||||
in.insert(in.end(), 3, 'x');
|
||||
|
||||
std::string out(65534, '*');
|
||||
out.append(3, 'x');
|
||||
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeString(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateRaw) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeRaw("hello"_us)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x03\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBooleanArray) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray({false, true, false})));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x10\x03\x00\x01\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateBooleanArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
in.insert(in.end(), 255, 0);
|
||||
std::vector<int> out(255, 0);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDoubleArray) {
|
||||
EXPECT_CALL(handler,
|
||||
EntryUpdate(0x5678, 0x1234, Value::MakeDoubleArray({0.5, 0.25})));
|
||||
DecodeComplete(
|
||||
"\x11\x56\x78\x12\x34\x11\x02"
|
||||
"\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateDoubleArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
in.insert(in.end(), 255 * 8, 0);
|
||||
std::vector<double> out(255, 0.0);
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringArray) {
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray({"hello", "bye"})));
|
||||
DecodeComplete(
|
||||
"\x11\x56\x78\x12\x34\x12\x02\x05hello\x03"
|
||||
"bye"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateStringArrayLarge) {
|
||||
std::vector<uint8_t> in{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
in.insert(in.end(), 255, 0);
|
||||
std::vector<std::string> out(255, "");
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(out))));
|
||||
DecodeComplete(in);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateRpc) {
|
||||
// RPC values are decoded as raw
|
||||
EXPECT_CALL(handler, EntryUpdate(0x5678, 0x1234, Value::MakeRaw("hello"_us)));
|
||||
DecodeComplete("\x11\x56\x78\x12\x34\x20\x05hello"_us);
|
||||
}
|
||||
|
||||
TEST_F(WireDecoder3Test, EntryUpdateTypeError) {
|
||||
auto in = "\x11\x56\x78\x12\x34\x30\x11"_us;
|
||||
decoder.Execute(&in);
|
||||
ASSERT_EQ(decoder.GetError(), "unrecognized value type");
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
@@ -1,284 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <wpi/SpanMatcher.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "../TestPrinters.h"
|
||||
#include "net3/Message3.h"
|
||||
#include "net3/WireEncoder3.h"
|
||||
#include "networktables/NetworkTableValue.h"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace nt {
|
||||
|
||||
class WireEncoder3Test : public ::testing::Test {
|
||||
protected:
|
||||
std::vector<uint8_t> out;
|
||||
wpi::raw_uvector_ostream os{out};
|
||||
};
|
||||
|
||||
TEST_F(WireEncoder3Test, Unknown) {
|
||||
net3::WireEncode(os, net3::Message3{});
|
||||
ASSERT_TRUE(out.empty());
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, KeepAlive) {
|
||||
net3::WireEncode(os, net3::Message3::KeepAlive());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClientHello) {
|
||||
net3::WireEncode(os, net3::Message3::ClientHello("hello"));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x01\x03\x00\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ProtoUnsup) {
|
||||
net3::WireEncode(os, net3::Message3::ProtoUnsup());
|
||||
net3::WireEncode(os, net3::Message3::ProtoUnsup(0x0200u));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x02\x03\x00\x02\x02\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ServerHelloDone) {
|
||||
net3::WireEncode(os, net3::Message3::ServerHelloDone());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x03"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ServerHello) {
|
||||
net3::WireEncode(os, net3::Message3::ServerHello(0x03, "hello"));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x04\x03\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClientHelloDone) {
|
||||
net3::WireEncode(os, net3::Message3::ClientHelloDone());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x05"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, FlagsUpdate) {
|
||||
net3::WireEncode(os, net3::Message3::FlagsUpdate(0x5678, 0x03));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x12\x56\x78\x03"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryDelete) {
|
||||
net3::WireEncode(os, net3::Message3::EntryDelete(0x5678));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x13\x56\x78"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ClearEntries) {
|
||||
net3::WireEncode(os, net3::Message3::ClearEntries());
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x14\xd0\x6c\xb2\x7a"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, ExecuteRpc) {
|
||||
net3::WireEncode(os, net3::Message3::ExecuteRpc(0x5678, 0x1234, "hello"_us));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x20\x56\x78\x12\x34\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, RpcResponse) {
|
||||
net3::WireEncode(os, net3::Message3::RpcResponse(0x5678, 0x1234, "hello"_us));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x21\x56\x78\x12\x34\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryAssignBoolean) {
|
||||
net3::WireEncode(os,
|
||||
net3::Message3::EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeBoolean(true), 0x9a));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x10\x04test\x00\x56\x78\x12\x34\x9a\x01"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryAssignDouble) {
|
||||
net3::WireEncode(os,
|
||||
net3::Message3::EntryAssign("test"sv, 0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5), 0x9a));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x10\x04test\x01\x56\x78\x12\x34"
|
||||
"\x9a\x41\x0c\x13\x80\x00\x00\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBoolean) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBoolean(true)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x00\x01"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDouble) {
|
||||
// values except min and max from
|
||||
// http://www.binaryconvert.com/result_double.html
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234, Value::MakeDouble(0.0)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x00\x00\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(2.3e5)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x41\x0c\x13\x80\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234,
|
||||
Value::MakeDouble(std::numeric_limits<double>::infinity())));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x7f\xf0\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(DBL_MIN)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x00\x10\x00\x00\x00\x00\x00\x00"_us));
|
||||
|
||||
out.clear();
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDouble(DBL_MAX)));
|
||||
ASSERT_THAT(
|
||||
out, wpi::SpanEq(
|
||||
"\x11\x56\x78\x12\x34\x01\x7f\xef\xff\xff\xff\xff\xff\xff"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateString) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString("hello"sv)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x02\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateString2) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x7f};
|
||||
ex.insert(ex.end(), 127, '*');
|
||||
std::string in(127, '*');
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x80, 0x01};
|
||||
ex.insert(ex.end(), 127, '*');
|
||||
ex.push_back('x');
|
||||
|
||||
std::string in(127, '*');
|
||||
in.push_back('x');
|
||||
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringHuge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x02, 0x81, 0x80, 0x04};
|
||||
ex.insert(ex.end(), 65534, '*');
|
||||
ex.insert(ex.end(), 3, 'x');
|
||||
|
||||
std::string in(65534, '*');
|
||||
in.append(3, 'x');
|
||||
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeString(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateRaw) {
|
||||
net3::WireEncode(os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeRaw("hello"_us)));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x03\x05hello"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeBooleanArray({false, true, false})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x10\x03\x00\x01\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<int> in(255, 0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateBooleanArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x10, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<int> in(256, 0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeBooleanArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray({0.5, 0.25})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x11\x02"
|
||||
"\x3f\xe0\x00\x00\x00\x00\x00\x00"
|
||||
"\x3f\xd0\x00\x00\x00\x00\x00\x00"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
ex.insert(ex.end(), 255 * 8, 0);
|
||||
std::vector<double> in(255, 0.0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateDoubleArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x11, 0xff};
|
||||
ex.insert(ex.end(), 255 * 8, 0);
|
||||
std::vector<double> in(256, 0.0);
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeDoubleArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArray) {
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(
|
||||
0x5678, 0x1234, Value::MakeStringArray({"hello", "bye"})));
|
||||
ASSERT_THAT(out, wpi::SpanEq("\x11\x56\x78\x12\x34\x12\x02\x05hello\x03"
|
||||
"bye"_us));
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArrayLarge) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<std::string> in(255, "");
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
TEST_F(WireEncoder3Test, EntryUpdateStringArrayTrunc) {
|
||||
std::vector<uint8_t> ex{0x11, 0x56, 0x78, 0x12, 0x34, 0x12, 0xff};
|
||||
ex.insert(ex.end(), 255, 0);
|
||||
std::vector<std::string> in(256, "");
|
||||
net3::WireEncode(
|
||||
os, net3::Message3::EntryUpdate(0x5678, 0x1234,
|
||||
Value::MakeStringArray(std::move(in))));
|
||||
ASSERT_THAT(out, ex);
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
Reference in New Issue
Block a user