重构
This commit is contained in:
+67
-184
@@ -1,201 +1,84 @@
|
||||
#include "c_LinkQueue.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "c_Test.h"
|
||||
|
||||
// 专门用于测试测试的值复制结构体
|
||||
typedef struct {
|
||||
char text[16];
|
||||
int id;
|
||||
} Message_t;
|
||||
|
||||
void test_log(const char* test_name) {
|
||||
printf("[PASS] %s\n", test_name);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char* data_str;
|
||||
int id;
|
||||
} DataPacket_t;
|
||||
|
||||
static void test_iter(void) {
|
||||
printf("==================================================\n");
|
||||
printf(" 開始執行 c_LinkQueueIter_Remove 單元測試\n");
|
||||
printf("==================================================\n\n");
|
||||
int log_level;
|
||||
uint32_t timestamp;
|
||||
} LogMessage_t;
|
||||
|
||||
TEST_CASE(test_link_queue_closure_and_memory_layout) {
|
||||
c_LinkQueue_t queue;
|
||||
c_LinkQueue_Init(&queue, sizeof(DataPacket_t));
|
||||
|
||||
DataPacket_t p1 = {"PacketA", 101};
|
||||
DataPacket_t p2 = {"PacketB", 102};
|
||||
DataPacket_t p3 = {"PacketC", 103};
|
||||
// 初始化一个专门存放 LogMessage_t 结构的泛型单向链式队列
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Init(&queue, sizeof(LogMessage_t), NULL));
|
||||
ASSERT_TRUE(c_LinkQueue_IsEmpty(&queue));
|
||||
ASSERT_TRUE(queue.head == NULL);
|
||||
ASSERT_TRUE(queue.tail == NULL);
|
||||
|
||||
// 推入三筆資料 (FIFO 順序: head -> p1 -> p2 -> p3 <- tail)
|
||||
c_LinkQueue_Push(&queue, &p1);
|
||||
c_LinkQueue_Push(&queue, &p2);
|
||||
c_LinkQueue_Push(&queue, &p3);
|
||||
assert(queue.size == 3);
|
||||
assert(((DataPacket_t*)queue.tail->data)->id == 103); // 確任目前尾端是 p3
|
||||
LogMessage_t msg1 = { .log_level = 1, .timestamp = 11111 };
|
||||
LogMessage_t msg2 = { .log_level = 2, .timestamp = 22222 };
|
||||
LogMessage_t msg3 = { .log_level = 3, .timestamp = 33333 };
|
||||
|
||||
c_LinkQueueIter_t iter;
|
||||
// 1. 验证入队操作与二级指针空安全合并能力
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Enqueue(&queue, &msg1)); // 第一次插入:head == tail == msg1
|
||||
ASSERT_PTR_NOT_NULL(queue.head);
|
||||
ASSERT_TRUE(queue.head == queue.tail);
|
||||
|
||||
// ==========================================
|
||||
// 測試 1:刪除中間節點 (p2: 102)
|
||||
// ==========================================
|
||||
c_LinkQueueIter_Init(&iter, &queue);
|
||||
while (c_LinkQueueIter_HasNext(&iter)) {
|
||||
DataPacket_t* pkt = (DataPacket_t*)c_LinkQueueIter_Get(&iter);
|
||||
if (pkt->id == 102) {
|
||||
c_LinkQueueIter_Remove(&iter); // 刪除 PacketB
|
||||
printf("[PASS] 成功刪除中間節點 (102)\n");
|
||||
} else {
|
||||
c_LinkQueueIter_Next(&iter);
|
||||
}
|
||||
}
|
||||
assert(queue.size == 2);
|
||||
assert(((DataPacket_t*)queue.tail->data)->id == 103); // 尾端應維持 103
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Enqueue(&queue, &msg2)); // 正常挂接:head->msg1, tail->msg2
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Enqueue(&queue, &msg3)); // 正常挂接:head->msg1, tail->msg3
|
||||
|
||||
// ==========================================
|
||||
// 測試 2:刪除尾端節點 (p3: 103) -> 測試 tail 更新
|
||||
// ==========================================
|
||||
c_LinkQueueIter_Init(&iter, &queue);
|
||||
while (c_LinkQueueIter_HasNext(&iter)) {
|
||||
DataPacket_t* pkt = (DataPacket_t*)c_LinkQueueIter_Get(&iter);
|
||||
if (pkt->id == 103) {
|
||||
c_LinkQueueIter_Remove(&iter); // 刪除 PacketC (當前的尾端)
|
||||
printf("[PASS] 成功刪除尾端節點 (103)\n");
|
||||
} else {
|
||||
c_LinkQueueIter_Next(&iter);
|
||||
}
|
||||
}
|
||||
assert(queue.size == 1);
|
||||
// 關鍵斷言:刪除原本的尾端 103 後,queue->tail 必須自動往前更新為 101 (PacketA)
|
||||
assert(queue.tail != NULL);
|
||||
assert(((DataPacket_t*)queue.tail->data)->id == 101);
|
||||
assert(queue.head == queue.tail); // 只剩一個元素時,head 應等於 tail
|
||||
ASSERT_INT_EQ(3, c_LinkQueue_Size(&queue));
|
||||
|
||||
// ==========================================
|
||||
// 3. 測試 3:刪除最後一個節點 (p1: 101) -> 測試佇列歸零
|
||||
// ==========================================
|
||||
c_LinkQueueIter_Init(&iter, &queue);
|
||||
assert(c_LinkQueueIter_HasNext(&iter) == C_TRUE);
|
||||
c_LinkQueueIter_Remove(&iter); // 刪除僅存的 PacketA
|
||||
printf("[PASS] 成功刪除最後一個節點 (101)\n");
|
||||
// 强隔离隔离性检查:修改临时外部变量,内部数据不应被污染
|
||||
msg1.log_level = 999;
|
||||
|
||||
assert(queue.size == 0);
|
||||
assert(queue.head == NULL);
|
||||
assert(queue.tail == NULL); // 關鍵斷言:完全空了之後 tail 必須回歸 NULL
|
||||
assert(c_LinkQueueIter_HasNext(&iter) == C_FALSE);
|
||||
// 2. 验证 Peek 窥探能力
|
||||
LogMessage_t* peeked = (LogMessage_t*)c_LinkQueue_Peek(&queue);
|
||||
ASSERT_PTR_NOT_NULL(peeked);
|
||||
ASSERT_INT_EQ(1, peeked->log_level); // 依旧是 1,证明值复制强屏障有效
|
||||
|
||||
// 3. 【严苛验证变长内存布局的拓扑对齐情况】
|
||||
// 根据变长公式寻址:QUEUE_NODE_DATA(node) 必须与抛出的数据指针完全吻合
|
||||
ASSERT_TRUE(c_LinkQueueNode_Get(queue.head) == peeked);
|
||||
|
||||
// 队尾节点的寻址验证
|
||||
LogMessage_t* tail_data = c_LinkQueueNode_Get(queue.tail);
|
||||
ASSERT_INT_EQ(3, tail_data->log_level);
|
||||
|
||||
// 4. 出队顺序与先进先出(FIFO)及边界收缩断言 (Dequeue)
|
||||
LogMessage_t out_res;
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Dequeue(&queue, &out_res));
|
||||
ASSERT_INT_EQ(1, out_res.log_level);
|
||||
ASSERT_INT_EQ(11111, out_res.timestamp);
|
||||
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Dequeue(&queue, &out_res));
|
||||
ASSERT_INT_EQ(2, out_res.log_level);
|
||||
|
||||
// 此时队列只剩最后一个元素 msg3,head 应当等同于 tail
|
||||
ASSERT_TRUE(queue.head == queue.tail);
|
||||
ASSERT_INT_EQ(1, c_LinkQueue_Size(&queue));
|
||||
|
||||
// 最后一个元素出队,迫使尾指针恢复到你指定的 0 纯净空空空状态
|
||||
ASSERT_INT_EQ(C_ERR_OK, c_LinkQueue_Dequeue(&queue, &out_res));
|
||||
ASSERT_INT_EQ(3, out_res.log_level);
|
||||
|
||||
// 终极下溢复位检查
|
||||
ASSERT_TRUE(c_LinkQueue_IsEmpty(&queue));
|
||||
ASSERT_TRUE(queue.head == NULL);
|
||||
ASSERT_TRUE(queue.tail == NULL);
|
||||
|
||||
// 防御重复下溢出队,应当报错拒绝
|
||||
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, c_LinkQueue_Dequeue(&queue, &out_res));
|
||||
|
||||
// 清理資源
|
||||
c_LinkQueue_Destroy(&queue);
|
||||
printf("\n==================================================\n");
|
||||
printf(" 恭喜!c_LinkQueueIter_Remove 所有特例驗證全數通過!\n");
|
||||
printf("==================================================\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("==================================================\n");
|
||||
printf(" 開始執行 c_LinkQueue 完整版測試用例\n");
|
||||
printf("==================================================\n\n");
|
||||
int main(void) {
|
||||
TEST_START(C_LinkQueue_Generic_Layout_Tests);
|
||||
RUN_TEST(test_link_queue_closure_and_memory_layout);
|
||||
TEST_REPORT();
|
||||
RETURN_TEST_STATUS;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 1. 測試初始化
|
||||
// ==========================================
|
||||
c_LinkQueue_t q;
|
||||
c_err_t err = c_LinkQueue_Init(&q, sizeof(Message_t));
|
||||
assert(err == C_ERR_OK);
|
||||
assert(q.size == 0);
|
||||
assert(q.head == NULL);
|
||||
test_log("1. 鏈結佇列初始化成功");
|
||||
|
||||
// ==========================================
|
||||
// 2. 測試資料推入 (Push)
|
||||
// ==========================================
|
||||
Message_t msg1 = {"MsgA", 101};
|
||||
Message_t msg2 = {"MsgB", 102};
|
||||
Message_t msg3 = {"MsgC", 103};
|
||||
|
||||
err = c_LinkQueue_Push(&q, &msg1); assert(err == C_ERR_OK);
|
||||
err = c_LinkQueue_Push(&q, &msg2); assert(err == C_ERR_OK);
|
||||
err = q.size == 2;
|
||||
err = c_LinkQueue_Push(&q, &msg3); assert(err == C_ERR_OK);
|
||||
assert(q.size == 3);
|
||||
test_log("2. 三筆資料成功推入佇列 (尾端追加)");
|
||||
|
||||
// ==========================================
|
||||
// 3. 測試迭代器走訪 (應符合 Push 的先進順序:A -> B -> C)
|
||||
// ==========================================
|
||||
c_LinkQueueIter_t iter;
|
||||
c_LinkQueueIter_Init(&iter, &q);
|
||||
|
||||
int check_idx = 0;
|
||||
while (c_LinkQueueIter_HasNext(&iter)) {
|
||||
Message_t* m = (Message_t*)c_LinkQueueIter_Next(&iter);
|
||||
if (check_idx == 0) assert(strcmp(m->text, "MsgA") == 0);
|
||||
if (check_idx == 1) assert(strcmp(m->text, "MsgB") == 0);
|
||||
if (check_idx == 2) assert(strcmp(m->text, "MsgC") == 0);
|
||||
check_idx++;
|
||||
}
|
||||
assert(check_idx == 3);
|
||||
test_log("3. 迭代器順序走訪驗證成功 (符合 FIFO 順序)");
|
||||
|
||||
// ==========================================
|
||||
// 4. 測試查看最前端元素 (Peek)
|
||||
// ==========================================
|
||||
Message_t local_buf;
|
||||
err = c_LinkQueue_Peek(&q, &local_buf);
|
||||
assert(err == C_ERR_OK);
|
||||
assert(strcmp(local_buf.text, "MsgA") == 0);
|
||||
assert(local_buf.id == 101);
|
||||
assert(q.size == 3); // 驗證 Peek 不會減少佇列大小
|
||||
test_log("4. 隨機查看最前端元素 (Peek) 成功");
|
||||
|
||||
// ==========================================
|
||||
// 5. 測試先進先出彈出 (Pop)
|
||||
// ==========================================
|
||||
// 第一次 Pop:預期拿到最先進入的 MsgA
|
||||
err = c_LinkQueue_Pop(&q, &local_buf);
|
||||
assert(err == C_ERR_OK);
|
||||
assert(strcmp(local_buf.text, "MsgA") == 0);
|
||||
assert(q.size == 2);
|
||||
|
||||
// 彈出後再次 Peek,最前端應該變成 MsgB
|
||||
err = c_LinkQueue_Peek(&q, &local_buf);
|
||||
assert(err == C_ERR_OK);
|
||||
assert(strcmp(local_buf.text, "MsgB") == 0);
|
||||
|
||||
// 第二次與第三次 Pop
|
||||
err = c_LinkQueue_Pop(&q, &local_buf); assert(err == C_ERR_OK); // 彈出 MsgB
|
||||
err = c_LinkQueue_Pop(&q, &local_buf); assert(err == C_ERR_OK); // 彈出 MsgC
|
||||
assert(q.size == 0);
|
||||
|
||||
// 空佇列彈出與查看測試,預期回傳越界錯誤
|
||||
err = c_LinkQueue_Pop(&q, &local_buf);
|
||||
assert(err == C_ERR_EMPTY);
|
||||
err = c_LinkQueue_Peek(&q, &local_buf);
|
||||
assert(err == C_ERR_EMPTY);
|
||||
test_log("5. 彈出 (Pop) 邏輯與空佇列防呆驗證成功");
|
||||
|
||||
// ==========================================
|
||||
// 6. 參數安全檢查
|
||||
// ==========================================
|
||||
assert(c_LinkQueue_Init(NULL, sizeof(Message_t)) == C_ERR_PARAM);
|
||||
assert(c_LinkQueue_Push(NULL, &msg1) == C_ERR_PARAM);
|
||||
// assert(c_LinkQueue_Pop(&q, NULL) == C_ERR_PARAM);
|
||||
test_log("6. 介面 NULL 指標防呆驗證成功");
|
||||
|
||||
// ==========================================
|
||||
// 7. 銷毀佇列
|
||||
// ==========================================
|
||||
c_LinkQueue_Destroy(&q);
|
||||
assert(q.head == NULL);
|
||||
assert(q.size == 0);
|
||||
test_log("7. 佇列銷毀與記憶體釋放成功");
|
||||
|
||||
printf("\n==================================================\n");
|
||||
printf(" 恭喜!所有 c_LinkQueue 測試皆順利通過!\n");
|
||||
printf("==================================================\n");
|
||||
|
||||
test_iter();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user