2022-08-24 21:03:30 +00:00
|
|
|
// 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"
|
|
|
|
|
2022-11-25 21:04:37 +00:00
|
|
|
#include "crypto-utils.h" // tr_rand_obj()
|
2022-08-24 21:03:30 +00:00
|
|
|
#include "session.h"
|
|
|
|
#include "tr-lpd.h"
|
|
|
|
|
|
|
|
#include "test-fixtures.h"
|
|
|
|
|
|
|
|
using namespace std::literals;
|
|
|
|
|
2022-11-14 20:16:29 +00:00
|
|
|
namespace libtransmission::test
|
2022-08-24 21:03:30 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
using LpdTest = SessionTest;
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
class MyMediator final : public tr_lpd::Mediator
|
|
|
|
{
|
|
|
|
public:
|
2022-10-01 14:12:49 +00:00
|
|
|
explicit MyMediator(tr_session& session)
|
|
|
|
: session_{ session }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-08-24 21:03:30 +00:00
|
|
|
[[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_;
|
|
|
|
}
|
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
[[nodiscard]] libtransmission::TimerMaker& timerMaker() override
|
|
|
|
{
|
|
|
|
return session_.timerMaker();
|
|
|
|
}
|
|
|
|
|
2022-08-24 21:03:30 +00:00
|
|
|
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_;
|
|
|
|
}
|
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
tr_session& session_;
|
2022-08-24 21:03:30 +00:00
|
|
|
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()
|
|
|
|
{
|
2022-11-25 21:04:37 +00:00
|
|
|
return tr_sha1::digest(tr_rand_obj<std::array<char, 256>>());
|
2022-08-24 21:03:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto makeRandomHashString()
|
|
|
|
{
|
|
|
|
return tr_strupper(tr_sha1_to_string(makeRandomHash()));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
TEST_F(LpdTest, HelloWorld)
|
|
|
|
{
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator = MyMediator{ *session_ };
|
|
|
|
auto lpd = tr_lpd::create(mediator, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
EXPECT_TRUE(lpd);
|
|
|
|
EXPECT_EQ(0U, std::size(mediator.found_));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(LpdTest, CanAnnounceAndRead)
|
|
|
|
{
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_a = MyMediator{ *session_ };
|
|
|
|
auto lpd_a = tr_lpd::create(mediator_a, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
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
|
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_b = MyMediator{ *session_ };
|
2022-08-24 21:03:30 +00:00
|
|
|
mediator_b.torrents_.push_back(info);
|
2022-10-01 14:12:49 +00:00
|
|
|
auto lpd_b = tr_lpd::create(mediator_b, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_a = MyMediator{ *session_ };
|
|
|
|
auto lpd_a = tr_lpd::create(mediator_a, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
EXPECT_TRUE(lpd_a);
|
|
|
|
|
|
|
|
auto info_hash_strings = std::array<std::string, 2>{};
|
|
|
|
auto infos = std::array<tr_lpd::Mediator::TorrentInfo, 2>{};
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_b = MyMediator{ *session_ };
|
2022-08-24 21:03:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
auto lpd_b = tr_lpd::create(mediator_b, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
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)
|
|
|
|
{
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_a = MyMediator{ *session_ };
|
|
|
|
auto lpd_a = tr_lpd::create(mediator_a, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
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>{};
|
2022-10-01 14:12:49 +00:00
|
|
|
auto mediator_b = MyMediator{ *session_ };
|
2022-08-24 21:03:30 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
auto lpd_b = tr_lpd::create(mediator_b, session_->eventBase());
|
2022-08-24 21:03:30 +00:00
|
|
|
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 }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-14 20:16:29 +00:00
|
|
|
} // namespace libtransmission::test
|