Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions tests/editor_undo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# 模块 editor_undo(批次 B5):src/editor 撤销/重做命令类(QUndoCommand 子类)+
# UndoList 组合命令。共 14 个类,逐类独立可执行文件,共享一个静态核心库
#(被测 9 个源文件 + 链接接缝 editor_undo_seam.cpp)。
#
# 链接接缝说明(详见 editor_undo_seam.h):TextEdit / EditWrapper / Window /
# BottomBar 的真实实现不参与链接;本目录所有目标一律不开 AUTOMOC
#(Q_OBJECT 类不实例化元对象特性,空壳定义由 editor_undo_seam.cpp 提供),
# 因此此处显式 set(CMAKE_AUTOMOC OFF) 防御上游目录属性渗透。

set(CMAKE_AUTOMOC OFF)

set(EDITOR_DIR "${CMAKE_SOURCE_DIR}/src/editor")
set(STUB_SHADOW "${CMAKE_SOURCE_DIR}/tests/3rdparty/stub/stub-shadow.cpp")

# ---- 静态核心库:被测源码 + 链接接缝(含传递头文件闭包的 include 目录) ----
add_library(editor_undo_core STATIC
${EDITOR_DIR}/inserttextundocommand.cpp
${EDITOR_DIR}/deletebackcommond.cpp
${EDITOR_DIR}/deletetextundocommand.cpp
${EDITOR_DIR}/indenttextcommond.cpp
${EDITOR_DIR}/insertblockbytextcommond.cpp
${EDITOR_DIR}/changemarkcommand.cpp
${EDITOR_DIR}/endlineformatcommond.cpp
${EDITOR_DIR}/replaceallcommond.cpp
${EDITOR_DIR}/undolist.cpp
editor_undo_seam.cpp
)

target_include_directories(editor_undo_core PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/tests/3rdparty/stub"
"${CMAKE_SOURCE_DIR}/src"
"${CMAKE_SOURCE_DIR}/src/editor"
"${CMAKE_SOURCE_DIR}/src/widgets"
"${CMAKE_SOURCE_DIR}/src/common"
"${CMAKE_SOURCE_DIR}/src/editor/markdown"
)

target_link_libraries(editor_undo_core PUBLIC
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::DBus
Qt6::PrintSupport
Qt6::Core5Compat
Dtk6::Core
Dtk6::Widget
KF6::SyntaxHighlighting
)

