Init: Migrate SimpleRemoter (Since v1.3.1) to Gitea

This commit is contained in:
yuanyuanxiang
2026-04-19 19:55:01 +02:00
commit 5a325a202b
744 changed files with 235562 additions and 0 deletions

View File

@@ -0,0 +1,629 @@
/**
* @file ChunkManagerTest.cpp
* @brief 分块管理和区间跟踪测试
*
* 测试覆盖:
* - FileRangeV2 区间操作
* - 接收状态跟踪
* - 区间合并算法
* - 断点续传区间计算
*/
#include <gtest/gtest.h>
#include <vector>
#include <algorithm>
#include <cstdint>
// ============================================
// 区间结构(测试专用)
// ============================================
struct Range {
uint64_t offset;
uint64_t length;
Range(uint64_t o = 0, uint64_t l = 0) : offset(o), length(l) {}
uint64_t end() const { return offset + length; }
bool operator<(const Range& other) const {
return offset < other.offset;
}
bool operator==(const Range& other) const {
return offset == other.offset && length == other.length;
}
};
// ============================================
// 区间管理类(断点续传核心逻辑)
// ============================================
class RangeManager {
public:
RangeManager(uint64_t fileSize = 0) : m_fileSize(fileSize), m_receivedBytes(0) {}
// 添加已接收区间
void AddRange(uint64_t offset, uint64_t length) {
if (length == 0) return;
Range newRange(offset, length);
m_ranges.push_back(newRange);
MergeRanges();
UpdateReceivedBytes();
}
// 获取已接收区间列表
const std::vector<Range>& GetRanges() const {
return m_ranges;
}
// 获取缺失区间列表
std::vector<Range> GetMissingRanges() const {
std::vector<Range> missing;
if (m_fileSize == 0) return missing;
uint64_t currentPos = 0;
for (const auto& r : m_ranges) {
if (r.offset > currentPos) {
missing.emplace_back(currentPos, r.offset - currentPos);
}
currentPos = r.end();
}
if (currentPos < m_fileSize) {
missing.emplace_back(currentPos, m_fileSize - currentPos);
}
return missing;
}
// 获取已接收字节数
uint64_t GetReceivedBytes() const {
return m_receivedBytes;
}
// 是否完整接收
bool IsComplete() const {
return m_receivedBytes >= m_fileSize && m_fileSize > 0;
}
// 清空
void Clear() {
m_ranges.clear();
m_receivedBytes = 0;
}
// 设置文件大小
void SetFileSize(uint64_t size) {
m_fileSize = size;
}
uint64_t GetFileSize() const {
return m_fileSize;
}
private:
void MergeRanges() {
if (m_ranges.size() <= 1) return;
std::sort(m_ranges.begin(), m_ranges.end());
std::vector<Range> merged;
merged.push_back(m_ranges[0]);
for (size_t i = 1; i < m_ranges.size(); ++i) {
Range& last = merged.back();
const Range& current = m_ranges[i];
// 检查是否可以合并(相邻或重叠)
if (current.offset <= last.end()) {
// 扩展现有区间
uint64_t newEnd = std::max(last.end(), current.end());
last.length = newEnd - last.offset;
} else {
// 新的独立区间
merged.push_back(current);
}
}
m_ranges = std::move(merged);
}
void UpdateReceivedBytes() {
m_receivedBytes = 0;
for (const auto& r : m_ranges) {
m_receivedBytes += r.length;
}
}
std::vector<Range> m_ranges;
uint64_t m_fileSize;
uint64_t m_receivedBytes;
};
// ============================================
// 接收状态类(模拟 FileRecvStateV2
// ============================================
class FileRecvState {
public:
FileRecvState() : m_fileSize(0), m_fileIndex(0), m_transferID(0) {}
void Initialize(uint64_t transferID, uint32_t fileIndex, uint64_t fileSize, const std::string& filePath) {
m_transferID = transferID;
m_fileIndex = fileIndex;
m_fileSize = fileSize;
m_filePath = filePath;
m_rangeManager.SetFileSize(fileSize);
}
void AddChunk(uint64_t offset, uint64_t length) {
m_rangeManager.AddRange(offset, length);
}
bool IsComplete() const {
return m_rangeManager.IsComplete();
}
uint64_t GetReceivedBytes() const {
return m_rangeManager.GetReceivedBytes();
}
double GetProgress() const {
if (m_fileSize == 0) return 0.0;
return static_cast<double>(GetReceivedBytes()) / m_fileSize * 100.0;
}
std::vector<Range> GetMissingRanges() const {
return m_rangeManager.GetMissingRanges();
}
const std::vector<Range>& GetReceivedRanges() const {
return m_rangeManager.GetRanges();
}
uint64_t GetTransferID() const { return m_transferID; }
uint32_t GetFileIndex() const { return m_fileIndex; }
uint64_t GetFileSize() const { return m_fileSize; }
const std::string& GetFilePath() const { return m_filePath; }
private:
uint64_t m_transferID;
uint32_t m_fileIndex;
uint64_t m_fileSize;
std::string m_filePath;
RangeManager m_rangeManager;
};
// ============================================
// Range 基础测试
// ============================================
class RangeTest : public ::testing::Test {};
TEST_F(RangeTest, DefaultConstruction) {
Range r;
EXPECT_EQ(r.offset, 0u);
EXPECT_EQ(r.length, 0u);
EXPECT_EQ(r.end(), 0u);
}
TEST_F(RangeTest, Construction) {
Range r(100, 200);
EXPECT_EQ(r.offset, 100u);
EXPECT_EQ(r.length, 200u);
EXPECT_EQ(r.end(), 300u);
}
TEST_F(RangeTest, Comparison) {
Range r1(0, 100);
Range r2(100, 100);
Range r3(0, 100);
EXPECT_TRUE(r1 < r2);
EXPECT_FALSE(r2 < r1);
EXPECT_TRUE(r1 == r3);
EXPECT_FALSE(r1 == r2);
}
// ============================================
// RangeManager 测试
// ============================================
class RangeManagerTest : public ::testing::Test {};
TEST_F(RangeManagerTest, InitialState) {
RangeManager rm(1000);
EXPECT_EQ(rm.GetReceivedBytes(), 0u);
EXPECT_EQ(rm.GetFileSize(), 1000u);
EXPECT_FALSE(rm.IsComplete());
EXPECT_TRUE(rm.GetRanges().empty());
}
TEST_F(RangeManagerTest, AddSingleRange) {
RangeManager rm(1000);
rm.AddRange(0, 500);
EXPECT_EQ(rm.GetReceivedBytes(), 500u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetRanges()[0].offset, 0u);
EXPECT_EQ(rm.GetRanges()[0].length, 500u);
}
TEST_F(RangeManagerTest, AddZeroLengthRange) {
RangeManager rm(1000);
rm.AddRange(0, 0);
EXPECT_EQ(rm.GetReceivedBytes(), 0u);
EXPECT_TRUE(rm.GetRanges().empty());
}
TEST_F(RangeManagerTest, AddNonOverlappingRanges) {
RangeManager rm(1000);
rm.AddRange(0, 100);
rm.AddRange(200, 100);
rm.AddRange(400, 100);
EXPECT_EQ(rm.GetReceivedBytes(), 300u);
ASSERT_EQ(rm.GetRanges().size(), 3u);
}
TEST_F(RangeManagerTest, MergeAdjacentRanges) {
RangeManager rm(1000);
rm.AddRange(0, 100);
rm.AddRange(100, 100); // 紧邻
EXPECT_EQ(rm.GetReceivedBytes(), 200u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetRanges()[0].offset, 0u);
EXPECT_EQ(rm.GetRanges()[0].length, 200u);
}
TEST_F(RangeManagerTest, MergeOverlappingRanges) {
RangeManager rm(1000);
rm.AddRange(0, 150);
rm.AddRange(100, 150); // 重叠
EXPECT_EQ(rm.GetReceivedBytes(), 250u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetRanges()[0].offset, 0u);
EXPECT_EQ(rm.GetRanges()[0].length, 250u);
}
TEST_F(RangeManagerTest, MergeContainedRange) {
RangeManager rm(1000);
rm.AddRange(0, 500);
rm.AddRange(100, 100); // 完全被包含
EXPECT_EQ(rm.GetReceivedBytes(), 500u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetRanges()[0].length, 500u);
}
TEST_F(RangeManagerTest, MergeOutOfOrder) {
RangeManager rm(1000);
rm.AddRange(500, 100);
rm.AddRange(100, 100);
rm.AddRange(0, 100);
EXPECT_EQ(rm.GetReceivedBytes(), 300u);
// 0-100 和 100-200 被合并成 0-200加上 500-600共 2 个区间
ASSERT_EQ(rm.GetRanges().size(), 2u);
// 验证排序和合并
EXPECT_EQ(rm.GetRanges()[0].offset, 0u);
EXPECT_EQ(rm.GetRanges()[0].length, 200u); // 合并后
EXPECT_EQ(rm.GetRanges()[1].offset, 500u);
EXPECT_EQ(rm.GetRanges()[1].length, 100u);
}
TEST_F(RangeManagerTest, MergeMultipleOverlapping) {
RangeManager rm(1000);
rm.AddRange(0, 100);
rm.AddRange(200, 100);
rm.AddRange(50, 200); // 跨越两个区间
EXPECT_EQ(rm.GetReceivedBytes(), 300u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetRanges()[0].offset, 0u);
EXPECT_EQ(rm.GetRanges()[0].length, 300u);
}
TEST_F(RangeManagerTest, GetMissingRanges_Empty) {
RangeManager rm(1000);
auto missing = rm.GetMissingRanges();
ASSERT_EQ(missing.size(), 1u);
EXPECT_EQ(missing[0].offset, 0u);
EXPECT_EQ(missing[0].length, 1000u);
}
TEST_F(RangeManagerTest, GetMissingRanges_Partial) {
RangeManager rm(1000);
rm.AddRange(0, 100);
rm.AddRange(500, 100);
auto missing = rm.GetMissingRanges();
ASSERT_EQ(missing.size(), 2u);
EXPECT_EQ(missing[0].offset, 100u);
EXPECT_EQ(missing[0].length, 400u);
EXPECT_EQ(missing[1].offset, 600u);
EXPECT_EQ(missing[1].length, 400u);
}
TEST_F(RangeManagerTest, GetMissingRanges_Complete) {
RangeManager rm(1000);
rm.AddRange(0, 1000);
auto missing = rm.GetMissingRanges();
EXPECT_TRUE(missing.empty());
}
TEST_F(RangeManagerTest, IsComplete_Exact) {
RangeManager rm(1000);
rm.AddRange(0, 1000);
EXPECT_TRUE(rm.IsComplete());
}
TEST_F(RangeManagerTest, IsComplete_OverReceived) {
RangeManager rm(1000);
rm.AddRange(0, 1500); // 超过文件大小
EXPECT_TRUE(rm.IsComplete());
}
TEST_F(RangeManagerTest, IsComplete_Partial) {
RangeManager rm(1000);
rm.AddRange(0, 999);
EXPECT_FALSE(rm.IsComplete());
}
TEST_F(RangeManagerTest, Clear) {
RangeManager rm(1000);
rm.AddRange(0, 500);
rm.Clear();
EXPECT_EQ(rm.GetReceivedBytes(), 0u);
EXPECT_TRUE(rm.GetRanges().empty());
}
// ============================================
// FileRecvState 测试
// ============================================
class FileRecvStateTest : public ::testing::Test {};
TEST_F(FileRecvStateTest, Initialize) {
FileRecvState state;
state.Initialize(12345, 0, 1024, "C:\\test\\file.txt");
EXPECT_EQ(state.GetTransferID(), 12345u);
EXPECT_EQ(state.GetFileIndex(), 0u);
EXPECT_EQ(state.GetFileSize(), 1024u);
EXPECT_EQ(state.GetFilePath(), "C:\\test\\file.txt");
EXPECT_EQ(state.GetReceivedBytes(), 0u);
EXPECT_FALSE(state.IsComplete());
}
TEST_F(FileRecvStateTest, AddChunks) {
FileRecvState state;
state.Initialize(1, 0, 1000, "file.txt");
state.AddChunk(0, 100);
EXPECT_EQ(state.GetReceivedBytes(), 100u);
EXPECT_NEAR(state.GetProgress(), 10.0, 0.01);
state.AddChunk(100, 400);
EXPECT_EQ(state.GetReceivedBytes(), 500u);
EXPECT_NEAR(state.GetProgress(), 50.0, 0.01);
state.AddChunk(500, 500);
EXPECT_EQ(state.GetReceivedBytes(), 1000u);
EXPECT_TRUE(state.IsComplete());
EXPECT_NEAR(state.GetProgress(), 100.0, 0.01);
}
TEST_F(FileRecvStateTest, OutOfOrderChunks) {
FileRecvState state;
state.Initialize(1, 0, 1000, "file.txt");
state.AddChunk(500, 200);
state.AddChunk(0, 200);
state.AddChunk(800, 200);
EXPECT_EQ(state.GetReceivedBytes(), 600u);
auto missing = state.GetMissingRanges();
ASSERT_EQ(missing.size(), 2u);
EXPECT_EQ(missing[0].offset, 200u);
EXPECT_EQ(missing[0].length, 300u);
EXPECT_EQ(missing[1].offset, 700u);
EXPECT_EQ(missing[1].length, 100u);
}
TEST_F(FileRecvStateTest, DuplicateChunks) {
FileRecvState state;
state.Initialize(1, 0, 1000, "file.txt");
state.AddChunk(0, 500);
state.AddChunk(0, 500); // 重复
state.AddChunk(250, 250); // 重叠
EXPECT_EQ(state.GetReceivedBytes(), 500u);
}
// ============================================
// 断点续传场景测试
// ============================================
class ResumeScenarioTest : public ::testing::Test {};
TEST_F(ResumeScenarioTest, SimulateInterruptedTransfer) {
FileRecvState state;
state.Initialize(12345, 0, 10000, "large_file.bin");
// 模拟接收了一些数据后中断
state.AddChunk(0, 2000);
state.AddChunk(2000, 2000);
state.AddChunk(5000, 1000);
EXPECT_EQ(state.GetReceivedBytes(), 5000u);
EXPECT_NEAR(state.GetProgress(), 50.0, 0.01);
// 获取需要续传的区间
auto missing = state.GetMissingRanges();
ASSERT_EQ(missing.size(), 2u);
// 验证缺失区间
EXPECT_EQ(missing[0].offset, 4000u);
EXPECT_EQ(missing[0].length, 1000u);
EXPECT_EQ(missing[1].offset, 6000u);
EXPECT_EQ(missing[1].length, 4000u);
}
TEST_F(ResumeScenarioTest, ResumeAndComplete) {
FileRecvState state;
state.Initialize(12345, 0, 10000, "large_file.bin");
// 初始接收
state.AddChunk(0, 3000);
state.AddChunk(7000, 3000);
EXPECT_FALSE(state.IsComplete());
// 续传缺失部分
auto missing = state.GetMissingRanges();
for (const auto& r : missing) {
state.AddChunk(r.offset, r.length);
}
EXPECT_TRUE(state.IsComplete());
EXPECT_EQ(state.GetReceivedBytes(), 10000u);
}
TEST_F(ResumeScenarioTest, SmallChunksReassembly) {
FileRecvState state;
state.Initialize(1, 0, 1000, "file.txt");
// 模拟接收很多小块
for (uint64_t i = 0; i < 1000; i += 10) {
state.AddChunk(i, 10);
}
EXPECT_TRUE(state.IsComplete());
// 验证区间已合并
const auto& ranges = state.GetReceivedRanges();
EXPECT_EQ(ranges.size(), 1u);
EXPECT_EQ(ranges[0].offset, 0u);
EXPECT_EQ(ranges[0].length, 1000u);
}
// ============================================
// 大文件场景测试
// ============================================
class LargeFileScenarioTest : public ::testing::Test {};
TEST_F(LargeFileScenarioTest, FileGreaterThan4GB) {
FileRecvState state;
uint64_t fileSize = 5ULL * 1024 * 1024 * 1024; // 5 GB
state.Initialize(1, 0, fileSize, "huge.bin");
uint64_t chunkSize = 64 * 1024; // 64 KB chunks
// 添加几个大区间
state.AddChunk(0, 1ULL * 1024 * 1024 * 1024); // 1 GB
state.AddChunk(3ULL * 1024 * 1024 * 1024, 1ULL * 1024 * 1024 * 1024); // 1 GB at 3GB
EXPECT_EQ(state.GetReceivedBytes(), 2ULL * 1024 * 1024 * 1024);
auto missing = state.GetMissingRanges();
ASSERT_EQ(missing.size(), 2u);
// 1GB-3GB 缺失
EXPECT_EQ(missing[0].offset, 1ULL * 1024 * 1024 * 1024);
EXPECT_EQ(missing[0].length, 2ULL * 1024 * 1024 * 1024);
// 4GB-5GB 缺失
EXPECT_EQ(missing[1].offset, 4ULL * 1024 * 1024 * 1024);
EXPECT_EQ(missing[1].length, 1ULL * 1024 * 1024 * 1024);
}
// ============================================
// 边界条件测试
// ============================================
class ChunkBoundaryTest : public ::testing::Test {};
TEST_F(ChunkBoundaryTest, ZeroFileSize) {
FileRecvState state;
state.Initialize(1, 0, 0, "empty.txt");
EXPECT_FALSE(state.IsComplete()); // 0大小文件不算完成
EXPECT_TRUE(state.GetMissingRanges().empty());
}
TEST_F(ChunkBoundaryTest, SingleByteFile) {
FileRecvState state;
state.Initialize(1, 0, 1, "tiny.txt");
state.AddChunk(0, 1);
EXPECT_TRUE(state.IsComplete());
}
TEST_F(ChunkBoundaryTest, MaxValues) {
RangeManager rm(UINT64_MAX);
// 添加接近最大值的区间
rm.AddRange(UINT64_MAX - 1000, 500);
EXPECT_EQ(rm.GetReceivedBytes(), 500u);
}
TEST_F(ChunkBoundaryTest, OverlappingAtBoundary) {
RangeManager rm(1000);
rm.AddRange(0, 500);
rm.AddRange(499, 2); // 重叠1字节
EXPECT_EQ(rm.GetReceivedBytes(), 501u);
ASSERT_EQ(rm.GetRanges().size(), 1u);
}
// ============================================
// 性能相关测试
// ============================================
class ChunkPerformanceTest : public ::testing::Test {};
TEST_F(ChunkPerformanceTest, ManySmallRanges) {
RangeManager rm(1000000);
// 添加大量不连续的小区间
for (uint64_t i = 0; i < 1000000; i += 20) {
rm.AddRange(i, 10);
}
// 验证区间数量合理
EXPECT_LE(rm.GetRanges().size(), 50000u);
EXPECT_EQ(rm.GetReceivedBytes(), 500000u);
}
TEST_F(ChunkPerformanceTest, ManyContiguousRanges) {
RangeManager rm(1000000);
// 添加大量连续的小区间(应该全部合并)
for (uint64_t i = 0; i < 1000; ++i) {
rm.AddRange(i * 1000, 1000);
}
// 应该合并成单个区间
ASSERT_EQ(rm.GetRanges().size(), 1u);
EXPECT_EQ(rm.GetReceivedBytes(), 1000000u);
EXPECT_TRUE(rm.IsComplete());
}