2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright (C) 2013-2022 Mnemosyne LLC.
|
2022-08-08 18:05:39 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <algorithm>
|
2022-06-20 04:08:58 +00:00
|
|
|
#include <memory>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <string>
|
2022-04-07 22:26:59 +00:00
|
|
|
#include <string_view>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <utility>
|
|
|
|
|
2023-01-02 16:23:51 +00:00
|
|
|
#include <libtransmission/transmission.h>
|
2021-12-15 21:25:42 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <libtransmission/block-info.h>
|
2023-01-02 16:23:51 +00:00
|
|
|
#include <libtransmission/cache.h> // tr_cacheWriteBlock()
|
|
|
|
#include <libtransmission/file.h> // tr_sys_path_*()
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <libtransmission/quark.h>
|
|
|
|
#include <libtransmission/torrent.h>
|
|
|
|
#include <libtransmission/torrent-files.h>
|
2023-01-02 16:23:51 +00:00
|
|
|
#include <libtransmission/tr-strbuf.h>
|
|
|
|
#include <libtransmission/variant.h>
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "gtest/gtest.h"
|
2020-08-11 18:11:55 +00:00
|
|
|
#include "test-fixtures.h"
|
|
|
|
|
2022-04-07 22:26:59 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2022-11-14 20:16:29 +00:00
|
|
|
namespace libtransmission::test
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
|
2022-03-21 20:22:50 +00:00
|
|
|
auto constexpr MaxWaitMsec = 5000;
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
class IncompleteDirTest
|
|
|
|
: public SessionTest
|
|
|
|
, public ::testing::WithParamInterface<std::pair<std::string, std::string>>
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
protected:
|
|
|
|
void SetUp() override
|
|
|
|
{
|
|
|
|
auto const download_dir = GetParam().second;
|
2023-11-21 15:02:03 +00:00
|
|
|
tr_variantDictAddStr(settings(), TR_KEY_download_dir, download_dir);
|
2022-03-21 20:22:50 +00:00
|
|
|
auto const incomplete_dir = GetParam().first;
|
2023-11-21 15:02:03 +00:00
|
|
|
tr_variantDictAddStr(settings(), TR_KEY_incomplete_dir, incomplete_dir);
|
2020-08-11 18:11:55 +00:00
|
|
|
tr_variantDictAddBool(settings(), TR_KEY_incomplete_dir_enabled, true);
|
|
|
|
|
|
|
|
SessionTest::SetUp();
|
|
|
|
}
|
2022-03-21 14:15:48 +00:00
|
|
|
|
|
|
|
static auto constexpr MaxWaitMsec = 3000;
|
2020-08-11 18:11:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(IncompleteDirTest, incompleteDir)
|
|
|
|
{
|
|
|
|
auto const* download_dir = tr_sessionGetDownloadDir(session_);
|
|
|
|
auto const* incomplete_dir = tr_sessionGetIncompleteDir(session_);
|
|
|
|
|
|
|
|
// init an incomplete torrent.
|
|
|
|
// the test zero_torrent will be missing its first piece.
|
2022-03-30 06:30:39 +00:00
|
|
|
auto* const tor = zeroTorrentInit(ZeroTorrentState::Partial);
|
2022-04-07 22:26:59 +00:00
|
|
|
auto path = tr_pathbuf{};
|
|
|
|
|
2022-04-15 23:39:04 +00:00
|
|
|
path.assign(incomplete_dir, '/', tr_torrentFile(tor, 0).name, tr_torrent_files::PartialFileSuffix);
|
2022-08-17 00:28:57 +00:00
|
|
|
EXPECT_EQ(path, tr_torrentFindFile(tor, 0));
|
2022-04-07 22:26:59 +00:00
|
|
|
path.assign(incomplete_dir, '/', tr_torrentFile(tor, 1).name);
|
2022-08-17 00:28:57 +00:00
|
|
|
EXPECT_EQ(path, tr_torrentFindFile(tor, 1));
|
2023-04-23 01:25:55 +00:00
|
|
|
EXPECT_EQ(tor->piece_size(), tr_torrentStat(tor)->leftUntilDone);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
// auto constexpr completeness_unset = tr_completeness { -1 };
|
|
|
|
// auto completeness = completeness_unset;
|
|
|
|
int completeness = -1;
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const zeroes_completeness_func =
|
|
|
|
[](tr_torrent* /*torrent*/, tr_completeness c, bool /*was_running*/, void* vc) noexcept
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
|
|
|
*static_cast<tr_completeness*>(vc) = c;
|
|
|
|
};
|
2022-07-23 06:04:34 +00:00
|
|
|
tr_sessionSetCompletenessCallback(session_, zeroes_completeness_func, &completeness);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
struct TestIncompleteDirData
|
|
|
|
{
|
|
|
|
tr_session* session = {};
|
|
|
|
tr_torrent* tor = {};
|
|
|
|
tr_block_index_t block = {};
|
|
|
|
tr_piece_index_t pieceIndex = {};
|
2024-02-17 19:31:49 +00:00
|
|
|
std::unique_ptr<Cache::BlockData> buf;
|
2020-08-11 18:11:55 +00:00
|
|
|
bool done = {};
|
|
|
|
};
|
|
|
|
|
2022-06-20 04:08:58 +00:00
|
|
|
auto const test_incomplete_dir_threadfunc = [](TestIncompleteDirData* data) noexcept
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
data->session->cache->write_block(data->tor->id(), data->block, std::move(data->buf));
|
2023-11-26 02:00:20 +00:00
|
|
|
data->tor->on_block_received(data->block);
|
2020-08-11 18:11:55 +00:00
|
|
|
data->done = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
// now finish writing it
|
|
|
|
{
|
2022-06-20 04:08:58 +00:00
|
|
|
auto data = TestIncompleteDirData{};
|
2020-08-11 18:11:55 +00:00
|
|
|
data.session = session_;
|
|
|
|
data.tor = tor;
|
|
|
|
|
2023-04-23 01:25:55 +00:00
|
|
|
auto const [begin, end] = tor->block_span_for_piece(data.pieceIndex);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2021-11-25 18:26:51 +00:00
|
|
|
for (tr_block_index_t block_index = begin; block_index < end; ++block_index)
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
2023-05-13 19:16:00 +00:00
|
|
|
data.buf = std::make_unique<Cache::BlockData>(tr_block_info::BlockSize);
|
|
|
|
std::fill_n(std::data(*data.buf), tr_block_info::BlockSize, '\0');
|
2020-08-11 18:11:55 +00:00
|
|
|
data.block = block_index;
|
|
|
|
data.done = false;
|
2023-12-24 14:32:14 +00:00
|
|
|
session_->run_in_session_thread(test_incomplete_dir_threadfunc, &data);
|
2020-08-11 18:11:55 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
auto const test = [&data]()
|
|
|
|
{
|
|
|
|
return data.done;
|
|
|
|
};
|
2022-03-21 14:15:48 +00:00
|
|
|
EXPECT_TRUE(waitFor(test, MaxWaitMsec));
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blockingTorrentVerify(tor);
|
|
|
|
EXPECT_EQ(0, tr_torrentStat(tor)->leftUntilDone);
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
auto test = [&completeness]()
|
|
|
|
{
|
|
|
|
return completeness != -1;
|
|
|
|
};
|
2022-03-21 20:22:50 +00:00
|
|
|
EXPECT_TRUE(waitFor(test, MaxWaitMsec));
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_EQ(TR_SEED, completeness);
|
|
|
|
|
2021-11-28 03:17:47 +00:00
|
|
|
auto const n = tr_torrentFileCount(tor);
|
|
|
|
for (tr_file_index_t i = 0; i < n; ++i)
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
2022-04-22 16:35:56 +00:00
|
|
|
auto const expected = tr_pathbuf{ download_dir, '/', tr_torrentFile(tor, i).name };
|
2022-08-17 00:28:57 +00:00
|
|
|
EXPECT_EQ(expected, tr_torrentFindFile(tor, i));
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup
|
2022-10-23 16:51:35 +00:00
|
|
|
tr_torrentRemove(tor, true, nullptr, nullptr);
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
|
|
IncompleteDir,
|
|
|
|
IncompleteDirTest,
|
|
|
|
::testing::Values(
|
2021-08-15 09:41:48 +00:00
|
|
|
// what happens when incompleteDir is a subdir of downloadDir
|
|
|
|
std::make_pair(std::string{ "Downloads/Incomplete" }, std::string{ "Downloads" }),
|
|
|
|
// test what happens when downloadDir is a subdir of incompleteDir
|
|
|
|
std::make_pair(std::string{ "Downloads" }, std::string{ "Downloads/Complete" }),
|
|
|
|
// test what happens when downloadDir and incompleteDir are siblings
|
|
|
|
std::make_pair(std::string{ "Incomplete" }, std::string{ "Downloads" })));
|
2020-08-11 18:11:55 +00:00
|
|
|
|
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
using MoveTest = SessionTest;
|
|
|
|
|
|
|
|
TEST_F(MoveTest, setLocation)
|
|
|
|
{
|
2022-07-23 07:13:18 +00:00
|
|
|
auto const target_dir = tr_pathbuf{ session_->configDir(), "/target"sv };
|
2020-08-11 18:11:55 +00:00
|
|
|
tr_sys_dir_create(target_dir.data(), TR_SYS_DIR_CREATE_PARENTS, 0777, nullptr);
|
|
|
|
|
|
|
|
// init a torrent.
|
2022-03-30 06:30:39 +00:00
|
|
|
auto* const tor = zeroTorrentInit(ZeroTorrentState::Complete);
|
2020-08-11 18:11:55 +00:00
|
|
|
blockingTorrentVerify(tor);
|
|
|
|
EXPECT_EQ(0, tr_torrentStat(tor)->leftUntilDone);
|
|
|
|
|
|
|
|
// now move it
|
2024-02-17 19:31:49 +00:00
|
|
|
auto state = -1;
|
2023-10-22 02:33:41 +00:00
|
|
|
tr_torrentSetLocation(tor, target_dir, true, &state);
|
2021-08-15 09:41:48 +00:00
|
|
|
auto test = [&state]()
|
|
|
|
{
|
|
|
|
return state == TR_LOC_DONE;
|
|
|
|
};
|
2022-03-21 20:22:50 +00:00
|
|
|
EXPECT_TRUE(waitFor(test, MaxWaitMsec));
|
2020-08-11 18:11:55 +00:00
|
|
|
EXPECT_EQ(TR_LOC_DONE, state);
|
|
|
|
|
|
|
|
// confirm the torrent is still complete after being moved
|
|
|
|
blockingTorrentVerify(tor);
|
|
|
|
EXPECT_EQ(0, tr_torrentStat(tor)->leftUntilDone);
|
|
|
|
|
|
|
|
// confirm the files really got moved
|
|
|
|
sync();
|
2021-11-28 03:17:47 +00:00
|
|
|
auto const n = tr_torrentFileCount(tor);
|
|
|
|
for (tr_file_index_t i = 0; i < n; ++i)
|
2020-08-11 18:11:55 +00:00
|
|
|
{
|
2022-04-22 16:35:56 +00:00
|
|
|
auto const expected = tr_pathbuf{ target_dir, '/', tr_torrentFile(tor, i).name };
|
2022-08-17 00:28:57 +00:00
|
|
|
EXPECT_EQ(expected, tr_torrentFindFile(tor, i));
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup
|
2022-10-23 16:51:35 +00:00
|
|
|
tr_torrentRemove(tor, true, nullptr, nullptr);
|
2020-08-11 18:11:55 +00:00
|
|
|
}
|
|
|
|
|
2022-11-14 20:16:29 +00:00
|
|
|
} // namespace libtransmission::test
|