A C library for asynchronous DNS requests (grpc依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

224 lines
6.0 KiB

// -*- mode: c++ -*-
#ifndef ARES_TEST_H
#define ARES_TEST_H
#include "ares.h"
#include "dns-proto.h"
// Include ares internal file for DNS protocol constants
#include "nameser.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <map>
namespace ares {
typedef unsigned char byte;
namespace test {
extern bool verbose;
extern int mock_port;
// Test fixture that ensures library initialization, and allows
// memory allocations to be failed.
class LibraryTest : public ::testing::Test {
public:
LibraryTest() {
EXPECT_EQ(ARES_SUCCESS,
ares_library_init_mem(ARES_LIB_INIT_ALL,
&LibraryTest::amalloc,
&LibraryTest::afree,
&LibraryTest::arealloc));
}
~LibraryTest() {
ares_library_cleanup();
ClearFails();
}
// Set the n-th malloc call (of any size) from the library to fail.
// (nth == 1 means the next call)
static void SetAllocFail(int nth);
// Set the next malloc call for the given size to fail.
static void SetAllocSizeFail(size_t size);
// Remove any pending alloc failures.
static void ClearFails();
static void *amalloc(size_t size);
static void* arealloc(void *ptr, size_t size);
static void afree(void *ptr);
private:
static bool ShouldAllocFail(size_t size);
static unsigned long fails_;
static std::map<size_t, int> size_fails_;
};
// Test fixture that uses a default channel.
class DefaultChannelTest : public LibraryTest {
public:
DefaultChannelTest() : channel_(nullptr) {
EXPECT_EQ(ARES_SUCCESS, ares_init(&channel_));
EXPECT_NE(nullptr, channel_);
}
~DefaultChannelTest() {
ares_destroy(channel_);
channel_ = nullptr;
}
// Process all pending work on ares-owned file descriptors.
void Process();
protected:
ares_channel channel_;
};
// Test fixture that uses a default channel with the specified lookup mode.
class DefaultChannelModeTest
: public LibraryTest,
public ::testing::WithParamInterface<std::string> {
public:
DefaultChannelModeTest() : channel_(nullptr) {
struct ares_options opts = {0};
opts.lookups = strdup(GetParam().c_str());
int optmask = ARES_OPT_LOOKUPS;
EXPECT_EQ(ARES_SUCCESS, ares_init_options(&channel_, &opts, optmask));
EXPECT_NE(nullptr, channel_);
free(opts.lookups);
}
~DefaultChannelModeTest() {
ares_destroy(channel_);
channel_ = nullptr;
}
// Process all pending work on ares-owned file descriptors.
void Process();
protected:
ares_channel channel_;
};
// Mock DNS server to allow responses to be scripted by tests.
class MockServer {
public:
MockServer(int port);
~MockServer();
// Mock method indicating the processing of a particular <name, RRtype>
// request.
MOCK_METHOD2(OnRequest, void(const std::string& name, int rrtype));
// Set the reply to be sent next; the query ID field will be overwritten
// with the value from the request.
void SetReplyData(const std::vector<byte>& reply) { reply_ = reply; }
void SetReply(const DNSPacket* reply) { SetReplyData(reply->data()); }
// Process activity on the mock server's socket FD.
void Process(int fd);
int port() const { return port_; }
int sockfd() const { return sockfd_; }
private:
void ProcessRequest(struct sockaddr_storage* addr, int addrlen,
int qid, const std::string& name, int rrtype);
int port_;
int sockfd_;
std::vector<byte> reply_;
};
// Test fixture that uses a mock DNS server.
class MockChannelOptsTest : public LibraryTest {
public:
MockChannelOptsTest(struct ares_options* givenopts, int optmask);
~MockChannelOptsTest();
// Process all pending work on ares-owned and mock-server-owned file descriptors.
void Process();
protected:
testing::NiceMock<MockServer> server_;
ares_channel channel_;
};
class MockChannelTest : public MockChannelOptsTest {
public:
MockChannelTest() : MockChannelOptsTest(nullptr, 0) {}
};
// gMock action to set the reply for a mock server.
ACTION_P2(SetReplyData, mockserver, data) {
mockserver->SetReplyData(data);
}
ACTION_P2(SetReply, mockserver, reply) {
mockserver->SetReply(reply);
}
// gMock action to cancel a channel.
ACTION_P2(CancelChannel, mockserver, channel) {
ares_cancel(channel);
}
// C++ wrapper for struct hostent.
struct HostEnt {
HostEnt() : addrtype_(-1) {}
HostEnt(const struct hostent* hostent);
std::string name_;
std::vector<std::string> aliases_;
int addrtype_; // AF_INET or AF_INET6
std::vector<std::string> addrs_;
};
std::ostream& operator<<(std::ostream& os, const HostEnt& result);
// Structure that describes the result of an ares_host_callback invocation.
struct HostResult {
// Whether the callback has been invoked.
bool done_;
// Explicitly provided result information.
int status_;
int timeouts_;
// Contents of the hostent structure, if provided.
HostEnt host_;
};
std::ostream& operator<<(std::ostream& os, const HostResult& result);
// Structure that describes the result of an ares_callback invocation.
struct SearchResult {
// Whether the callback has been invoked.
bool done_;
// Explicitly provided result information.
int status_;
int timeouts_;
std::vector<byte> data_;
};
std::ostream& operator<<(std::ostream& os, const SearchResult& result);
// Structure that describes the result of an ares_nameinfo_callback invocation.
struct NameInfoResult {
// Whether the callback has been invoked.
bool done_;
// Explicitly provided result information.
int status_;
int timeouts_;
std::string node_;
std::string service_;
};
std::ostream& operator<<(std::ostream& os, const NameInfoResult& result);
// Standard implementation of ares callbacks that fill out the corresponding
// structures.
void HostCallback(void *data, int status, int timeouts,
struct hostent *hostent);
void SearchCallback(void *data, int status, int timeouts,
unsigned char *abuf, int alen);
void NameInfoCallback(void *data, int status, int timeouts,
char *node, char *service);
} // namespace test
} // namespace ares
#endif