-
Nicolas Pope authoredNicolas Pope authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
peer_unit.cpp 8.82 KiB
#include "catch.hpp"
#include <iostream>
#include <memory>
//#include <map>
#include <tuple>
#include <ftl/net/peer.hpp>
#include <ftl/net/protocol.hpp>
#include <ftl/config.h>
/* Allow socket functions to be mocked */
#define TEST_MOCKS
#include "../src/net_internal.hpp"
using std::tuple;
using std::get;
using ftl::net::Peer;
#ifdef WIN32
#pragma comment(lib, "Ws2_32.lib")
#endif
// --- Mock --------------------------------------------------------------------
class MockPeer : public Peer {
public:
MockPeer() : Peer(0) {}
void mock_data() { data(); }
};
// --- Support -----------------------------------------------------------------
static std::map<int, std::string> fakedata;
#ifdef WIN32
int ftl::net::internal::recv(SOCKET sd, char *buf, int n, int f) {
#else
ssize_t ftl::net::internal::recv(int sd, void *buf, size_t n, int f) {
#endif
if (fakedata.count(sd) == 0) {
std::cout << "Unrecognised socket" << std::endl;
return 0;
}
int l = fakedata[sd].size();
std::memcpy(buf, fakedata[sd].c_str(), l);
fakedata.erase(sd);
return l;
}
#ifdef WIN32
int ftl::net::internal::send(SOCKET sd, const char *v, int cnt, int flags) {
int len = cnt;
// TODO(nick) merge multiple sends
fakedata[sd] = std::string(v, len);
return len;
}
#else
ssize_t ftl::net::internal::writev(int sd, const struct iovec *v, int cnt) {
size_t len = 0; //v[0].iov_len+v[1].iov_len;
char buf[1000];
char *bufp = &buf[0];
for (auto i=0; i<cnt; i++) {
std::memcpy(bufp,v[i].iov_base,v[i].iov_len);
len += v[i].iov_len;
bufp += v[i].iov_len;
}
fakedata[sd] = std::string(&buf[0], len);
return len;
}
#endif
static std::function<void()> waithandler;
namespace ftl {
namespace net {
bool wait() {
if (waithandler) waithandler();
//waithandler = nullptr;
return true;
}
};
};
/*void fake_send(int sd, uint32_t service, ARGS) {
//std::cout << "HEX SEND: " << hexStr(data) << std::endl;
char buf[8+1024];
assert(data.size() < 1024);
ftl::net::Header *h = (ftl::net::Header*)&buf;
h->size = data.size()+4;
h->service = service;
std::memcpy(&buf[8],data.data(),data.size());
fakedata[sd] = std::string(&buf[0], 8+data.size());
//std::cout << "HEX SEND2: " << hexStr(fakedata[sd]) << std::endl;
}*/
void send_handshake(Peer &p) {
ftl::UUID id;
p.send("__handshake__", ftl::net::kMagic, ((8 << 16) + (5 << 8) + 2), id);
}
template <typename T>
tuple<std::string, T> readResponse(int s) {
msgpack::object_handle msg = msgpack::unpack(fakedata[s].data(), fakedata[s].size());
tuple<uint8_t, std::string, T> req;
msg.get().convert(req);
return std::make_tuple(get<1>(req), get<2>(req));
}
// --- Files to test -----------------------------------------------------------
#include "../src/peer.cpp"
// --- Tests -------------------------------------------------------------------
TEST_CASE("Peer(int)", "[]") {
SECTION("initiates a valid handshake") {
MockPeer s;
auto [name, hs] = readResponse<ftl::net::Handshake>(0);
REQUIRE( name == "__handshake__" );
// 1) Sends magic (64bits)
REQUIRE( get<0>(hs) == ftl::net::kMagic );
// 2) Sends FTL Version
REQUIRE( get<1>(hs) == (FTL_VERSION_MAJOR << 16) + (FTL_VERSION_MINOR << 8) + FTL_VERSION_PATCH );
// 3) Sends peer UUID
REQUIRE( s.status() == Peer::kConnecting );
}
SECTION("completes on full handshake") {
MockPeer s;
// Send handshake response
send_handshake(s);
s.mock_data();
REQUIRE( s.status() == Peer::kConnected );
}
SECTION("has correct version on full handshake") {
MockPeer s;
// Send handshake response
send_handshake(s);
s.mock_data();
REQUIRE( s.getFTLVersion() == (8 << 16) + (5 << 8) + 2 );
}
SECTION("has correct peer id on full handshake") {
MockPeer s;
// Send handshake response
//REQUIRE( s.id() == );
}
}
/*TEST_CASE("Peer::call()", "[rpc]") {
MockPeer s;
SECTION("no argument call") {
waithandler = [&]() {
// Read fakedata sent
// TODO Validate data
// Do a fake send
auto res_obj = std::make_tuple(1,0,msgpack::object(),66);
std::stringstream buf;
msgpack::pack(buf, res_obj);
fake_send(0, FTL_PROTOCOL_RPCRETURN, buf.str());
s.mock_data();
};
int res = s.call<int>("test1");
REQUIRE( (res == 66) );
}
SECTION("one argument call") {
waithandler = [&]() {
// Read fakedata sent
// TODO Validate data
// Do a fake send
auto res_obj = std::make_tuple(1,1,msgpack::object(),43);
std::stringstream buf;
msgpack::pack(buf, res_obj);
fake_send(0, FTL_PROTOCOL_RPCRETURN, buf.str());
s.mock_data();
};
int res = s.call<int>("test1", 78);
REQUIRE( (res == 43) );
}
waithandler = nullptr;
}*/
TEST_CASE("Peer::bind()", "[rpc]") {
MockPeer s;
SECTION("no argument call") {
bool done = false;
s.bind("hello", [&]() {
done = true;
});
send_handshake(s);
s.mock_data();
s.send("hello");
s.mock_data(); // Force it to read the fake send...
REQUIRE( done );
}
SECTION("one argument call") {
int done = 0;
s.bind("hello", [&](int a) {
done = a;
});
send_handshake(s);
s.mock_data();
s.send("hello", 55);
s.mock_data(); // Force it to read the fake send...
REQUIRE( (done == 55) );
}
SECTION("two argument call") {
std::string done;
s.bind("hello", [&](int a, std::string b) {
done = b;
});
send_handshake(s);
s.mock_data();
s.send("hello", 55, "world");
s.mock_data(); // Force it to read the fake send...
REQUIRE( (done == "world") );
}
}
TEST_CASE("Socket::send()", "[io]") {
MockPeer s;
SECTION("send an int") {
int i = 607;
s.send("dummy",i);
auto [name, value] = readResponse<tuple<int>>(0);
REQUIRE( (name == "dummy") );
REQUIRE( (get<0>(value) == 607) );
}
SECTION("send a string") {
std::string str("hello world");
s.send("dummy",str);
auto [name, value] = readResponse<tuple<std::string>>(0);
REQUIRE( (name == "dummy") );
REQUIRE( (get<0>(value) == "hello world") );
}
SECTION("send const char* string") {
s.send("dummy","hello world");
auto [name, value] = readResponse<tuple<std::string>>(0);
REQUIRE( (name == "dummy") );
REQUIRE( (get<0>(value) == "hello world") );
}
/*SECTION("send const char* array") {
s.send(100,ftl::net::array{"hello world",10});
REQUIRE( (get_service(0) == 100) );
REQUIRE( (get_size(0) == 10) );
REQUIRE( (get_value<std::string>(0) == "hello worl") );
}*/
SECTION("send a tuple") {
auto tup = std::make_tuple(55,66,true,6.7);
s.send("dummy",tup);
auto [name, value] = readResponse<tuple<decltype(tup)>>(0);
REQUIRE( (name == "dummy") );
REQUIRE( (get<1>(get<0>(value)) == 66) );
}
SECTION("send multiple strings") {
std::string str("hello ");
std::string str2("world");
s.send("dummy2",str,str2);
auto [name, value] = readResponse<tuple<std::string,std::string>>(0);
REQUIRE( (name == "dummy2") );
REQUIRE( (get<0>(value) == "hello") );
REQUIRE( (get<1>(value) == "world") );
}
}
/*TEST_CASE("Socket::read()", "[io]") {
MockSocket s;
SECTION("read an int") {
int i = 99;
fake_send(0, 100, std::string((char*)&i,4));
i = 0;
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.size() == sizeof(int)) );
REQUIRE( (s.read(i) == sizeof(int)) );
REQUIRE( (i == 99) );
}
SECTION("read two ints") {
int i[2];
i[0] = 99;
i[1] = 101;
fake_send(0, 100, std::string((char*)&i,2*sizeof(int)));
i[0] = 0;
i[1] = 0;
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.size() == 2*sizeof(int)) );
REQUIRE( (s.read(&i,2) == 2*sizeof(int)) );
REQUIRE( (i[0] == 99) );
REQUIRE( (i[1] == 101) );
}
SECTION("multiple reads") {
int i[2];
i[0] = 99;
i[1] = 101;
fake_send(0, 100, std::string((char*)&i,2*sizeof(int)));
i[0] = 0;
i[1] = 0;
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.read(&i[0],1) == sizeof(int)) );
REQUIRE( (i[0] == 99) );
REQUIRE( (s.read(&i[1],1) == sizeof(int)) );
REQUIRE( (i[1] == 101) );
}
SECTION("read a string") {
std::string str;
fake_send(0, 100, std::string("hello world"));
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.size() == 11) );
REQUIRE( (s.read(str) == 11) );
REQUIRE( (str == "hello world") );
}
SECTION("read into existing string") {
std::string str;
str.reserve(11);
void *ptr = str.data();
fake_send(0, 100, std::string("hello world"));
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.size() == 11) );
REQUIRE( (s.read(str) == 11) );
REQUIRE( (str == "hello world") );
REQUIRE( (str.data() == ptr) );
}
SECTION("read too much data") {
int i = 99;
fake_send(0, 100, std::string((char*)&i,4));
i = 0;
s.mock_data(); // Force a message read, but no protocol...
REQUIRE( (s.size() == sizeof(int)) );
REQUIRE( (s.read(&i,2) == sizeof(int)) );
REQUIRE( (i == 99) );
}
}*/