transmission/tests/libtransmission/dns-test.cc

200 lines
5.0 KiB
C++

// This file Copyright (C) 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <chrono>
#include <memory>
#include <event2/event.h>
#include "transmission.h"
#include "dns-ev.h"
#include "dns.h"
#include "trevent.h" // for tr_evthread_init();
#include "gtest/gtest.h"
#include "test-fixtures.h"
using namespace std::literals;
namespace libtransmission::test
{
class EvDnsTest : public ::testing::Test
{
protected:
void SetUp() override
{
::testing::Test::SetUp();
tr_evthread_init();
event_base_ = event_base_new();
}
void TearDown() override
{
event_base_free(event_base_);
event_base_ = nullptr;
::testing::Test::TearDown();
}
static bool waitFor(
struct event_base* evb,
std::function<bool()> const& test,
std::chrono::milliseconds msec = DefaultTimeout)
{
auto const deadline = std::chrono::steady_clock::now() + msec;
for (;;)
{
if (test())
{
return true;
}
if (std::chrono::steady_clock::now() > deadline)
{
return false;
}
event_base_loop(evb, EVLOOP_ONCE);
}
}
static auto constexpr DefaultTimeout = 5s;
struct event_base* event_base_ = nullptr;
};
TEST_F(EvDnsTest, canLookup)
{
auto dns = EvDns{ event_base_, tr_time };
auto done = false;
dns.lookup(
"example.com",
[&done](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_GT(expires_at, tr_time());
done = true;
});
waitFor(event_base_, [&done]() { return done; });
EXPECT_TRUE(done);
}
TEST_F(EvDnsTest, canRequestWhilePending)
{
auto dns = EvDns{ event_base_, tr_time };
auto n_done = size_t{ 0 };
dns.lookup(
"example.com",
[&n_done](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_GT(expires_at, tr_time());
++n_done;
});
dns.lookup(
"example.com",
[&n_done](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_GT(expires_at, tr_time());
++n_done;
});
// wait for both callbacks to be called
waitFor(event_base_, [&n_done]() { return n_done >= 2U; });
EXPECT_EQ(2U, n_done);
}
TEST_F(EvDnsTest, canCancel)
{
auto dns = EvDns{ event_base_, tr_time };
auto n_done = size_t{ 0 };
static auto constexpr Name = "example.com"sv;
auto tag = dns.lookup(
Name,
[&n_done](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
++n_done;
// we cancelled this req, so `ai` and `ailen` should be zeroed out
EXPECT_EQ(nullptr, ai);
EXPECT_EQ(0, ailen);
EXPECT_EQ(0, expires_at);
});
dns.lookup(
Name,
[&n_done](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
++n_done;
// this one did _not_ get cancelled so it should be OK
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_GT(expires_at, tr_time());
});
dns.cancel(tag);
// wait for both callbacks to be called
waitFor(event_base_, [&n_done]() { return n_done >= 2U; });
EXPECT_EQ(2U, n_done);
}
TEST_F(EvDnsTest, doesCacheEntries)
{
auto dns = EvDns{ event_base_, tr_time };
static auto constexpr Name = "example.com"sv;
struct sockaddr const* ai_addr = nullptr;
dns.lookup(
Name,
[&ai_addr](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_GT(expires_at, tr_time());
ai_addr = ai;
});
// wait for the lookup
waitFor(event_base_, [&ai_addr]() { return ai_addr != nullptr; });
ASSERT_NE(nullptr, ai_addr);
auto second_callback_called = false;
dns.lookup(
Name,
[&ai_addr, &second_callback_called](struct sockaddr const* ai, socklen_t ailen, time_t expires_at)
{
EXPECT_NE(nullptr, ai);
EXPECT_GT(ailen, 0);
EXPECT_EQ(ai_addr, ai);
EXPECT_GT(expires_at, tr_time());
second_callback_called = true;
});
// since it's cached, the callback should have been invoked
// without waiting for the event loop
EXPECT_TRUE(second_callback_called);
// confirm that `cached()` returns the cached value immediately
auto res = dns.cached(Name);
EXPECT_TRUE(res);
EXPECT_EQ(ai_addr, res->first);
EXPECT_GT(res->second, 0);
}
} // namespace libtransmission::test