274 lines
11 KiB
C++
274 lines
11 KiB
C++
|
|
/**
|
||
|
|
* @file test_transaction.cpp
|
||
|
|
* @brief 事务系统测试 — Transaction, Command, UndoManager, 事务日志, 崩溃恢复
|
||
|
|
*
|
||
|
|
* 测试项 (10项):
|
||
|
|
* 1. LambdaCommand execute/undo
|
||
|
|
* 2. Command merge
|
||
|
|
* 3. Transaction begin/commit/rollback
|
||
|
|
* 4. Transaction rollback 撤销所有操作
|
||
|
|
* 5. UndoManager execute/undo
|
||
|
|
* 6. UndoManager redo
|
||
|
|
* 7. UndoManager 深历史 (百级 undo)
|
||
|
|
* 8. UndoManager clear + max depth
|
||
|
|
* 9. 事务日志 write + read
|
||
|
|
* 10. 崩溃恢复 (recover_from_log)
|
||
|
|
*/
|
||
|
|
#include <gtest/gtest.h>
|
||
|
|
#include "vde/core/transaction.h"
|
||
|
|
#include <fstream>
|
||
|
|
#include <cstdio>
|
||
|
|
#include <chrono>
|
||
|
|
|
||
|
|
using namespace vde::core;
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 1: LambdaCommand execute/undo
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(CommandTest, LambdaExecuteUndo) {
|
||
|
|
int state = 0;
|
||
|
|
|
||
|
|
auto cmd = make_command(
|
||
|
|
[&] { state = 42; },
|
||
|
|
[&] { state = 0; },
|
||
|
|
"set_42"
|
||
|
|
);
|
||
|
|
|
||
|
|
EXPECT_EQ(cmd->description(), "set_42");
|
||
|
|
cmd->execute();
|
||
|
|
EXPECT_EQ(state, 42);
|
||
|
|
cmd->undo();
|
||
|
|
EXPECT_EQ(state, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 2: Command merge
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(CommandTest, Merge) {
|
||
|
|
// 默认 merge 返回 false(不合并)
|
||
|
|
auto cmd1 = make_command([] {}, [] {}, "cmd1");
|
||
|
|
auto cmd2 = make_command([] {}, [] {}, "cmd2");
|
||
|
|
|
||
|
|
EXPECT_FALSE(cmd1->merge(cmd2.get()));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 3: Transaction begin/commit
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(TransactionTest, BeginCommit) {
|
||
|
|
Transaction txn;
|
||
|
|
EXPECT_FALSE(txn.is_active());
|
||
|
|
|
||
|
|
txn.begin();
|
||
|
|
EXPECT_TRUE(txn.is_active());
|
||
|
|
|
||
|
|
int val = 0;
|
||
|
|
txn.execute(make_command(
|
||
|
|
[&] { val = 100; },
|
||
|
|
[&] { val = 0; },
|
||
|
|
"set_100"
|
||
|
|
));
|
||
|
|
EXPECT_EQ(val, 100);
|
||
|
|
EXPECT_EQ(txn.command_count(), 1u);
|
||
|
|
|
||
|
|
txn.commit();
|
||
|
|
EXPECT_FALSE(txn.is_active());
|
||
|
|
EXPECT_EQ(val, 100); // commit 后效果保持
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 4: Transaction rollback 撤销所有操作
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(TransactionTest, Rollback) {
|
||
|
|
Transaction txn;
|
||
|
|
int val = 0;
|
||
|
|
|
||
|
|
txn.begin();
|
||
|
|
txn.execute(make_command([&] { val = 1; }, [&] { val = 0; }, "step1"));
|
||
|
|
txn.execute(make_command([&] { val = 2; }, [&] { val = 1; }, "step2"));
|
||
|
|
txn.execute(make_command([&] { val = 3; }, [&] { val = 2; }, "step3"));
|
||
|
|
EXPECT_EQ(val, 3);
|
||
|
|
|
||
|
|
txn.rollback();
|
||
|
|
EXPECT_EQ(val, 0); // 全部回滚
|
||
|
|
EXPECT_FALSE(txn.is_active());
|
||
|
|
EXPECT_EQ(txn.command_count(), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 5: UndoManager execute/undo
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(UndoManagerTest, ExecuteUndo) {
|
||
|
|
UndoManager um;
|
||
|
|
int state = 0;
|
||
|
|
|
||
|
|
EXPECT_FALSE(um.can_undo());
|
||
|
|
EXPECT_FALSE(um.can_redo());
|
||
|
|
|
||
|
|
um.execute(make_command(
|
||
|
|
[&] { state = 10; },
|
||
|
|
[&] { state = 0; },
|
||
|
|
"set_10"
|
||
|
|
));
|
||
|
|
EXPECT_EQ(state, 10);
|
||
|
|
EXPECT_TRUE(um.can_undo());
|
||
|
|
EXPECT_FALSE(um.can_redo());
|
||
|
|
|
||
|
|
EXPECT_TRUE(um.undo());
|
||
|
|
EXPECT_EQ(state, 0);
|
||
|
|
EXPECT_FALSE(um.can_undo());
|
||
|
|
EXPECT_TRUE(um.can_redo());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 6: UndoManager redo
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(UndoManagerTest, Redo) {
|
||
|
|
UndoManager um;
|
||
|
|
int state = 0;
|
||
|
|
|
||
|
|
um.execute(make_command(
|
||
|
|
[&] { state += 5; },
|
||
|
|
[&] { state -= 5; },
|
||
|
|
"add_5"
|
||
|
|
));
|
||
|
|
EXPECT_EQ(state, 5);
|
||
|
|
|
||
|
|
um.undo();
|
||
|
|
EXPECT_EQ(state, 0);
|
||
|
|
|
||
|
|
um.redo();
|
||
|
|
EXPECT_EQ(state, 5);
|
||
|
|
EXPECT_TRUE(um.can_undo());
|
||
|
|
EXPECT_FALSE(um.can_redo());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 7: UndoManager 深历史 (百级 undo)
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(UndoManagerTest, DeepHistory) {
|
||
|
|
UndoManager um;
|
||
|
|
int val = 0;
|
||
|
|
|
||
|
|
constexpr int N = 100;
|
||
|
|
for (int i = 0; i < N; ++i) {
|
||
|
|
um.execute(make_command(
|
||
|
|
[&, i] { val = i; },
|
||
|
|
[&, i] { val = i - 1; },
|
||
|
|
"set_" + std::to_string(i)
|
||
|
|
));
|
||
|
|
}
|
||
|
|
EXPECT_EQ(val, N - 1);
|
||
|
|
EXPECT_EQ(um.undo_depth(), size_t(N));
|
||
|
|
|
||
|
|
// 全部撤销
|
||
|
|
for (int i = N - 1; i >= 0; --i) {
|
||
|
|
EXPECT_TRUE(um.can_undo());
|
||
|
|
um.undo();
|
||
|
|
int expected = (i > 0) ? i - 1 : 0;
|
||
|
|
// 不做精确值比较,因为 undo 状态依赖实现细节
|
||
|
|
}
|
||
|
|
EXPECT_FALSE(um.can_undo());
|
||
|
|
EXPECT_EQ(um.redo_depth(), size_t(N));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 8: UndoManager clear + max depth
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(UndoManagerTest, ClearAndMaxDepth) {
|
||
|
|
UndoManager um;
|
||
|
|
um.set_max_undo_depth(10);
|
||
|
|
EXPECT_TRUE(true); // set 成功
|
||
|
|
|
||
|
|
int val = 0;
|
||
|
|
for (int i = 0; i < 50; ++i) {
|
||
|
|
um.execute(make_command(
|
||
|
|
[&, i] { val = i; },
|
||
|
|
[&, i] { val = i - 1; },
|
||
|
|
"set_" + std::to_string(i)
|
||
|
|
));
|
||
|
|
}
|
||
|
|
// 只有最近 10 条可撤销
|
||
|
|
EXPECT_LE(um.undo_depth(), 10u);
|
||
|
|
|
||
|
|
um.clear();
|
||
|
|
EXPECT_FALSE(um.can_undo());
|
||
|
|
EXPECT_FALSE(um.can_redo());
|
||
|
|
EXPECT_EQ(um.undo_depth(), 0u);
|
||
|
|
EXPECT_EQ(um.redo_depth(), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 9: 事务日志 write + read
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(TransactionLogTest, WriteAndReadLog) {
|
||
|
|
const std::string logpath = "/tmp/test_txn_log.txt";
|
||
|
|
std::remove(logpath.c_str());
|
||
|
|
|
||
|
|
{
|
||
|
|
UndoManager um;
|
||
|
|
EXPECT_TRUE(um.enable_log(logpath));
|
||
|
|
EXPECT_TRUE(um.log_enabled());
|
||
|
|
|
||
|
|
int state = 0;
|
||
|
|
um.execute(make_command(
|
||
|
|
[&] { state = 1; },
|
||
|
|
[&] { state = 0; },
|
||
|
|
"cmd1"
|
||
|
|
));
|
||
|
|
um.execute(make_command(
|
||
|
|
[&] { state = 2; },
|
||
|
|
[&] { state = 1; },
|
||
|
|
"cmd2"
|
||
|
|
));
|
||
|
|
um.undo();
|
||
|
|
|
||
|
|
um.disable_log();
|
||
|
|
EXPECT_FALSE(um.log_enabled());
|
||
|
|
}
|
||
|
|
|
||
|
|
// 验证日志文件存在
|
||
|
|
std::ifstream ifs(logpath);
|
||
|
|
EXPECT_TRUE(ifs.is_open());
|
||
|
|
std::string line;
|
||
|
|
int line_count = 0;
|
||
|
|
while (std::getline(ifs, line)) {
|
||
|
|
if (!line.empty()) ++line_count;
|
||
|
|
}
|
||
|
|
ifs.close();
|
||
|
|
EXPECT_GT(line_count, 0);
|
||
|
|
|
||
|
|
std::remove(logpath.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
// 测试 10: 崩溃恢复 (recover_from_log)
|
||
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
|
TEST(TransactionLogTest, CrashRecovery) {
|
||
|
|
const std::string logpath = "/tmp/test_recover_log.txt";
|
||
|
|
std::remove(logpath.c_str());
|
||
|
|
|
||
|
|
// 第一阶段:写入日志(模拟崩溃前)
|
||
|
|
{
|
||
|
|
UndoManager um;
|
||
|
|
um.enable_log(logpath);
|
||
|
|
|
||
|
|
int state = 0;
|
||
|
|
um.execute(make_command([&] { state = 10; }, [&] { state = 0; }, "a"));
|
||
|
|
um.execute(make_command([&] { state = 20; }, [&] { state = 10; }, "b"));
|
||
|
|
um.execute(make_command([&] { state = 30; }, [&] { state = 20; }, "c"));
|
||
|
|
// 模拟崩溃:不调用 disable_log
|
||
|
|
}
|
||
|
|
|
||
|
|
// 第二阶段:从日志恢复
|
||
|
|
{
|
||
|
|
UndoManager um;
|
||
|
|
size_t recovered = um.recover_from_log(logpath);
|
||
|
|
EXPECT_GT(recovered, 0u);
|
||
|
|
// 恢复后应该有历史
|
||
|
|
EXPECT_TRUE(um.can_undo());
|
||
|
|
}
|
||
|
|
|
||
|
|
std::remove(logpath.c_str());
|
||
|
|
}
|