transmission/tests/libtransmission/announcer-test.cc

310 lines
9.8 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 <algorithm>
#include <array>
#include <cassert>
#include <cstddef> // std::byte
#include <optional>
#include <string_view>
#define LIBTRANSMISSION_ANNOUNCER_MODULE
#include <libtransmission/announcer-common.h>
#include <libtransmission/net.h>
#include "gtest/gtest.h"
using AnnouncerTest = ::testing::Test;
using namespace std::literals;
static char const* const LogName = "LogName";
TEST_F(AnnouncerTest, parseHttpAnnounceResponseNoPeers)
{
// clang-format off
auto constexpr NoPeers =
"d"
"8:complete" "i3e"
"10:downloaded" "i2e"
"10:incomplete" "i0e"
"8:interval" "i1803e"
"12:min interval" "i1800e"
"5:peers" "0:"
"11:external ip" "4:\x01\x02\x03\x04"
"e"sv;
// clang-format on
auto response = tr_announce_response{};
tr_announcerParseHttpAnnounceResponse(response, NoPeers, LogName);
EXPECT_EQ(1803, response.interval);
EXPECT_EQ(1800, response.min_interval);
EXPECT_EQ(3, response.seeders);
EXPECT_EQ(0, response.leechers);
EXPECT_EQ(2, response.downloads);
auto addr = tr_address::from_string("1.2.3.4");
EXPECT_TRUE(addr.has_value());
assert(addr.has_value());
EXPECT_EQ(*addr, response.external_ip);
EXPECT_EQ(0U, std::size(response.pex));
EXPECT_EQ(0U, std::size(response.pex6));
EXPECT_EQ(""sv, response.errmsg);
EXPECT_EQ(""sv, response.warning);
}
TEST_F(AnnouncerTest, parseHttpAnnounceResponsePexCompact)
{
// clang-format off
auto constexpr IPv4Peers =
"d"
"8:complete" "i3e"
"10:downloaded" "i2e"
"10:incomplete" "i0e"
"8:interval" "i1803e"
"12:min interval" "i1800e"
"5:peers"
"6:\x7F\x00\x00\x01\xfc\x27"
"e"sv;
// clang-format on
auto response = tr_announce_response{};
tr_announcerParseHttpAnnounceResponse(response, IPv4Peers, LogName);
EXPECT_EQ(1803, response.interval);
EXPECT_EQ(1800, response.min_interval);
EXPECT_EQ(3, response.seeders);
EXPECT_EQ(0, response.leechers);
EXPECT_EQ(2, response.downloads);
EXPECT_EQ(""sv, response.errmsg);
EXPECT_EQ(""sv, response.warning);
EXPECT_EQ(1U, std::size(response.pex));
EXPECT_EQ(0U, std::size(response.pex6));
if (std::size(response.pex) == 1)
{
EXPECT_EQ("127.0.0.1:64551"sv, response.pex[0].display_name());
}
}
TEST_F(AnnouncerTest, parseHttpAnnounceResponsePexList)
{
// clang-format off
auto constexpr IPv4Peers =
"d"
"8:complete" "i3e"
"10:downloaded" "i2e"
"10:incomplete" "i0e"
"8:interval" "i1803e"
"12:min interval" "i1800e"
"5:peers"
"l"
"d"
"7:peer id" "20:-TR300Z-0123456789AB"
"2:ip" "7:8.8.4.4"
"4:port" "i53e"
"e"
"e"
"e"sv;
// clang-format on
auto response = tr_announce_response{};
tr_announcerParseHttpAnnounceResponse(response, IPv4Peers, LogName);
EXPECT_EQ(1803, response.interval);
EXPECT_EQ(1800, response.min_interval);
EXPECT_EQ(3, response.seeders);
EXPECT_EQ(0, response.leechers);
EXPECT_EQ(2, response.downloads);
EXPECT_EQ(""sv, response.errmsg);
EXPECT_EQ(""sv, response.warning);
EXPECT_EQ(1U, std::size(response.pex));
EXPECT_EQ(0U, std::size(response.pex6));
if (std::size(response.pex) == 1)
{
EXPECT_EQ("8.8.4.4:53"sv, response.pex[0].display_name());
}
}
TEST_F(AnnouncerTest, parseHttpAnnounceResponseFailureReason)
{
// clang-format off
auto constexpr NoPeers =
"d"
"8:complete" "i3e"
"14:failure reason" "6:foobar"
"10:downloaded" "i2e"
"10:incomplete" "i0e"
"8:interval" "i1803e"
"12:min interval" "i1800e"
"5:peers" "0:"
"e"sv;
// clang-format on
auto response = tr_announce_response{};
tr_announcerParseHttpAnnounceResponse(response, NoPeers, LogName);
EXPECT_EQ(1803, response.interval);
EXPECT_EQ(1800, response.min_interval);
EXPECT_EQ(3, response.seeders);
EXPECT_EQ(0, response.leechers);
EXPECT_EQ(2, response.downloads);
EXPECT_EQ(0U, std::size(response.pex));
EXPECT_EQ(0U, std::size(response.pex6));
EXPECT_EQ("foobar"sv, response.errmsg);
EXPECT_EQ(""sv, response.warning);
}
TEST_F(AnnouncerTest, parseHttpScrapeResponseMulti)
{
// clang-format off
auto constexpr ResponseBenc =
"d"
"5:files"
"d"
"20:aaaaaaaaaaaaaaaaaaaa"
"d"
"8:complete" "i1e"
"10:incomplete" "i2e"
"10:downloaded" "i3e"
"e"
"20:bbbbbbbbbbbbbbbbbbbb"
"d"
"8:complete" "i4e"
"10:incomplete" "i5e"
"10:downloaded" "i6e"
"e"
"20:cccccccccccccccccccc"
"d"
"8:complete" "i7e"
"10:incomplete" "i8e"
"10:downloaded" "i9e"
"e"
"e"
"e"sv;
// clang-format on
auto response = tr_scrape_response{};
std::fill_n(std::data(response.rows[0].info_hash), std::size(response.rows[0].info_hash), std::byte{ 'a' });
std::fill_n(std::data(response.rows[1].info_hash), std::size(response.rows[1].info_hash), std::byte{ 'b' });
std::fill_n(std::data(response.rows[2].info_hash), std::size(response.rows[2].info_hash), std::byte{ 'c' });
response.row_count = 3;
tr_announcerParseHttpScrapeResponse(response, ResponseBenc, LogName);
EXPECT_EQ(1, response.rows[0].seeders);
EXPECT_EQ(2, response.rows[0].leechers);
EXPECT_EQ(3, response.rows[0].downloads);
EXPECT_EQ(4, response.rows[1].seeders);
EXPECT_EQ(5, response.rows[1].leechers);
EXPECT_EQ(6, response.rows[1].downloads);
EXPECT_EQ(7, response.rows[2].seeders);
EXPECT_EQ(8, response.rows[2].leechers);
EXPECT_EQ(9, response.rows[2].downloads);
}
TEST_F(AnnouncerTest, parseHttpScrapeResponseMultiWithUnexpected)
{
// clang-format off
auto constexpr ResponseBenc =
"d"
"5:files"
"d"
"20:aaaaaaaaaaaaaaaaaaaa"
"d"
"8:complete" "i1e"
"10:incomplete" "i2e"
"10:downloaded" "i3e"
"e"
"20:bbbbbbbbbbbbbbbbbbbb"
"d"
"8:complete" "i4e"
"10:incomplete" "i5e"
"10:downloaded" "i6e"
"e"
"20:cccccccccccccccccccc"
"d"
"8:complete" "i7e"
"10:incomplete" "i8e"
"10:downloaded" "i9e"
"e"
"20:dddddddddddddddddddd"
"d"
"8:complete" "i7e"
"10:incomplete" "i8e"
"10:downloaded" "i9e"
"e"
"e"
"e"sv;
// clang-format on
auto response = tr_scrape_response{};
std::fill_n(std::data(response.rows[0].info_hash), std::size(response.rows[0].info_hash), std::byte{ 'a' });
std::fill_n(std::data(response.rows[1].info_hash), std::size(response.rows[1].info_hash), std::byte{ 'b' });
std::fill_n(std::data(response.rows[2].info_hash), std::size(response.rows[2].info_hash), std::byte{ 'c' });
response.row_count = 3;
tr_announcerParseHttpScrapeResponse(response, ResponseBenc, LogName);
EXPECT_EQ(1, response.rows[0].seeders);
EXPECT_EQ(2, response.rows[0].leechers);
EXPECT_EQ(3, response.rows[0].downloads);
EXPECT_EQ(4, response.rows[1].seeders);
EXPECT_EQ(5, response.rows[1].leechers);
EXPECT_EQ(6, response.rows[1].downloads);
EXPECT_EQ(7, response.rows[2].seeders);
EXPECT_EQ(8, response.rows[2].leechers);
EXPECT_EQ(9, response.rows[2].downloads);
}
TEST_F(AnnouncerTest, parseHttpScrapeResponseMultiWithMissing)
{
// clang-format off
auto constexpr ResponseBenc =
"d"
"5:files"
"d"
"20:aaaaaaaaaaaaaaaaaaaa"
"d"
"8:complete" "i1e"
"10:incomplete" "i2e"
"10:downloaded" "i3e"
"e"
"20:cccccccccccccccccccc"
"d"
"8:complete" "i7e"
"10:incomplete" "i8e"
"10:downloaded" "i9e"
"e"
"e"
"e"sv;
// clang-format on
auto response = tr_scrape_response{};
std::fill_n(std::data(response.rows[0].info_hash), std::size(response.rows[0].info_hash), std::byte{ 'a' });
std::fill_n(std::data(response.rows[1].info_hash), std::size(response.rows[1].info_hash), std::byte{ 'b' });
std::fill_n(std::data(response.rows[2].info_hash), std::size(response.rows[2].info_hash), std::byte{ 'c' });
response.row_count = 3;
tr_announcerParseHttpScrapeResponse(response, ResponseBenc, LogName);
EXPECT_EQ(1, response.rows[0].seeders);
EXPECT_EQ(2, response.rows[0].leechers);
EXPECT_EQ(3, response.rows[0].downloads);
EXPECT_EQ(std::nullopt, response.rows[1].seeders);
EXPECT_EQ(std::nullopt, response.rows[1].leechers);
EXPECT_EQ(std::nullopt, response.rows[1].downloads);
EXPECT_EQ(7, response.rows[2].seeders);
EXPECT_EQ(8, response.rows[2].leechers);
EXPECT_EQ(9, response.rows[2].downloads);
}