transmission/tests/libtransmission/lpd-test.cc

203 lines
5.5 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 <set>
#include <string>
#include <string_view>
#include <vector>
#include "transmission.h"
#include "crypto-utils.h"
#include "session.h"
#include "tr-lpd.h"
#include "test-fixtures.h"
using namespace std::literals;
namespace libtransmission
{
namespace test
{
using LpdTest = SessionTest;
namespace
{
class MyMediator final : public tr_lpd::Mediator
{
public:
MyMediator() = default;
~MyMediator() override = default;
[[nodiscard]] tr_port port() const override
{
return port_;
}
[[nodiscard]] bool allowsLPD() const override
{
return allows_lpd_;
}
[[nodiscard]] std::vector<TorrentInfo> torrents() const override
{
return torrents_;
}
void setNextAnnounceTime(std::string_view info_hash_str, time_t announce_after) override
{
for (auto& tor : torrents_)
{
if (tor.info_hash_str == info_hash_str)
{
tor.announce_after = announce_after;
break;
}
}
}
bool onPeerFound(std::string_view info_hash_str, tr_address /*address*/, tr_port /*port*/) override
{
found_.insert(std::string{ info_hash_str });
return found_returns_;
}
tr_port port_ = tr_port::fromHost(51413);
bool allows_lpd_ = true;
std::vector<TorrentInfo> torrents_;
std::set<std::string> found_;
bool found_returns_ = true;
};
auto makeRandomHash()
{
auto buf = std::array<char, 256>{};
tr_rand_buffer(std::data(buf), std::size(buf));
return tr_sha1::digest(buf);
}
auto makeRandomHashString()
{
return tr_strupper(tr_sha1_to_string(makeRandomHash()));
}
} // namespace
TEST_F(LpdTest, HelloWorld)
{
auto mediator = MyMediator{};
auto lpd = tr_lpd::create(mediator, session_->timerMaker(), session_->eventBase());
EXPECT_TRUE(lpd);
EXPECT_EQ(0U, std::size(mediator.found_));
}
TEST_F(LpdTest, CanAnnounceAndRead)
{
auto mediator_a = MyMediator{};
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
EXPECT_TRUE(lpd_a);
auto const info_hash_str = makeRandomHashString();
auto info = tr_lpd::Mediator::TorrentInfo{};
info.info_hash_str = info_hash_str;
info.activity = TR_STATUS_SEED;
info.allows_lpd = true;
info.announce_after = 0; // never announced
auto mediator_b = MyMediator{};
mediator_b.torrents_.push_back(info);
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
EXPECT_EQ(1U, mediator_a.found_.count(info_hash_str));
EXPECT_EQ(0U, mediator_b.found_.count(info_hash_str));
}
TEST_F(LpdTest, canMultiAnnounce)
{
auto mediator_a = MyMediator{};
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
EXPECT_TRUE(lpd_a);
auto info_hash_strings = std::array<std::string, 2>{};
auto infos = std::array<tr_lpd::Mediator::TorrentInfo, 2>{};
auto mediator_b = MyMediator{};
for (size_t i = 0; i < std::size(info_hash_strings); ++i)
{
auto& info_hash_string = info_hash_strings[i];
auto& info = infos[i];
info_hash_string = makeRandomHashString();
info.info_hash_str = info_hash_string;
info.activity = TR_STATUS_SEED;
info.allows_lpd = true;
info.announce_after = 0; // never announced
}
for (auto const& info : infos)
{
mediator_b.torrents_.push_back(info);
}
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
for (auto const& info : infos)
{
EXPECT_EQ(1U, mediator_a.found_.count(std::string{ info.info_hash_str }));
}
}
TEST_F(LpdTest, DoesNotReannounceTooSoon)
{
auto mediator_a = MyMediator{};
auto lpd_a = tr_lpd::create(mediator_a, session_->timerMaker(), session_->eventBase());
EXPECT_TRUE(lpd_a);
// similar to canMultiAnnounce...
auto info_hash_strings = std::array<std::string, 2>{};
auto infos = std::array<tr_lpd::Mediator::TorrentInfo, 2>{};
auto mediator_b = MyMediator{};
for (size_t i = 0; i < std::size(info_hash_strings); ++i)
{
auto& info_hash_string = info_hash_strings[i];
auto& info = infos[i];
info_hash_string = makeRandomHashString();
info.info_hash_str = info_hash_string;
info.activity = TR_STATUS_SEED;
info.allows_lpd = true;
info.announce_after = 0; // never announced
}
// ...except one torrent has already been announced
// and doesn't need to be reannounced until later
auto const now = time(nullptr);
infos[0].announce_after = now + 60;
for (auto const& info : infos)
{
mediator_b.torrents_.push_back(info);
}
auto lpd_b = tr_lpd::create(mediator_b, session_->timerMaker(), session_->eventBase());
waitFor([&mediator_a]() { return !std::empty(mediator_a.found_); }, 1s);
for (auto& info : infos)
{
auto const expected_count = info.announce_after <= now ? 1U : 0U;
EXPECT_EQ(expected_count, mediator_a.found_.count(std::string{ info.info_hash_str }));
}
}
} // namespace test
} // namespace libtransmission