# ---- 逐类测试可执行文件 ----
function(eu_add_test name)
add_executable(test_${name} test_${name}.cpp ${STUB_SHADOW})
target_include_directories(test_${name} PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/tests/3rdparty/stub"
)
target_link_libraries(test_${name} PRIVATE
editor_undo_core
GTest::gtest
GTest::gtest_main
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(test_${name} PRIVATE gcov)
endif()
gtest_discover_tests(test_${name} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
message(STATUS "UT: test_${name} configured")
endfunction()

eu_add_test(inserttextundocommand)
eu_add_test(midbuttoninserttextundocommand)
eu_add_test(draginserttextundocommand)
eu_add_test(deletebackcommand)
eu_add_test(deletebackaltcommand)
eu_add_test(deletetextundocommand)
eu_add_test(deletetextundocommand2)
eu_add_test(indenttextcommand)
eu_add_test(unindenttextcommand)
eu_add_test(insertblockbytextcommand)
eu_add_test(changemarkcommand)
eu_add_test(endlineformartcommand)
eu_add_test(replaceallcommand)
eu_add_test(undolist)
140 changes: 140 additions & 0 deletions tests/editor_undo/editor_undo_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// editor_undo 批次(B5)测试公共设施:
// - ut::ensureApp():offscreen QApplication(QT_QPA_PLATFORM 由 CMake 测试属性与
// 运行环境注入,本文件不读取/设置环境变量,无 qputenv/qunsetenv 配对问题)
// - ut::toLf():QTextCursor::selectedText() 以 U+2029 表示换行,比较前统一归一化
// - ut::makeSelection():按起止绝对位置构造绑定文档的 ExtraSelection
// - RecordingCommand:undo/redo 顺序记录命令(UndoList / ChangeMarkCommand 子命令)
// - EditorUndoTestBase:接缝状态 + 空壳 TextEdit 的通用夹具基类
//
// 夹具基类说明:各测试 Fixture 命名为 {ClassName}Test : public EditorUndoTestBase,
// 符合 test-types.md §3.4.3 派生夹具约定(共享 SetUp:重置并安装接缝、构造编辑器;
// 共享 TearDown:卸载接缝、销毁编辑器)。被测 QUndoCommand 子类无信号发射
//(QUndoCommand 非 QObject),故不使用 QSignalSpy,副作用经接缝记录与文档状态断言。

#ifndef AUTOTESTS_EDITOR_UNDO_HELPERS_H
#define AUTOTESTS_EDITOR_UNDO_HELPERS_H

#include <gtest/gtest.h>

Check warning on line 20 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <gtest/gtest.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <QApplication>

Check warning on line 22 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QList>

Check warning on line 23 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QList> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QPlainTextEdit>

Check warning on line 24 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QPlainTextEdit> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QString>

Check warning on line 25 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QString> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTextCursor>

Check warning on line 26 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTextCursor> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTextDocument>

Check warning on line 27 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTextDocument> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include "editor_undo_seam.h"
#include "editwrapper.h"

Check warning on line 30 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "editwrapper.h" not found.
#include "../widgets/bottombar.h"

Check warning on line 31 in tests/editor_undo/editor_undo_helpers.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "../widgets/bottombar.h" not found.

namespace ut {

// 确保 offscreen QApplication 存在(Qt widget 类构造必需)。
inline QApplication *ensureApp()
{
if (!QApplication::instance()) {
static int argc = 1;
static char argv0[] = "test_editor_undo";
static char *argv[] = { argv0, nullptr };
static QApplication app(argc, argv);
return &app;
}
return qobject_cast<QApplication *>(QApplication::instance());
}

// U+2029(段落分隔符)→ '\n',用于比较 QTextCursor::selectedText() 结果。
inline QString toLf(const QString &s)
{
QString r = s;
r.replace(QChar(0x2029), QChar('\n'));
return r;
}

// 构造绑定 doc 的选区光标:anchor=startPos,position=endPos。
inline QTextCursor cursorAt(QTextDocument *doc, int startPos, int endPos)
{
QTextCursor cursor(doc);
cursor.setPosition(startPos);
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
return cursor;
}

inline QTextEdit::ExtraSelection makeSelection(QTextDocument *doc, int startPos, int endPos)
{
QTextEdit::ExtraSelection selection;
selection.cursor = cursorAt(doc, startPos, endPos);
return selection;
}

// 以普通 QWidget 内存伪装 BottomBar*/Window*/EditWrapper*:这三者的接缝成员均为
// 非虚函数,直接调用记录桩,不触碰伪造对象的 vtable,安全可控。
inline BottomBar *fakeBottomBar(QObject *host)
{
return reinterpret_cast<BottomBar *>(host);
}

} // namespace ut

// 顺序记录命令:undo/redo 向夹具成员 orderLog 追加 "name:undo"/"name:redo"。
// orderLog 指向夹具成员(非 static/全局),用例间无残留。
class RecordingCommand : public QUndoCommand
{
public:
explicit RecordingCommand(QStringList *orderLog, const QString &name, QUndoCommand *parent = nullptr)
: QUndoCommand(parent)
, m_orderLog(orderLog)
, m_name(name)
{
}

void undo() override
{
if (m_orderLog)
*m_orderLog << (m_name + ":undo");
}

void redo() override
{
if (m_orderLog)
*m_orderLog << (m_name + ":redo");
}

private:
QStringList *m_orderLog = nullptr;
QString m_name;
};

// 通用夹具基类:接缝状态成员 + 空壳 TextEdit(行为等价 DPlainTextEdit)。
class EditorUndoTestBase : public ::testing::Test
{
protected:
static void SetUpTestSuite()
{
// 空壳 TextEdit 为 QWidget 派生,须 QApplication;offscreen 平台无真实窗口。
ut::ensureApp();
}

void SetUp() override
{
seam = EditorUndoSeam(); // 全部计数器/记录归零
ut_install_seam(&seam);
edit = new TextEdit();
}

void TearDown() override
{
ut_install_seam(nullptr);
delete edit;
edit = nullptr;
}

QString docText() const { return edit->document()->toPlainText(); }

EditorUndoSeam seam;
TextEdit *edit = nullptr;
};

#endif // AUTOTESTS_EDITOR_UNDO_HELPERS_H
Loading
Loading