Skip to content

Commit

Permalink
test: put the generic parts from StaticContentsSock into a separate c…
Browse files Browse the repository at this point in the history
…lass

This allows reusing them in other mocked implementations.

Also move the implementation (method definitions) to
`test/util/net.cpp` to make the header `test/util/net.h` easier to follow.
  • Loading branch information
vasild authored and Sjors committed Aug 29, 2024
1 parent 9537d0a commit 5c59b1f
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 75 deletions.
103 changes: 103 additions & 0 deletions src/test/util/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,106 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candida
}
return candidates;
}

// Have different ZeroSock (or others that inherit from it) objects have different
// m_socket because EqualSharedPtrSock compares m_socket and we want to avoid two
// different objects comparing as equal.
static std::atomic<SOCKET> g_mocked_sock_fd{0};

ZeroSock::ZeroSock() : Sock{g_mocked_sock_fd++} {}

// Sock::~Sock() would try to close(2) m_socket if it is not INVALID_SOCKET, avoid that.
ZeroSock::~ZeroSock() { m_socket = INVALID_SOCKET; }

ssize_t ZeroSock::Send(const void*, size_t len, int) const { return len; }

ssize_t ZeroSock::Recv(void* buf, size_t len, int flags) const
{
memset(buf, 0x0, len);
return len;
}

int ZeroSock::Connect(const sockaddr*, socklen_t) const { return 0; }

int ZeroSock::Bind(const sockaddr*, socklen_t) const { return 0; }

int ZeroSock::Listen(int) const { return 0; }

std::unique_ptr<Sock> ZeroSock::Accept(sockaddr* addr, socklen_t* addr_len) const
{
if (addr != nullptr) {
// Pretend all connections come from 5.5.5.5:6789
memset(addr, 0x00, *addr_len);
const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in));
if (*addr_len >= write_len) {
*addr_len = write_len;
sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr);
addr_in->sin_family = AF_INET;
memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr));
addr_in->sin_port = htons(6789);
}
}
return std::make_unique<ZeroSock>();
}

int ZeroSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
std::memset(opt_val, 0x0, *opt_len);
return 0;
}

int ZeroSock::SetSockOpt(int, int, const void*, socklen_t) const { return 0; }

int ZeroSock::GetSockName(sockaddr* name, socklen_t* name_len) const
{
std::memset(name, 0x0, *name_len);
return 0;
}

bool ZeroSock::SetNonBlocking() const { return true; }

bool ZeroSock::IsSelectable() const { return true; }

bool ZeroSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
{
if (occurred != nullptr) {
*occurred = requested;
}
return true;
}

bool ZeroSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
{
for (auto& [sock, events] : events_per_sock) {
(void)sock;
events.occurred = events.requested;
}
return true;
}

ZeroSock& ZeroSock::operator=(Sock&& other)
{
assert(false && "Move of Sock into ZeroSock not allowed.");
return *this;
}

StaticContentsSock::StaticContentsSock(const std::string& contents)
: m_contents{contents}
{
}

ssize_t StaticContentsSock::Recv(void* buf, size_t len, int flags) const
{
const size_t consume_bytes{std::min(len, m_contents.size() - m_consumed)};
std::memcpy(buf, m_contents.data() + m_consumed, consume_bytes);
if ((flags & MSG_PEEK) == 0) {
m_consumed += consume_bytes;
}
return consume_bytes;
}

StaticContentsSock& StaticContentsSock::operator=(Sock&& other)
{
assert(false && "Move of Sock into StaticContentsSock not allowed.");
return *this;
}
117 changes: 42 additions & 75 deletions src/test/util/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,110 +133,77 @@ constexpr auto ALL_NETWORKS = std::array{
Network::NET_INTERNAL,
};

std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context);

/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
* exhausted an EOF is returned by further reads.
* A mocked Sock alternative that succeeds on all operations.
* Returns infinite amount of 0x0 bytes on reads.
*/
class StaticContentsSock : public Sock
class ZeroSock : public Sock
{
public:
explicit StaticContentsSock(const std::string& contents)
: Sock{INVALID_SOCKET},
m_contents{contents}
{
}
ZeroSock();

~StaticContentsSock() override { m_socket = INVALID_SOCKET; }
~ZeroSock() override;

StaticContentsSock& operator=(Sock&& other) override
{
assert(false && "Move of Sock into MockSock not allowed.");
return *this;
}
ssize_t Send(const void*, size_t len, int) const override;

ssize_t Send(const void*, size_t len, int) const override { return len; }
ssize_t Recv(void* buf, size_t len, int flags) const override;

ssize_t Recv(void* buf, size_t len, int flags) const override
{
const size_t consume_bytes{std::min(len, m_contents.size() - m_consumed)};
std::memcpy(buf, m_contents.data() + m_consumed, consume_bytes);
if ((flags & MSG_PEEK) == 0) {
m_consumed += consume_bytes;
}
return consume_bytes;
}
int Connect(const sockaddr*, socklen_t) const override;

int Connect(const sockaddr*, socklen_t) const override { return 0; }
int Bind(const sockaddr*, socklen_t) const override;

int Bind(const sockaddr*, socklen_t) const override { return 0; }
int Listen(int) const override;

int Listen(int) const override { return 0; }
std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override;

std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override
{
if (addr != nullptr) {
// Pretend all connections come from 5.5.5.5:6789
memset(addr, 0x00, *addr_len);
const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in));
if (*addr_len >= write_len) {
*addr_len = write_len;
sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr);
addr_in->sin_family = AF_INET;
memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr));
addr_in->sin_port = htons(6789);
}
}
return std::make_unique<StaticContentsSock>("");
};

int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
{
std::memset(opt_val, 0x0, *opt_len);
return 0;
}
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;

int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; }
int SetSockOpt(int, int, const void*, socklen_t) const override;

int GetSockName(sockaddr* name, socklen_t* name_len) const override
{
std::memset(name, 0x0, *name_len);
return 0;
}
int GetSockName(sockaddr* name, socklen_t* name_len) const override;

bool SetNonBlocking() const override { return true; }
bool SetNonBlocking() const override;

bool IsSelectable() const override { return true; }
bool IsSelectable() const override;

bool Wait(std::chrono::milliseconds timeout,
Event requested,
Event* occurred = nullptr) const override
{
if (occurred != nullptr) {
*occurred = requested;
}
return true;
}
Event* occurred = nullptr) const override;

bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override
{
for (auto& [sock, events] : events_per_sock) {
(void)sock;
events.occurred = events.requested;
}
return true;
}
bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override;

private:
ZeroSock& operator=(Sock&& other) override;
};

/**
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
* and ignores all writes. The data to be returned is given to the constructor and when it is
* exhausted an EOF is returned by further reads.
*/
class StaticContentsSock : public ZeroSock
{
public:
explicit StaticContentsSock(const std::string& contents);

/**
* Return parts of the contents that was provided at construction until it is exhausted
* and then return 0 (EOF).
*/
ssize_t Recv(void* buf, size_t len, int flags) const override;

bool IsConnected(std::string&) const override
{
return true;
}

private:
StaticContentsSock& operator=(Sock&& other) override;

const std::string m_contents;
mutable size_t m_consumed{0};
};

std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context);

#endif // BITCOIN_TEST_UTIL_NET_H

0 comments on commit 5c59b1f

Please sign in to comment.