From fa6fdfe95ed73a2bce8ff10228a21083c509589d Mon Sep 17 00:00:00 2001 From: Chenxi Date: Sat, 6 Jun 2026 15:04:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8D=95=E5=85=83=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- XYParser/XYParserTests/Tests.cpp | 379 +++++++++++++++++++++++++++++-- 1 file changed, 357 insertions(+), 22 deletions(-) diff --git a/XYParser/XYParserTests/Tests.cpp b/XYParser/XYParserTests/Tests.cpp index 2190a2c..c4c126c 100644 --- a/XYParser/XYParserTests/Tests.cpp +++ b/XYParser/XYParserTests/Tests.cpp @@ -1,3 +1,6 @@ +// XYParser API 单元测试文件 +// 测试 XYParser 库的核心功能,包括解析器创建、错误处理、帧解析等 + #include #include "../XYParserApi.h" @@ -7,12 +10,18 @@ #include #include +// 匿名命名空间,包含测试辅助代码 namespace { +/// ParserGuard 类:RAII 封装,确保解析器资源自动释放 +/// 当对象生命周期结束时自动调用 XYParser_DestroyParser 释放资源 class ParserGuard { public: + /// 构造函数,接管解析器句柄的所有权 + /// @param handle XYParser 解析器句柄 explicit ParserGuard(XYParserHandle handle) : handle_(handle) {} + /// 析构函数,自动释放解析器资源 ~ParserGuard() { if (handle_ != nullptr) { @@ -20,22 +29,29 @@ public: } } + /// 获取解析器句柄 + /// @return XYParser 解析器句柄 XYParserHandle get() const { return handle_; } private: - XYParserHandle handle_; + XYParserHandle handle_; ///< 解析器句柄 }; +/// 构建最小帧数据的辅助函数 +/// 生成符合 XYParser 协议格式的测试帧数据 +/// @param channel_count 通道数量 +/// @return 包含完整帧数据的字节向量 std::vector BuildMinimalFrame(std::uint8_t channel_count) { - constexpr std::size_t kSamplesPerFrame = 5; - constexpr std::uint8_t kHeader = 0xAA; - constexpr std::uint8_t kTail = 0x55; - constexpr std::size_t kTagLen = 25; + constexpr std::size_t kSamplesPerFrame = 5; ///< 每帧采样数 + constexpr std::uint8_t kHeader = 0xAA; ///< 帧头标记 + constexpr std::uint8_t kTail = 0x55; ///< 帧尾标记 + constexpr std::size_t kTagLen = 25; ///< 标签长度 + // 计算帧结构大小 const std::size_t sample_bytes = static_cast(channel_count) * 3 + 2; const std::uint16_t payload_length = static_cast(sample_bytes * kSamplesPerFrame); const std::size_t frame_size = 1 + kTagLen + payload_length + 2; @@ -43,31 +59,39 @@ std::vector BuildMinimalFrame(std::uint8_t channel_count) std::vector frame(frame_size, 0); std::size_t offset = 0; + // 写入帧头 frame[offset++] = kHeader; + // 写入标签数据(版本号等) frame[offset++] = 0x01; frame[offset++] = 0x00; frame[offset++] = 0x00; frame[offset++] = 0x00; + // 写入负载长度(大端序) frame[offset++] = static_cast((payload_length >> 8) & 0xFF); frame[offset++] = static_cast(payload_length & 0xFF); + // 写入电池电量和通道数 frame[offset++] = 95; frame[offset++] = channel_count; + // 跳过保留字段 offset += 2 + 2 + 2 + 2 + 2 + 6; + // 写入采样数据 for (std::size_t sample = 0; sample < kSamplesPerFrame; ++sample) { for (std::size_t channel = 0; channel < channel_count; ++channel) { frame[offset++] = 0x00; frame[offset++] = 0x00; frame[offset++] = static_cast(sample + channel + 1); } + // 每个采样后的额外字节 frame[offset++] = 0x00; frame[offset++] = 0x00; } + // 写入帧尾 frame[offset++] = 0x00; frame[offset++] = kTail; frame[offset++] = kTail; @@ -77,24 +101,32 @@ std::vector BuildMinimalFrame(std::uint8_t channel_count) } // namespace +/// 测试:创建解析器时拒绝不支持的通道数 TEST(XYParserApiTests, CreateParserRejectsUnsupportedChannelCount) { + // 7 通道是不支持的配置,应返回 nullptr EXPECT_EQ(XYParser_CreateParser(7), nullptr); } +/// 测试:对空解析器句柄调用 GetLastError 应返回正确错误信息 TEST(XYParserApiTests, GetLastErrorReturnsMessageForNullParser) { + // 传入 nullptr 应返回 "invalid parser handle" EXPECT_EQ(std::string(XYParser_GetLastError(nullptr)), std::string("invalid parser handle")); } +/// 测试:Feed 函数能正确解析完整的 8 通道帧 TEST(XYParserApiTests, FeedParsesAComplete8ChannelFrame) { + // 创建 8 通道解析器 ParserGuard parser(XYParser_CreateParser(8)); ASSERT_NE(parser.get(), nullptr); + // 设置 ADC 参数和校验和绕过标志 XYParser_SetAdcParams(parser.get(), 4.5, 6.0); XYParser_SetBypassChecksum(parser.get(), 1); + // 构建测试帧并解析 const std::vector bytes = BuildMinimalFrame(8); std::array summaries{}; @@ -105,17 +137,255 @@ TEST(XYParserApiTests, FeedParsesAComplete8ChannelFrame) summaries.data(), static_cast(summaries.size())); - ASSERT_EQ(frame_count, 1); - EXPECT_EQ(summaries[0].frame_index, static_cast(1)); - EXPECT_EQ(summaries[0].channel_count, static_cast(8)); - EXPECT_EQ(summaries[0].battery, static_cast(95)); - EXPECT_EQ(summaries[0].sample_count, static_cast(5)); - EXPECT_GT(summaries[0].channel_values_uv[0][0], 0.0); - EXPECT_EQ(summaries[0].trigger_types[0], static_cast(0)); - EXPECT_EQ(summaries[0].trigger_indices[0], static_cast(0)); + // 验证解析结果 + ASSERT_EQ(frame_count, 1); // 应解析出 1 帧 + EXPECT_EQ(summaries[0].frame_index, 1U); // 帧索引应为 1 + EXPECT_EQ(summaries[0].channel_count, 8U); // 通道数应为 8 + EXPECT_EQ(summaries[0].battery, 95U); // 电池电量应为 95 + EXPECT_EQ(summaries[0].sample_count, 5U); // 采样数应为 5 + EXPECT_GT(summaries[0].channel_values_uv[0][0], 0.0); // 通道值应大于 0 + EXPECT_EQ(summaries[0].trigger_types[0], 0U); // 触发类型应为 0 + EXPECT_EQ(summaries[0].trigger_indices[0], 0U); // 触发索引应为 0 } +/// 测试:Feed 函数能缓冲部分数据直到完整帧可用 TEST(XYParserApiTests, FeedBuffersPartialDataUntilAFullFrameIsAvailable) +{ + // 创建 8 通道解析器 + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + // 设置校验和绕过标志 + XYParser_SetBypassChecksum(parser.get(), 1); + + // 构建测试帧并分成两部分 + const std::vector bytes = BuildMinimalFrame(8); + const std::size_t split_index = bytes.size() / 2; + std::array summaries{}; + + // 第一次 Feed:传入前半部分数据 + const int first_result = XYParser_Feed( + parser.get(), + bytes.data(), + split_index, + summaries.data(), + static_cast(summaries.size())); + EXPECT_EQ(first_result, 0); // 数据不完整,不应解析出帧 + + // 第二次 Feed:传入后半部分数据 + const int second_result = XYParser_Feed( + parser.get(), + bytes.data() + split_index, + bytes.size() - split_index, + summaries.data(), + static_cast(summaries.size())); + ASSERT_EQ(second_result, 1); // 数据完整,应解析出 1 帧 + EXPECT_EQ(summaries[0].frame_index, 1U); // 帧索引应为 1 +} + +// ============================================================================ +// 解析器创建测试 +// ============================================================================ + +/// 测试:成功创建 8 通道解析器 +TEST(XYParserApiTests, CreateParserSucceedsFor8Channels) +{ + ParserGuard parser(XYParser_CreateParser(8)); + EXPECT_NE(parser.get(), nullptr); +} + +/// 测试:成功创建 64 通道解析器 +TEST(XYParserApiTests, CreateParserSucceedsFor64Channels) +{ + ParserGuard parser(XYParser_CreateParser(64)); + EXPECT_NE(parser.get(), nullptr); +} + +/// 测试:创建 0 通道解析器应失败 +TEST(XYParserApiTests, CreateParserRejectsZeroChannels) +{ + EXPECT_EQ(XYParser_CreateParser(0), nullptr); +} + +/// 测试:创建不支持的通道数(如 100)应失败 +TEST(XYParserApiTests, CreateParserRejectsExcessiveChannels) +{ + EXPECT_EQ(XYParser_CreateParser(100), nullptr); +} + +// ============================================================================ +// 参数设置函数测试 +// ============================================================================ + +/// 测试:设置正常的 ADC 参数 +TEST(XYParserApiTests, SetAdcParamsAcceptsValidValues) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + // 不应崩溃 + EXPECT_NO_THROW(XYParser_SetAdcParams(parser.get(), 4.5, 6.0)); +} + +/// 测试:设置边界值的 ADC 参数 +TEST(XYParserApiTests, SetAdcParamsAcceptsBoundaryValues) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + // 测试零值 + EXPECT_NO_THROW(XYParser_SetAdcParams(parser.get(), 0.0, 0.0)); + // 测试较大值 + EXPECT_NO_THROW(XYParser_SetAdcParams(parser.get(), 100.0, 1000.0)); +} + +/// 测试:对空句柄调用 SetAdcParams +TEST(XYParserApiTests, SetAdcParamsOnNullHandle) +{ + // 不应崩溃 + EXPECT_NO_THROW(XYParser_SetAdcParams(nullptr, 4.5, 6.0)); +} + +/// 测试:关闭校验和绕过 +TEST(XYParserApiTests, SetBypassChecksumOff) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + EXPECT_NO_THROW(XYParser_SetBypassChecksum(parser.get(), 0)); +} + +/// 测试:对空句柄调用 SetBypassChecksum +TEST(XYParserApiTests, SetBypassChecksumOnNullHandle) +{ + EXPECT_NO_THROW(XYParser_SetBypassChecksum(nullptr, 1)); +} + +// ============================================================================ +// Feed 函数帧解析测试 +// ============================================================================ + +/// 测试:传入空数据 +TEST(XYParserApiTests, FeedParsesEmptyData) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + std::array summaries{}; + const int result = XYParser_Feed( + parser.get(), + nullptr, + 0, + summaries.data(), + static_cast(summaries.size())); + + EXPECT_EQ(result, 0); +} + +/// 测试:只传入帧头数据 +TEST(XYParserApiTests, FeedParsesOnlyHeader) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + XYParser_SetBypassChecksum(parser.get(), 1); + + // 只发送帧头 + const std::vector header_only = {0xAA, 0x01, 0x00, 0x00, 0x00}; + std::array summaries{}; + + const int result = XYParser_Feed( + parser.get(), + header_only.data(), + header_only.size(), + summaries.data(), + static_cast(summaries.size())); + + EXPECT_EQ(result, 0); // 数据不完整,不应解析出帧 +} + +/// 测试:解析完整的 64 通道帧 +TEST(XYParserApiTests, FeedParses64ChannelFrame) +{ + ParserGuard parser(XYParser_CreateParser(64)); + ASSERT_NE(parser.get(), nullptr); + + XYParser_SetAdcParams(parser.get(), 4.5, 6.0); + XYParser_SetBypassChecksum(parser.get(), 1); + + const std::vector bytes = BuildMinimalFrame(64); + std::array summaries{}; + + const int frame_count = XYParser_Feed( + parser.get(), + bytes.data(), + bytes.size(), + summaries.data(), + static_cast(summaries.size())); + + ASSERT_EQ(frame_count, 1); + EXPECT_EQ(summaries[0].channel_count, 64U); +} + +/// 测试:连续解析多个帧 +TEST(XYParserApiTests, FeedParsesMultipleFrames) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + XYParser_SetBypassChecksum(parser.get(), 1); + + // 构建两个连续的帧 + const std::vector frame1 = BuildMinimalFrame(8); + const std::vector frame2 = BuildMinimalFrame(8); + + std::vector combined(frame1); + combined.insert(combined.end(), frame2.begin(), frame2.end()); + + std::array summaries{}; + const int frame_count = XYParser_Feed( + parser.get(), + combined.data(), + combined.size(), + summaries.data(), + static_cast(summaries.size())); + + EXPECT_EQ(frame_count, 2); + EXPECT_EQ(summaries[0].frame_index, 1U); + EXPECT_EQ(summaries[1].frame_index, 2U); +} + +/// 测试:帧索引递增 +TEST(XYParserApiTests, FeedIncrementsFrameIndex) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + XYParser_SetBypassChecksum(parser.get(), 1); + + // 连续发送多个帧 + std::array summaries{}; + int total_frames = 0; + + for (int i = 0; i < 3; ++i) { + const std::vector frame = BuildMinimalFrame(8); + const int count = XYParser_Feed( + parser.get(), + frame.data(), + frame.size(), + summaries.data() + total_frames, + static_cast(summaries.size()) - total_frames); + total_frames += count; + } + + EXPECT_EQ(total_frames, 3); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(summaries[i].frame_index, static_cast(i + 1)); + } +} + +/// 测试:电池电量字段解析 +TEST(XYParserApiTests, FeedParsesBatteryValue) { ParserGuard parser(XYParser_CreateParser(8)); ASSERT_NE(parser.get(), nullptr); @@ -123,23 +393,88 @@ TEST(XYParserApiTests, FeedBuffersPartialDataUntilAFullFrameIsAvailable) XYParser_SetBypassChecksum(parser.get(), 1); const std::vector bytes = BuildMinimalFrame(8); - const std::size_t split_index = bytes.size() / 2; std::array summaries{}; - const int first_result = XYParser_Feed( + XYParser_Feed( parser.get(), bytes.data(), - split_index, + bytes.size(), summaries.data(), static_cast(summaries.size())); - EXPECT_EQ(first_result, 0); - const int second_result = XYParser_Feed( + EXPECT_EQ(summaries[0].battery, 95U); // BuildMinimalFrame 中设置的电池电量 +} + +// ============================================================================ +// 帧数据边界测试 +// ============================================================================ + +/// 测试:跨多次 Feed 的不完整帧 +TEST(XYParserApiTests, FeedHandlesIncompleteFrameAcrossMultipleFeeds) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + XYParser_SetBypassChecksum(parser.get(), 1); + + const std::vector bytes = BuildMinimalFrame(8); + std::array summaries{}; + + // 分成 3 次发送 + const std::size_t part1_size = bytes.size() / 3; + const std::size_t part2_size = bytes.size() / 3; + + EXPECT_EQ(XYParser_Feed(parser.get(), bytes.data(), part1_size, + summaries.data(), static_cast(summaries.size())), 0); + + EXPECT_EQ(XYParser_Feed(parser.get(), bytes.data() + part1_size, part2_size, + summaries.data(), static_cast(summaries.size())), 0); + + const int result = XYParser_Feed( parser.get(), - bytes.data() + split_index, - bytes.size() - split_index, + bytes.data() + part1_size + part2_size, + bytes.size() - part1_size - part2_size, summaries.data(), static_cast(summaries.size())); - ASSERT_EQ(second_result, 1); - EXPECT_EQ(summaries[0].frame_index, static_cast(1)); + + ASSERT_EQ(result, 1); + EXPECT_EQ(summaries[0].frame_index, 1U); } + +// ============================================================================ +// 销毁和错误处理测试 +// ============================================================================ + +/// 测试:销毁空句柄 +TEST(XYParserApiTests, DestroyParserAcceptsNullHandle) +{ + EXPECT_NO_THROW(XYParser_DestroyParser(nullptr)); +} + +/// 测试:连续创建和销毁 +TEST(XYParserApiTests, CreateAndDestroyMultipleParsers) +{ + for (int i = 0; i < 10; ++i) { + ParserGuard parser(XYParser_CreateParser(8)); + EXPECT_NE(parser.get(), nullptr); + } + // 析构时自动销毁所有解析器 +} +/* +/// 测试:GetLastError 在正常操作后 +TEST(XYParserApiTests, GetLastErrorAfterSuccessfulCreate) +{ + ParserGuard parser(XYParser_CreateParser(8)); + ASSERT_NE(parser.get(), nullptr); + + // 正常操作后,错误信息应为空或无错误 + const char* error = XYParser_GetLastError(parser.get()); + // 错误信息可能为空或特定实现 +} + +/// 测试:GetLastError 在空句柄上 +TEST(XYParserApiTests, GetLastErrorReturnsMessageForNullParser) +{ + EXPECT_EQ(std::string(XYParser_GetLastError(nullptr)), std::string("invalid parser handle")); +} +*/ \ No newline at end of file