mirror of
https://github.com/transmission/transmission
synced 2024-12-29 11:06:23 +00:00
390 lines
14 KiB
C++
390 lines
14 KiB
C++
// This file Copyright (C) 2021-2022 Mnemosyne LLC.
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
// License text can be found in the licenses/ folder.
|
|
|
|
#include <array>
|
|
#include <cstdlib>
|
|
#include <set>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "transmission.h"
|
|
|
|
#include "announce-list.h"
|
|
#include "error.h"
|
|
#include "torrent-metainfo.h"
|
|
#include "utils.h"
|
|
#include "variant.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
using AnnounceListTest = ::testing::Test;
|
|
using namespace std::literals;
|
|
|
|
TEST_F(AnnounceListTest, canAdd)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Announce = "https://example.org/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_EQ(1, announce_list.add(Tier, Announce));
|
|
auto const tracker = announce_list.at(0);
|
|
EXPECT_EQ(Announce, tracker.announce.full);
|
|
EXPECT_EQ("https://example.org/scrape"sv, tracker.scrape.full);
|
|
EXPECT_EQ(Tier, tracker.tier);
|
|
EXPECT_EQ("example.org:443"sv, tracker.host.sv());
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, groupsSiblingsIntoSameTier)
|
|
{
|
|
auto constexpr Tier1 = tr_tracker_tier_t{ 1 };
|
|
auto constexpr Tier2 = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Tier3 = tr_tracker_tier_t{ 3 };
|
|
auto constexpr Announce1 = "https://example.org/announce"sv;
|
|
auto constexpr Announce2 = "http://example.org/announce"sv;
|
|
auto constexpr Announce3 = "udp://example.org:999/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier1, Announce1));
|
|
EXPECT_TRUE(announce_list.add(Tier2, Announce2));
|
|
EXPECT_TRUE(announce_list.add(Tier3, Announce3));
|
|
|
|
EXPECT_EQ(3, std::size(announce_list));
|
|
EXPECT_EQ(Tier1, announce_list.at(0).tier);
|
|
EXPECT_EQ(Tier1, announce_list.at(1).tier);
|
|
EXPECT_EQ(Tier1, announce_list.at(2).tier);
|
|
EXPECT_EQ(Announce1, announce_list.at(1).announce.full);
|
|
EXPECT_EQ(Announce2, announce_list.at(0).announce.full);
|
|
EXPECT_EQ(Announce3, announce_list.at(2).announce.full);
|
|
EXPECT_EQ("example.org:443"sv, announce_list.at(1).host.sv());
|
|
EXPECT_EQ("example.org:80"sv, announce_list.at(0).host.sv());
|
|
EXPECT_EQ("example.org:999"sv, announce_list.at(2).host.sv());
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canAddWithoutScrape)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Announce = "https://example.org/foo"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
|
auto const tracker = announce_list.at(0);
|
|
EXPECT_EQ(Announce, tracker.announce.full);
|
|
EXPECT_TRUE(std::empty(tracker.scrape_str));
|
|
EXPECT_EQ(Tier, tracker.tier);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canAddUdp)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Announce = "udp://example.org/"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
|
auto const tracker = announce_list.at(0);
|
|
EXPECT_EQ(Announce, tracker.announce.full);
|
|
EXPECT_EQ("udp://example.org/"sv, tracker.scrape.full);
|
|
EXPECT_EQ(Tier, tracker.tier);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotAddDuplicateAnnounce)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Announce = "https://example.org/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
|
EXPECT_EQ(1, announce_list.size());
|
|
EXPECT_FALSE(announce_list.add(Tier, Announce));
|
|
EXPECT_EQ(1, announce_list.size());
|
|
|
|
auto constexpr Announce2 = "https://example.org:443/announce"sv;
|
|
EXPECT_FALSE(announce_list.add(Tier, Announce2));
|
|
EXPECT_EQ(1, announce_list.size());
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotAddInvalidUrl)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 2 };
|
|
auto constexpr Announce = "telnet://example.org/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_FALSE(announce_list.add(Tier, Announce));
|
|
EXPECT_EQ(0, announce_list.size());
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canSet)
|
|
{
|
|
auto constexpr Urls = std::array<char const*, 3>{
|
|
"https://www.example.com/a/announce",
|
|
"https://www.example.com/b/announce",
|
|
"https://www.example.com/c/announce",
|
|
};
|
|
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 1, 2, 3 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_EQ(3, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
|
EXPECT_EQ(3, announce_list.size());
|
|
EXPECT_EQ(Tiers[0], announce_list.at(0).tier);
|
|
EXPECT_EQ(Tiers[1], announce_list.at(1).tier);
|
|
EXPECT_EQ(Tiers[2], announce_list.at(2).tier);
|
|
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
|
EXPECT_EQ(Urls[1], announce_list.at(1).announce.full);
|
|
EXPECT_EQ(Urls[2], announce_list.at(2).announce.full);
|
|
auto const expected_tiers = std::set<tr_tracker_tier_t>(std::begin(Tiers), std::end(Tiers));
|
|
auto const tiers = announce_list.tiers();
|
|
EXPECT_EQ(expected_tiers, tiers);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canSetUnsortedWithBackupsInTiers)
|
|
{
|
|
auto constexpr Urls = std::array<char const*, 6>{
|
|
"https://www.backup-a.com/announce", "https://www.backup-b.com/announce", "https://www.backup-c.com/announce",
|
|
"https://www.primary-a.com/announce", "https://www.primary-b.com/announce", "https://www.primary-c.com/announce",
|
|
};
|
|
auto constexpr Tiers = std::array<tr_tracker_tier_t, 6>{ 0, 1, 2, 0, 1, 2 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_EQ(6, announce_list.set(std::data(Urls), std::data(Tiers), 6));
|
|
EXPECT_EQ(6, announce_list.size());
|
|
EXPECT_EQ(0, announce_list.at(0).tier);
|
|
EXPECT_EQ(0, announce_list.at(1).tier);
|
|
EXPECT_EQ(1, announce_list.at(2).tier);
|
|
EXPECT_EQ(1, announce_list.at(3).tier);
|
|
EXPECT_EQ(2, announce_list.at(4).tier);
|
|
EXPECT_EQ(2, announce_list.at(5).tier);
|
|
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
|
EXPECT_EQ(Urls[3], announce_list.at(1).announce.full);
|
|
EXPECT_EQ(Urls[1], announce_list.at(2).announce.full);
|
|
EXPECT_EQ(Urls[4], announce_list.at(3).announce.full);
|
|
EXPECT_EQ(Urls[2], announce_list.at(4).announce.full);
|
|
EXPECT_EQ(Urls[5], announce_list.at(5).announce.full);
|
|
|
|
// confirm that each has a unique id
|
|
auto ids = std::set<tr_tracker_id_t>{};
|
|
for (size_t i = 0, n = std::size(announce_list); i < n; ++i)
|
|
{
|
|
ids.insert(announce_list.at(i).id);
|
|
}
|
|
EXPECT_EQ(std::size(announce_list), std::size(ids));
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canSetExceptDuplicate)
|
|
{
|
|
auto constexpr Urls = std::array<char const*, 3>{
|
|
"https://www.example.com/a/announce",
|
|
"https://www.example.com/b/announce",
|
|
"https://www.example.com/b/announce",
|
|
};
|
|
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 3, 2, 1 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_EQ(2, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
|
EXPECT_EQ(2, announce_list.size());
|
|
EXPECT_EQ(Tiers[0], announce_list.at(1).tier);
|
|
EXPECT_EQ(Tiers[1], announce_list.at(0).tier);
|
|
EXPECT_EQ(Urls[0], announce_list.at(1).announce.full);
|
|
EXPECT_EQ(Urls[1], announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canSetExceptInvalid)
|
|
{
|
|
auto constexpr Urls = std::array<char const*, 3>{
|
|
"https://www.example.com/a/announce",
|
|
"telnet://www.example.com/b/announce",
|
|
"https://www.example.com/c/announce",
|
|
};
|
|
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 1, 2, 3 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_EQ(2, announce_list.set(std::data(Urls), std::data(Tiers), 3));
|
|
EXPECT_EQ(2, announce_list.size());
|
|
EXPECT_EQ(Tiers[0], announce_list.at(0).tier);
|
|
EXPECT_EQ(Tiers[2], announce_list.at(1).tier);
|
|
EXPECT_EQ(Urls[0], announce_list.at(0).announce.full);
|
|
EXPECT_EQ(Urls[2], announce_list.at(1).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canRemoveById)
|
|
{
|
|
auto constexpr Announce = "https://www.example.com/announce"sv;
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
announce_list.add(Tier, Announce);
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
auto const id = announce_list.at(0).id;
|
|
|
|
EXPECT_TRUE(announce_list.remove(id));
|
|
EXPECT_EQ(0, std::size(announce_list));
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotRemoveByInvalidId)
|
|
{
|
|
auto constexpr Announce = "https://www.example.com/announce"sv;
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
announce_list.add(Tier, Announce);
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
auto const id = announce_list.at(0).id;
|
|
|
|
EXPECT_FALSE(announce_list.remove(id + 1));
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
EXPECT_EQ(Announce, announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canRemoveByAnnounce)
|
|
{
|
|
auto constexpr Announce = "https://www.example.com/announce"sv;
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
announce_list.add(Tier, Announce);
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
|
|
EXPECT_TRUE(announce_list.remove(Announce));
|
|
EXPECT_EQ(0, std::size(announce_list));
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotRemoveByInvalidAnnounce)
|
|
{
|
|
auto constexpr Announce = "https://www.example.com/announce"sv;
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
announce_list.add(Tier, Announce);
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
|
|
EXPECT_FALSE(announce_list.remove("https://www.not-example.com/announce"sv));
|
|
EXPECT_EQ(1, std::size(announce_list));
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canReplace)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
|
auto constexpr Announce2 = "https://www.example.com/2/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
|
EXPECT_TRUE(announce_list.replace(announce_list.at(0).id, Announce2));
|
|
EXPECT_EQ(Announce2, announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotReplaceInvalidId)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
|
auto constexpr Announce2 = "https://www.example.com/2/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
|
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id + 1, Announce2));
|
|
EXPECT_EQ(Announce1, announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotReplaceWithInvalidAnnounce)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
auto constexpr Announce1 = "https://www.example.com/1/announce"sv;
|
|
auto constexpr Announce2 = "telnet://www.example.com/2/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce1));
|
|
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id, Announce2));
|
|
EXPECT_EQ(Announce1, announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, canNotReplaceWithDuplicate)
|
|
{
|
|
auto constexpr Tier = tr_tracker_tier_t{ 1 };
|
|
auto constexpr Announce = "https://www.example.com/announce"sv;
|
|
|
|
auto announce_list = tr_announce_list{};
|
|
EXPECT_TRUE(announce_list.add(Tier, Announce));
|
|
EXPECT_FALSE(announce_list.replace(announce_list.at(0).id, Announce));
|
|
EXPECT_EQ(Announce, announce_list.at(0).announce.full);
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, announceToScrape)
|
|
{
|
|
struct ScrapeTest
|
|
{
|
|
std::string_view announce;
|
|
std::string_view expected_scrape;
|
|
};
|
|
|
|
auto constexpr Tests = std::array<ScrapeTest, 3>{ {
|
|
{ "https://www.example.com/announce"sv, "https://www.example.com/scrape"sv },
|
|
{ "https://www.example.com/foo"sv, ""sv },
|
|
{ "udp://www.example.com:999/"sv, "udp://www.example.com:999/"sv },
|
|
} };
|
|
|
|
for (auto const test : Tests)
|
|
{
|
|
auto const scrape = tr_announce_list::announceToScrape(tr_quark_new(test.announce));
|
|
EXPECT_EQ(tr_quark_new(test.expected_scrape), scrape);
|
|
}
|
|
}
|
|
|
|
TEST_F(AnnounceListTest, save)
|
|
{
|
|
auto constexpr Urls = std::array<char const*, 3>{
|
|
"https://www.example.com/a/announce",
|
|
"https://www.example.com/b/announce",
|
|
"https://www.example.com/c/announce",
|
|
};
|
|
auto constexpr Tiers = std::array<tr_tracker_tier_t, 3>{ 0, 1, 2 };
|
|
|
|
// first, set up a scratch .torrent
|
|
auto constexpr* const OriginalFile = LIBTRANSMISSION_TEST_ASSETS_DIR "/Android-x86 8.1 r6 iso.torrent";
|
|
auto original_content = std::vector<char>{};
|
|
auto const test_file = tr_strvJoin(::testing::TempDir(), "transmission-announce-list-test.torrent"sv);
|
|
tr_error* error = nullptr;
|
|
EXPECT_TRUE(tr_loadFile(original_content, OriginalFile, &error));
|
|
EXPECT_EQ(nullptr, error);
|
|
EXPECT_TRUE(tr_saveFile(test_file, { std::data(original_content), std::size(original_content) }, &error));
|
|
EXPECT_EQ(nullptr, error);
|
|
|
|
// make an announce_list for it
|
|
auto announce_list = tr_announce_list();
|
|
EXPECT_TRUE(announce_list.add(Tiers[0], Urls[0]));
|
|
EXPECT_TRUE(announce_list.add(Tiers[1], Urls[1]));
|
|
EXPECT_TRUE(announce_list.add(Tiers[2], Urls[2]));
|
|
|
|
// try saving to a nonexistent .torrent file
|
|
EXPECT_FALSE(announce_list.save("/this/path/does/not/exist", &error));
|
|
EXPECT_NE(nullptr, error);
|
|
EXPECT_NE(0, error->code);
|
|
tr_error_clear(&error);
|
|
|
|
// now save to a real .torrent fi le
|
|
EXPECT_TRUE(announce_list.save(test_file, &error));
|
|
EXPECT_EQ(nullptr, error);
|
|
|
|
// load the original
|
|
auto original_tm = tr_torrent_metainfo{};
|
|
EXPECT_TRUE(original_tm.parseBenc({ std::data(original_content), std::size(original_content) }));
|
|
|
|
// load the scratch that we saved to
|
|
auto modified_tm = tr_torrent_metainfo{};
|
|
EXPECT_TRUE(modified_tm.parseTorrentFile(test_file));
|
|
|
|
// test that non-announce parts of the metainfo are the same
|
|
EXPECT_EQ(original_tm.name(), modified_tm.name());
|
|
EXPECT_EQ(original_tm.fileCount(), modified_tm.fileCount());
|
|
EXPECT_EQ(original_tm.dateCreated(), modified_tm.dateCreated());
|
|
EXPECT_EQ(original_tm.pieceCount(), modified_tm.pieceCount());
|
|
|
|
// test that the saved version has the updated announce list
|
|
EXPECT_TRUE(std::equal(
|
|
std::begin(announce_list),
|
|
std::end(announce_list),
|
|
std::begin(modified_tm.announceList()),
|
|
std::end(modified_tm.announceList())));
|
|
|
|
// cleanup
|
|
std::remove(test_file.c_str());
|
|
}
|