/** * @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 #include "vde/core/transaction.h" #include #include #include 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()); }