From ba6249bd4a979a63406295279b2cde1b914f30ca Mon Sep 17 00:00:00 2001 From: suyu <1643689728@qq.com> Date: Fri, 21 Aug 2026 10:27:39 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E7=BB=9F=E4=B8=80=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E5=92=8C=E5=9B=9E=E5=BD=92=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- app/tests/alarm_service_tests.cpp | 24 +-- app/tests/alarm_service_tests.pro | 47 ++--- app/tests/domain_tests.cpp | 9 +- app/tests/domain_tests.pro | 31 +--- app/tests/hmi_editor_service_tests.cpp | 25 +-- app/tests/hmi_editor_service_tests.pro | 46 ++--- app/tests/logic_editor_service_tests.cpp | 46 ++--- app/tests/logic_editor_service_tests.pro | 44 ++--- app/tests/main_window_tests.cpp | 24 +-- app/tests/main_window_tests.pro | 117 +++--------- .../offline_simulation_service_tests.cpp | 9 +- .../offline_simulation_service_tests.pro | 35 ++-- app/tests/performance_tests.cpp | 97 ++++++++++ app/tests/performance_tests.pro | 16 ++ app/tests/plc_runtime_tests.cpp | 23 +-- app/tests/plc_runtime_tests.pro | 57 ++---- app/tests/pri/test_defaults.pri | 8 + app/tests/pri/test_layers.pri | 175 ++++++++++++++++++ app/tests/project_management_tests.cpp | 9 +- app/tests/project_management_tests.pro | 44 ++--- app/tests/register_monitor_service_tests.cpp | 9 +- app/tests/register_monitor_service_tests.pro | 25 +-- app/tests/runtime_mode_service_tests.cpp | 23 +-- app/tests/runtime_mode_service_tests.pro | 48 ++--- app/tests/support/test_support.h | 34 ++++ app/tests/tests.pro | 28 +++ scripts/run_qt_tests.ps1 | 88 +++++++++ 28 files changed, 643 insertions(+), 503 deletions(-) create mode 100644 app/tests/performance_tests.cpp create mode 100644 app/tests/performance_tests.pro create mode 100644 app/tests/pri/test_defaults.pri create mode 100644 app/tests/pri/test_layers.pri create mode 100644 app/tests/support/test_support.h create mode 100644 app/tests/tests.pro create mode 100644 scripts/run_qt_tests.ps1 diff --git a/.gitignore b/.gitignore index 984e6f6..7f5b708 100644 --- a/.gitignore +++ b/.gitignore @@ -55,10 +55,11 @@ tmp/ Thumbs.db Desktop.ini -/scripts +/scripts/* +!/scripts/run_qt_tests.ps1 /docs/images /docs/类图Mermaid代码.md /other 设计方案书.doc /学习 -/json \ No newline at end of file +/json diff --git a/app/tests/alarm_service_tests.cpp b/app/tests/alarm_service_tests.cpp index 0e64c8a..5e3f5b8 100644 --- a/app/tests/alarm_service_tests.cpp +++ b/app/tests/alarm_service_tests.cpp @@ -3,6 +3,7 @@ #include "services/alarm_editor_service.h" #include "services/alarm_service.h" #include "services/project_service.h" +#include "support/test_support.h" #include #include @@ -12,27 +13,8 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; - -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestProjectStorage = TestSupport::InMemoryProjectStorage; +using TestSupport::require; const AlarmRecord *findRecord( const AlarmService &service, const std::string &definition_id) diff --git a/app/tests/alarm_service_tests.pro b/app/tests/alarm_service_tests.pro index 21f2bf0..c824b98 100644 --- a/app/tests/alarm_service_tests.pro +++ b/app/tests/alarm_service_tests.pro @@ -1,35 +1,24 @@ -TEMPLATE = app -TARGET = alarm_service_tests - -CONFIG += console c++17 testcase warn_on -CONFIG -= app_bundle qt +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -INCLUDEPATH += ../src +TARGET = alarm_service_tests +QT -= gui +CONFIG += testcase SOURCES += \ alarm_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/services/project_service.cpp \ - ../src/services/alarm_editor_service.cpp \ - ../src/services/alarm_service.cpp + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_HMI_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ + $$DOMAIN_PROJECT_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_ALARM_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_model.h \ - ../src/domain/project_storage.h \ - ../src/services/project_service.h \ - ../src/services/alarm_editor_service.h \ - ../src/services/alarm_service.h - -QT -= gui + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_HMI_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ + $$DOMAIN_PROJECT_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_ALARM_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/domain_tests.cpp b/app/tests/domain_tests.cpp index 9a3c9b0..e665a65 100644 --- a/app/tests/domain_tests.cpp +++ b/app/tests/domain_tests.cpp @@ -6,6 +6,7 @@ #include "domain/register_address.h" #include "domain/register_repository.h" #include "domain/runtime_state.h" +#include "support/test_support.h" #include #include @@ -17,13 +18,7 @@ namespace { -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; void testRegisterAddressBoundaries() { diff --git a/app/tests/domain_tests.pro b/app/tests/domain_tests.pro index 24204d9..776c36e 100644 --- a/app/tests/domain_tests.pro +++ b/app/tests/domain_tests.pro @@ -1,29 +1,14 @@ -TEMPLATE = app -TARGET = domain_tests - -CONFIG += console c++17 testcase warn_on -CONFIG -= app_bundle qt +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -INCLUDEPATH += ../src +TARGET = domain_tests +QT -= gui +CONFIG += testcase SOURCES += \ domain_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/runtime_state.cpp + $$DOMAIN_ALL_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_limits.h \ - ../src/domain/project_model.h \ - ../src/domain/runtime_state.h + $$DOMAIN_ALL_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/hmi_editor_service_tests.cpp b/app/tests/hmi_editor_service_tests.cpp index ec9e531..cf54a30 100644 --- a/app/tests/hmi_editor_service_tests.cpp +++ b/app/tests/hmi_editor_service_tests.cpp @@ -4,6 +4,7 @@ #include "services/hmi_runtime_service.h" #include "services/hmi_navigation_service.h" #include "services/project_service.h" +#include "support/test_support.h" #include #include @@ -12,28 +13,8 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - // 编辑服务测试只关注内存模型变更,不依赖真实文件系统 - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; - -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestProjectStorage = TestSupport::InMemoryProjectStorage; +using TestSupport::require; void testControlEditing() { diff --git a/app/tests/hmi_editor_service_tests.pro b/app/tests/hmi_editor_service_tests.pro index b504ff9..0662cb7 100644 --- a/app/tests/hmi_editor_service_tests.pro +++ b/app/tests/hmi_editor_service_tests.pro @@ -1,36 +1,22 @@ -TEMPLATE = app -TARGET = hmi_editor_service_tests - -CONFIG += console c++17 warn_on -CONFIG -= app_bundle +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -INCLUDEPATH += ../src +TARGET = hmi_editor_service_tests SOURCES += \ hmi_editor_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/services/project_service.cpp \ - ../src/services/hmi_editor_service.cpp \ - ../src/services/hmi_navigation_service.cpp \ - ../src/services/hmi_runtime_service.cpp + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_HMI_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ + $$DOMAIN_PROJECT_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_HMI_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_model.h \ - ../src/domain/project_storage.h \ - ../src/services/project_service.h \ - ../src/services/editor_history.h \ - ../src/services/hmi_editor_service.h \ - ../src/services/hmi_navigation_service.h \ - ../src/services/hmi_runtime_service.h + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_HMI_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ + $$DOMAIN_PROJECT_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_HMI_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/logic_editor_service_tests.cpp b/app/tests/logic_editor_service_tests.cpp index 11f665b..008573a 100644 --- a/app/tests/logic_editor_service_tests.cpp +++ b/app/tests/logic_editor_service_tests.cpp @@ -2,6 +2,7 @@ #include "domain/project_limits.h" #include "services/logic_editor_service.h" #include "services/project_service.h" +#include "support/test_support.h" #include #include @@ -9,27 +10,8 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; - -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestProjectStorage = TestSupport::InMemoryProjectStorage; +using TestSupport::require; ContactNodeConfig contact(int address) { @@ -299,11 +281,17 @@ void testConditionColumnLimit() require(service.appendCondition(logic_id, rung_id, contact(column)).succeeded, "the first ten condition columns must be editable"); } + require(project_service.saveAs("logic-editor-condition-limit.json").succeeded, + "the ten-column network must be saveable before testing overflow"); + require(!project_service.isModified(), + "saving the ten-column network must clear the modified state"); const LogicEditorResult overflow = service.appendCondition( logic_id, rung_id, contact(ProjectLimits::kMaximumConditionColumns)); require(!overflow.succeeded && overflow.error == LogicEditorError::InvalidOperation, "the eleventh condition column must be rejected by the editor service"); + require(!project_service.isModified(), + "a failed eleventh-column edit must preserve the saved state"); const LadderRung *rung = service.findRung(logic_id, rung_id); require(rung != nullptr && rung->condition.has_value() && rung->condition->kind == ConditionExpressionKind::Series @@ -311,6 +299,22 @@ void testConditionColumnLimit() == static_cast( ProjectLimits::kMaximumConditionColumns), "a rejected eleventh column must leave the ten-column network unchanged"); + + require(service.setOutput( + logic_id, + rung_id, + CoilNodeConfig{ + RegisterAddress{RegisterArea::M, 20}, CoilMode::Normal}, + true) + .succeeded + && project_service.isModified(), + "a successful edit must make the project modified again"); + require(!service.appendCondition( + logic_id, rung_id, + contact(ProjectLimits::kMaximumConditionColumns)) + .succeeded + && project_service.isModified(), + "a failed edit must preserve an existing modified state"); } void testUnconditionalOutputEditing() diff --git a/app/tests/logic_editor_service_tests.pro b/app/tests/logic_editor_service_tests.pro index 7d29eed..6e31d38 100644 --- a/app/tests/logic_editor_service_tests.pro +++ b/app/tests/logic_editor_service_tests.pro @@ -1,32 +1,24 @@ -TEMPLATE = app -TARGET = logic_editor_service_tests - -CONFIG += console c++17 testcase warn_on -CONFIG -= app_bundle qt +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -INCLUDEPATH += ../src +TARGET = logic_editor_service_tests +QT -= gui +CONFIG += testcase SOURCES += \ logic_editor_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/services/project_service.cpp \ - ../src/services/logic_editor_service.cpp + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_HMI_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ + $$DOMAIN_PROJECT_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_LOGIC_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/register_repository.h \ - ../src/domain/alarm_model.h \ - ../src/domain/project_storage.h \ - ../src/services/project_service.h \ - ../src/services/editor_history.h \ - ../src/services/logic_editor_service.h + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_HMI_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ + $$DOMAIN_PROJECT_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_LOGIC_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/main_window_tests.cpp b/app/tests/main_window_tests.cpp index 801ae41..3f9e85d 100644 --- a/app/tests/main_window_tests.cpp +++ b/app/tests/main_window_tests.cpp @@ -21,6 +21,7 @@ #include "ui/plc_connection_dialog.h" #include "ui/runtime_monitor_widget.h" #include "ui/runtime_monitor_window.h" +#include "support/test_support.h" #include #include @@ -55,20 +56,7 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - // 主窗口测试使用无副作用存储实现,隔离文件对 UI 行为的影响 - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; +using TestProjectStorage = TestSupport::InMemoryProjectStorage; class RepeatedStatusGateway final : public PlcCommunicationGateway { @@ -265,13 +253,7 @@ public: std::function error_reported; }; -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; template ObjectType *requiredChild(MainWindow &window, const char *name) diff --git a/app/tests/main_window_tests.pro b/app/tests/main_window_tests.pro index 6c3596b..3d0e6b1 100644 --- a/app/tests/main_window_tests.pro +++ b/app/tests/main_window_tests.pro @@ -1,101 +1,32 @@ -QT += widgets serialport testlib +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -TEMPLATE = app TARGET = main_window_tests - -CONFIG += console c++17 warn_on -CONFIG -= app_bundle - -INCLUDEPATH += ../src +QT += widgets serialport testlib SOURCES += \ main_window_tests.cpp \ - ../src/ui/main_window.cpp \ - ../src/ui/alarm_configuration_dialog.cpp \ - ../src/ui/register_comment_dialog.cpp \ - ../src/ui/plc_connection_dialog.cpp \ - ../src/ui/free_monitor_widget.cpp \ - ../src/ui/runtime_monitor_window.cpp \ - ../src/ui/runtime_monitor_widget.cpp \ - ../src/ui/toolbar_icon_factory.cpp \ - ../src/ui/project_workspace_controller.cpp \ - ../src/ui/property_panel_controller.cpp \ - ../src/ui/runtime_panel_controller.cpp \ - ../src/ui/hmi_editor_widget.cpp \ - ../src/ui/logic_editor_widget.cpp \ - ../src/ui/logic_instruction_dialog.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/active_register_repository.cpp \ - ../src/domain/register_monitor_model.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/runtime_state.cpp \ - ../src/services/project_service.cpp \ - ../src/services/alarm_editor_service.cpp \ - ../src/services/alarm_service.cpp \ - ../src/services/hmi_editor_service.cpp \ - ../src/services/hmi_navigation_service.cpp \ - ../src/services/logic_editor_service.cpp \ - ../src/services/hmi_runtime_service.cpp \ - ../src/services/software_logic_executor.cpp \ - ../src/services/offline_simulation_service.cpp \ - ../src/services/runtime_mode_service.cpp \ - ../src/services/register_monitor_service.cpp \ - ../src/services/register_comment_service.cpp - -HEADERS += \ - ../src/ui/main_window.h \ - ../src/ui/alarm_configuration_dialog.h \ - ../src/ui/register_comment_dialog.h \ - ../src/ui/plc_connection_dialog.h \ - ../src/ui/free_monitor_widget.h \ - ../src/ui/runtime_monitor_window.h \ - ../src/ui/runtime_monitor_widget.h \ - ../src/ui/toolbar_icon_factory.h \ - ../src/ui/project_workspace_controller.h \ - ../src/ui/property_panel_controller.h \ - ../src/ui/runtime_panel_controller.h \ - ../src/ui/hmi_editor_widget.h \ - ../src/ui/logic_editor_widget.h \ - ../src/ui/logic_instruction_dialog.h \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/active_register_repository.h \ - ../src/domain/register_monitor_model.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_model.h \ - ../src/domain/project_storage.h \ - ../src/domain/runtime_state.h \ - ../src/services/project_service.h \ - ../src/services/editor_history.h \ - ../src/services/alarm_editor_service.h \ - ../src/services/alarm_service.h \ - ../src/services/hmi_editor_service.h \ - ../src/services/hmi_navigation_service.h \ - ../src/services/logic_editor_service.h \ - ../src/services/hmi_runtime_service.h \ - ../src/services/software_logic_executor.h \ - ../src/services/offline_simulation_service.h \ - ../src/services/runtime_mode_service.h \ - ../src/services/register_monitor_service.h \ - ../src/services/register_comment_service.h + $$UI_MAIN_SOURCES \ + $$DOMAIN_ALL_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_ALARM_SOURCES \ + $$SERVICE_HMI_SOURCES \ + $$SERVICE_LOGIC_SOURCES \ + $$SERVICE_OFFLINE_SOURCES \ + $$SERVICE_RUNTIME_SOURCES \ + $$SERVICE_MONITOR_SOURCES HEADERS += \ - ../src/services/plc_communication_gateway.h + $$UI_MAIN_HEADERS \ + $$DOMAIN_ALL_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_ALARM_HEADERS \ + $$SERVICE_HMI_HEADERS \ + $$SERVICE_LOGIC_HEADERS \ + $$SERVICE_OFFLINE_HEADERS \ + $$SERVICE_RUNTIME_HEADERS \ + $$SERVICE_MONITOR_HEADERS \ + ../src/services/plc_communication_gateway.h \ + $$TEST_SUPPORT_HEADERS -FORMS += \ - ../src/ui/main_window.ui \ - ../src/ui/alarm_configuration_dialog.ui \ - ../src/ui/register_comment_dialog.ui \ - ../src/ui/plc_connection_dialog.ui \ - ../src/ui/free_monitor_widget.ui \ - ../src/ui/runtime_monitor_window.ui \ - ../src/ui/runtime_monitor_widget.ui \ - ../src/ui/logic_instruction_dialog.ui +FORMS += $$UI_MAIN_FORMS diff --git a/app/tests/offline_simulation_service_tests.cpp b/app/tests/offline_simulation_service_tests.cpp index bd327ff..cc4d83f 100644 --- a/app/tests/offline_simulation_service_tests.cpp +++ b/app/tests/offline_simulation_service_tests.cpp @@ -3,6 +3,7 @@ #include "services/hmi_runtime_service.h" #include "services/offline_simulation_service.h" #include "services/software_logic_executor.h" +#include "support/test_support.h" #include @@ -14,13 +15,7 @@ namespace { -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; LogicNode contact(const std::string &id, int address, ContactMode mode = ContactMode::NormallyOpen) diff --git a/app/tests/offline_simulation_service_tests.pro b/app/tests/offline_simulation_service_tests.pro index 7fb4967..40b3fe0 100644 --- a/app/tests/offline_simulation_service_tests.pro +++ b/app/tests/offline_simulation_service_tests.pro @@ -1,31 +1,22 @@ -QT += core -QT -= gui +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -TEMPLATE = app TARGET = offline_simulation_service_tests - -CONFIG += console c++17 warn_on -CONFIG -= app_bundle - -INCLUDEPATH += ../src +QT += core +QT -= gui SOURCES += \ offline_simulation_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_HMI_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ ../src/services/hmi_runtime_service.cpp \ - ../src/services/software_logic_executor.cpp \ - ../src/services/offline_simulation_service.cpp + $$SERVICE_OFFLINE_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_HMI_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ ../src/services/hmi_runtime_service.h \ - ../src/services/software_logic_executor.h \ - ../src/services/offline_simulation_service.h + $$SERVICE_OFFLINE_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/performance_tests.cpp b/app/tests/performance_tests.cpp new file mode 100644 index 0000000..a43bc3e --- /dev/null +++ b/app/tests/performance_tests.cpp @@ -0,0 +1,97 @@ +#include "domain/control_logic_model.h" +#include "domain/register_repository.h" +#include "services/software_logic_executor.h" + +#include + +#include +#include + +namespace +{ + +LogicNode contact(const std::string &id, int address) +{ + return {id, + ContactNodeConfig{ + RegisterAddress{RegisterArea::M, address}, + ContactMode::NormallyOpen}, + true}; +} + +LogicNode coil(const std::string &id, int address) +{ + return {id, + CoilNodeConfig{ + RegisterAddress{RegisterArea::M, address}, + CoilMode::Normal}, + true}; +} + +ControlLogic makeLogic(int index) +{ + LadderRung rung; + rung.id = "rung-" + std::to_string(index); + rung.name = rung.id; + rung.condition = ConditionExpression::fromNode( + contact("contact-" + std::to_string(index), index)); + rung.output = coil("coil-" + std::to_string(index), index + 100); + return {"logic-" + std::to_string(index), + "logic-" + std::to_string(index), + {rung}, + true}; +} + +class PerformanceTests final : public QObject +{ + Q_OBJECT + +private slots: + void softwareExecutor_scansRepresentativeProject() + { + std::vector logics; + logics.reserve(100); + for (int index = 0; index < 100; ++index) + { + logics.push_back(makeLogic(index)); + } + + SoftwareLogicExecutor executor; + VirtualRegisterRepository repository; + QVERIFY(executor.validate(logics).succeeded); + QVERIFY(executor.executeScan(logics, repository).succeeded); + + QBENCHMARK + { + executor.executeScan(logics, repository); + } + } + + void virtualRepository_handlesBoundaryWorkload() + { + VirtualRegisterRepository repository; + for (int index = 0; index <= RegisterAddress::kMaximumIndex; ++index) + { + QVERIFY(repository.writeBit( + {RegisterArea::M, index}, (index % 2) == 0) + .succeeded); + QVERIFY(repository.readBit({RegisterArea::M, index}).succeeded); + } + + QBENCHMARK + { + for (int index = 0; index <= RegisterAddress::kMaximumIndex; ++index) + { + repository.writeBit( + {RegisterArea::M, index}, (index % 2) == 0); + repository.readBit({RegisterArea::M, index}); + } + } + } +}; + +} // namespace + +QTEST_APPLESS_MAIN(PerformanceTests) + +#include "performance_tests.moc" diff --git a/app/tests/performance_tests.pro b/app/tests/performance_tests.pro new file mode 100644 index 0000000..6f2db72 --- /dev/null +++ b/app/tests/performance_tests.pro @@ -0,0 +1,16 @@ +include(pri/test_defaults.pri) +include(pri/test_layers.pri) + +TARGET = performance_tests +QT += testlib + +SOURCES += \ + performance_tests.cpp \ + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ + ../src/services/software_logic_executor.cpp + +HEADERS += \ + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ + ../src/services/software_logic_executor.h diff --git a/app/tests/plc_runtime_tests.cpp b/app/tests/plc_runtime_tests.cpp index 4d621cf..8f3f77a 100644 --- a/app/tests/plc_runtime_tests.cpp +++ b/app/tests/plc_runtime_tests.cpp @@ -3,6 +3,7 @@ #include "domain/project_storage.h" #include "infrastructure/plc_communication_error_classifier.h" #include "infrastructure/plc_communication_service.h" +#include "support/test_support.h" #include "infrastructure/plc_register_repository.h" #include "services/offline_simulation_service.h" #include "services/plc_communication_gateway.h" @@ -16,19 +17,7 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; +using TestProjectStorage = TestSupport::InMemoryProjectStorage; class FakePlcGateway final : public PlcCommunicationGateway { @@ -130,13 +119,7 @@ public: std::function error_reported; }; -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; void testPlcCacheAndWriteForwarding() { diff --git a/app/tests/plc_runtime_tests.pro b/app/tests/plc_runtime_tests.pro index d2e771b..82580c9 100644 --- a/app/tests/plc_runtime_tests.pro +++ b/app/tests/plc_runtime_tests.pro @@ -1,51 +1,24 @@ -QT += core serialbus serialport +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -TEMPLATE = app TARGET = plc_runtime_tests +QT += core serialbus serialport -CONFIG += console c++17 testcase warn_on -CONFIG -= app_bundle - -INCLUDEPATH += ../src +CONFIG += testcase SOURCES += \ plc_runtime_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/active_register_repository.cpp \ - ../src/domain/register_monitor_model.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/runtime_state.cpp \ - ../src/services/project_service.cpp \ - ../src/services/software_logic_executor.cpp \ - ../src/services/offline_simulation_service.cpp \ - ../src/services/runtime_mode_service.cpp \ - ../src/infrastructure/plc_communication_error_classifier.cpp \ - ../src/infrastructure/plc_register_repository.cpp \ - ../src/infrastructure/plc_communication_service.cpp + $$DOMAIN_ALL_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_OFFLINE_SOURCES \ + $$SERVICE_RUNTIME_SOURCES \ + $$INFRASTRUCTURE_PLC_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/active_register_repository.h \ - ../src/domain/register_monitor_model.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_limits.h \ - ../src/domain/project_model.h \ - ../src/domain/project_storage.h \ - ../src/domain/runtime_state.h \ - ../src/services/project_service.h \ - ../src/services/software_logic_executor.h \ - ../src/services/offline_simulation_service.h \ - ../src/services/runtime_mode_service.h \ + $$DOMAIN_ALL_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_OFFLINE_HEADERS \ + $$SERVICE_RUNTIME_HEADERS \ + $$INFRASTRUCTURE_PLC_HEADERS \ ../src/services/plc_communication_gateway.h \ - ../src/infrastructure/plc_communication_error_classifier.h \ - ../src/infrastructure/plc_register_repository.h \ - ../src/infrastructure/plc_communication_service.h + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/pri/test_defaults.pri b/app/tests/pri/test_defaults.pri new file mode 100644 index 0000000..27e5a2d --- /dev/null +++ b/app/tests/pri/test_defaults.pri @@ -0,0 +1,8 @@ +# 所有测试目标共享的 qmake 基础配置 +TEMPLATE = app +CONFIG += console c++17 warn_on +CONFIG -= app_bundle + +INCLUDEPATH += $$PWD/../../src $$PWD/.. + +TEST_SUPPORT_HEADERS = $$PWD/../support/test_support.h diff --git a/app/tests/pri/test_layers.pri b/app/tests/pri/test_layers.pri new file mode 100644 index 0000000..882abf8 --- /dev/null +++ b/app/tests/pri/test_layers.pri @@ -0,0 +1,175 @@ +# 测试按生产分层选择源码,避免每个 .pro 重复维护同一批路径 + +DOMAIN_REGISTER_SOURCES = \ + ../src/domain/register_address.cpp \ + ../src/domain/register_repository.cpp + +DOMAIN_REGISTER_HEADERS = \ + ../src/domain/register_address.h \ + ../src/domain/register_repository.h + +DOMAIN_HMI_SOURCES = \ + ../src/domain/hmi_model.cpp \ + ../src/domain/hmi_control_registry.cpp + +DOMAIN_HMI_HEADERS = \ + ../src/domain/hmi_model.h \ + ../src/domain/hmi_control_registry.h + +DOMAIN_LOGIC_SOURCES = \ + ../src/domain/control_logic_model.cpp + +DOMAIN_LOGIC_HEADERS = \ + ../src/domain/control_logic_model.h + +DOMAIN_PROJECT_SOURCES = \ + ../src/domain/alarm_model.cpp \ + ../src/domain/project_model.cpp + +DOMAIN_PROJECT_HEADERS = \ + ../src/domain/alarm_model.h \ + ../src/domain/project_model.h \ + ../src/domain/project_limits.h \ + ../src/domain/project_storage.h + +DOMAIN_RUNTIME_SOURCES = \ + ../src/domain/runtime_state.cpp + +DOMAIN_RUNTIME_HEADERS = \ + ../src/domain/runtime_state.h + +DOMAIN_MONITOR_SOURCES = \ + ../src/domain/active_register_repository.cpp \ + ../src/domain/register_monitor_model.cpp + +DOMAIN_MONITOR_HEADERS = \ + ../src/domain/active_register_repository.h \ + ../src/domain/register_monitor_model.h + +DOMAIN_ALL_SOURCES = \ + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_HMI_SOURCES \ + $$DOMAIN_LOGIC_SOURCES \ + $$DOMAIN_PROJECT_SOURCES \ + $$DOMAIN_RUNTIME_SOURCES \ + $$DOMAIN_MONITOR_SOURCES + +DOMAIN_ALL_HEADERS = \ + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_HMI_HEADERS \ + $$DOMAIN_LOGIC_HEADERS \ + $$DOMAIN_PROJECT_HEADERS \ + $$DOMAIN_RUNTIME_HEADERS \ + $$DOMAIN_MONITOR_HEADERS + +SERVICE_PROJECT_SOURCES = \ + ../src/services/project_service.cpp + +SERVICE_PROJECT_HEADERS = \ + ../src/services/project_service.h + +SERVICE_ALARM_SOURCES = \ + ../src/services/alarm_editor_service.cpp \ + ../src/services/alarm_service.cpp + +SERVICE_ALARM_HEADERS = \ + ../src/services/alarm_editor_service.h \ + ../src/services/alarm_service.h + +SERVICE_HMI_SOURCES = \ + ../src/services/hmi_editor_service.cpp \ + ../src/services/hmi_navigation_service.cpp \ + ../src/services/hmi_runtime_service.cpp + +SERVICE_HMI_HEADERS = \ + ../src/services/editor_history.h \ + ../src/services/hmi_editor_service.h \ + ../src/services/hmi_navigation_service.h \ + ../src/services/hmi_runtime_service.h + +SERVICE_LOGIC_SOURCES = \ + ../src/services/logic_editor_service.cpp + +SERVICE_LOGIC_HEADERS = \ + ../src/services/editor_history.h \ + ../src/services/logic_editor_service.h + +SERVICE_OFFLINE_SOURCES = \ + ../src/services/software_logic_executor.cpp \ + ../src/services/offline_simulation_service.cpp + +SERVICE_OFFLINE_HEADERS = \ + ../src/services/software_logic_executor.h \ + ../src/services/offline_simulation_service.h + +SERVICE_RUNTIME_SOURCES = \ + ../src/services/runtime_mode_service.cpp + +SERVICE_RUNTIME_HEADERS = \ + ../src/services/runtime_mode_service.h + +SERVICE_MONITOR_SOURCES = \ + ../src/services/register_monitor_service.cpp \ + ../src/services/register_comment_service.cpp + +SERVICE_MONITOR_HEADERS = \ + ../src/services/register_monitor_service.h \ + ../src/services/register_comment_service.h + +INFRASTRUCTURE_JSON_SOURCES = \ + ../src/infrastructure/json_project_storage.cpp + +INFRASTRUCTURE_JSON_HEADERS = \ + ../src/infrastructure/json_project_storage.h + +INFRASTRUCTURE_PLC_SOURCES = \ + ../src/infrastructure/plc_communication_error_classifier.cpp \ + ../src/infrastructure/plc_register_repository.cpp \ + ../src/infrastructure/plc_communication_service.cpp + +INFRASTRUCTURE_PLC_HEADERS = \ + ../src/infrastructure/plc_communication_error_classifier.h \ + ../src/infrastructure/plc_register_repository.h \ + ../src/infrastructure/plc_communication_service.h + +UI_MAIN_SOURCES = \ + ../src/ui/main_window.cpp \ + ../src/ui/logic_instruction_dialog.cpp \ + ../src/ui/alarm_configuration_dialog.cpp \ + ../src/ui/register_comment_dialog.cpp \ + ../src/ui/plc_connection_dialog.cpp \ + ../src/ui/free_monitor_widget.cpp \ + ../src/ui/runtime_monitor_window.cpp \ + ../src/ui/runtime_monitor_widget.cpp \ + ../src/ui/toolbar_icon_factory.cpp \ + ../src/ui/project_workspace_controller.cpp \ + ../src/ui/property_panel_controller.cpp \ + ../src/ui/runtime_panel_controller.cpp \ + ../src/ui/hmi_editor_widget.cpp \ + ../src/ui/logic_editor_widget.cpp + +UI_MAIN_HEADERS = \ + ../src/ui/main_window.h \ + ../src/ui/logic_instruction_dialog.h \ + ../src/ui/alarm_configuration_dialog.h \ + ../src/ui/register_comment_dialog.h \ + ../src/ui/plc_connection_dialog.h \ + ../src/ui/free_monitor_widget.h \ + ../src/ui/runtime_monitor_window.h \ + ../src/ui/runtime_monitor_widget.h \ + ../src/ui/toolbar_icon_factory.h \ + ../src/ui/project_workspace_controller.h \ + ../src/ui/property_panel_controller.h \ + ../src/ui/runtime_panel_controller.h \ + ../src/ui/hmi_editor_widget.h \ + ../src/ui/logic_editor_widget.h + +UI_MAIN_FORMS = \ + ../src/ui/main_window.ui \ + ../src/ui/logic_instruction_dialog.ui \ + ../src/ui/alarm_configuration_dialog.ui \ + ../src/ui/register_comment_dialog.ui \ + ../src/ui/plc_connection_dialog.ui \ + ../src/ui/free_monitor_widget.ui \ + ../src/ui/runtime_monitor_window.ui \ + ../src/ui/runtime_monitor_widget.ui diff --git a/app/tests/project_management_tests.cpp b/app/tests/project_management_tests.cpp index f7d0b4a..e74833f 100644 --- a/app/tests/project_management_tests.cpp +++ b/app/tests/project_management_tests.cpp @@ -1,6 +1,7 @@ #include "domain/project_storage.h" #include "domain/hmi_model.h" #include "infrastructure/json_project_storage.h" +#include "support/test_support.h" #include "services/register_comment_service.h" #include "services/project_service.h" #include "domain/project_limits.h" @@ -19,13 +20,7 @@ namespace { -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; Project makeExampleProject() { diff --git a/app/tests/project_management_tests.pro b/app/tests/project_management_tests.pro index 70269c1..4b4d502 100644 --- a/app/tests/project_management_tests.pro +++ b/app/tests/project_management_tests.pro @@ -1,38 +1,20 @@ -TEMPLATE = app -TARGET = project_management_tests - -CONFIG += console c++17 testcase warn_on -CONFIG -= app_bundle +include(pri/test_defaults.pri) +include(pri/test_layers.pri) +TARGET = project_management_tests QT += core - -INCLUDEPATH += ../src +CONFIG += testcase SOURCES += \ project_management_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/runtime_state.cpp \ - ../src/services/project_service.cpp \ - ../src/services/register_comment_service.cpp \ - ../src/infrastructure/json_project_storage.cpp + $$DOMAIN_ALL_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_MONITOR_SOURCES \ + $$INFRASTRUCTURE_JSON_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_limits.h \ - ../src/domain/project_model.h \ - ../src/domain/runtime_state.h \ - ../src/domain/project_storage.h \ - ../src/services/project_service.h \ - ../src/services/register_comment_service.h \ - ../src/infrastructure/json_project_storage.h + $$DOMAIN_ALL_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_MONITOR_HEADERS \ + $$INFRASTRUCTURE_JSON_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/register_monitor_service_tests.cpp b/app/tests/register_monitor_service_tests.cpp index f007a1e..2b132b6 100644 --- a/app/tests/register_monitor_service_tests.cpp +++ b/app/tests/register_monitor_service_tests.cpp @@ -2,6 +2,7 @@ #include "domain/register_monitor_model.h" #include "domain/register_repository.h" #include "services/register_monitor_service.h" +#include "support/test_support.h" #include #include @@ -9,13 +10,7 @@ namespace { -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; void testRangeManagement() { diff --git a/app/tests/register_monitor_service_tests.pro b/app/tests/register_monitor_service_tests.pro index a5d93bc..06bf2e7 100644 --- a/app/tests/register_monitor_service_tests.pro +++ b/app/tests/register_monitor_service_tests.pro @@ -1,24 +1,17 @@ -QT -= gui +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -TEMPLATE = app TARGET = register_monitor_service_tests - -CONFIG += console c++17 warn_on -CONFIG -= app_bundle - -INCLUDEPATH += ../src +QT -= gui SOURCES += \ register_monitor_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/active_register_repository.cpp \ - ../src/domain/register_monitor_model.cpp \ + $$DOMAIN_REGISTER_SOURCES \ + $$DOMAIN_MONITOR_SOURCES \ ../src/services/register_monitor_service.cpp HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/active_register_repository.h \ - ../src/domain/register_monitor_model.h \ - ../src/services/register_monitor_service.h + $$DOMAIN_REGISTER_HEADERS \ + $$DOMAIN_MONITOR_HEADERS \ + ../src/services/register_monitor_service.h \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/runtime_mode_service_tests.cpp b/app/tests/runtime_mode_service_tests.cpp index b02a687..548d4cd 100644 --- a/app/tests/runtime_mode_service_tests.cpp +++ b/app/tests/runtime_mode_service_tests.cpp @@ -4,6 +4,7 @@ #include "domain/active_register_repository.h" #include "domain/project_storage.h" #include "domain/register_repository.h" +#include "support/test_support.h" #include #include @@ -13,19 +14,7 @@ namespace { -class TestProjectStorage final : public ProjectStorage -{ -public: - ProjectSaveResult save(const Project &, const std::string &) override - { - return {true, ProjectStorageError::None, {}}; - } - - ProjectLoadResult load(const std::string &) override - { - return {false, {}, ProjectStorageError::FileReadFailed, {}}; - } -}; +using TestProjectStorage = TestSupport::InMemoryProjectStorage; class ReadyPlcGateway final : public PlcCommunicationGateway { @@ -97,13 +86,7 @@ private: std::vector poll_addresses; }; -void require(bool condition, const std::string &message) -{ - if (!condition) - { - throw std::runtime_error(message); - } -} +using TestSupport::require; void testModeTransitions() { diff --git a/app/tests/runtime_mode_service_tests.pro b/app/tests/runtime_mode_service_tests.pro index fc26a51..0190d5c 100644 --- a/app/tests/runtime_mode_service_tests.pro +++ b/app/tests/runtime_mode_service_tests.pro @@ -1,43 +1,19 @@ -QT -= gui +include(pri/test_defaults.pri) +include(pri/test_layers.pri) -TEMPLATE = app TARGET = runtime_mode_service_tests - -CONFIG += console c++17 warn_on -CONFIG -= app_bundle - -INCLUDEPATH += ../src +QT -= gui SOURCES += \ runtime_mode_service_tests.cpp \ - ../src/domain/register_address.cpp \ - ../src/domain/register_repository.cpp \ - ../src/domain/active_register_repository.cpp \ - ../src/domain/register_monitor_model.cpp \ - ../src/domain/alarm_model.cpp \ - ../src/domain/hmi_model.cpp \ - ../src/domain/hmi_control_registry.cpp \ - ../src/domain/control_logic_model.cpp \ - ../src/domain/project_model.cpp \ - ../src/domain/runtime_state.cpp \ - ../src/services/project_service.cpp \ - ../src/services/software_logic_executor.cpp \ - ../src/services/offline_simulation_service.cpp \ - ../src/services/runtime_mode_service.cpp + $$DOMAIN_ALL_SOURCES \ + $$SERVICE_PROJECT_SOURCES \ + $$SERVICE_OFFLINE_SOURCES \ + $$SERVICE_RUNTIME_SOURCES HEADERS += \ - ../src/domain/register_address.h \ - ../src/domain/register_repository.h \ - ../src/domain/active_register_repository.h \ - ../src/domain/register_monitor_model.h \ - ../src/domain/alarm_model.h \ - ../src/domain/hmi_model.h \ - ../src/domain/hmi_control_registry.h \ - ../src/domain/control_logic_model.h \ - ../src/domain/project_model.h \ - ../src/domain/project_storage.h \ - ../src/domain/runtime_state.h \ - ../src/services/project_service.h \ - ../src/services/software_logic_executor.h \ - ../src/services/offline_simulation_service.h \ - ../src/services/runtime_mode_service.h + $$DOMAIN_ALL_HEADERS \ + $$SERVICE_PROJECT_HEADERS \ + $$SERVICE_OFFLINE_HEADERS \ + $$SERVICE_RUNTIME_HEADERS \ + $$TEST_SUPPORT_HEADERS diff --git a/app/tests/support/test_support.h b/app/tests/support/test_support.h new file mode 100644 index 0000000..91f50bd --- /dev/null +++ b/app/tests/support/test_support.h @@ -0,0 +1,34 @@ +#pragma once + +#include "domain/project_storage.h" + +#include +#include + +namespace TestSupport +{ + +inline void require(bool condition, const std::string &message) +{ + if (!condition) + { + throw std::runtime_error(message); + } +} + +// 编辑服务测试只验证内存模型和服务契约,不应依赖真实文件系统 +class InMemoryProjectStorage final : public ProjectStorage +{ +public: + ProjectSaveResult save(const Project &, const std::string &) override + { + return {true, ProjectStorageError::None, {}}; + } + + ProjectLoadResult load(const std::string &) override + { + return {false, {}, ProjectStorageError::FileReadFailed, {}}; + } +}; + +} // namespace TestSupport diff --git a/app/tests/tests.pro b/app/tests/tests.pro new file mode 100644 index 0000000..df5f80b --- /dev/null +++ b/app/tests/tests.pro @@ -0,0 +1,28 @@ +# 测试总入口:只负责编排目标,不把不同环境的测试揉成一个可执行文件 +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS += \ + domain \ + alarm_service \ + hmi_editor_service \ + logic_editor_service \ + offline_simulation \ + project_management \ + register_monitor \ + runtime_mode \ + plc_runtime \ + main_window \ + performance + +domain.file = domain_tests.pro +alarm_service.file = alarm_service_tests.pro +hmi_editor_service.file = hmi_editor_service_tests.pro +logic_editor_service.file = logic_editor_service_tests.pro +offline_simulation.file = offline_simulation_service_tests.pro +project_management.file = project_management_tests.pro +register_monitor.file = register_monitor_service_tests.pro +runtime_mode.file = runtime_mode_service_tests.pro +plc_runtime.file = plc_runtime_tests.pro +main_window.file = main_window_tests.pro +performance.file = performance_tests.pro diff --git a/scripts/run_qt_tests.ps1 b/scripts/run_qt_tests.ps1 new file mode 100644 index 0000000..dcae291 --- /dev/null +++ b/scripts/run_qt_tests.ps1 @@ -0,0 +1,88 @@ +[CmdletBinding()] +param( + [ValidateSet('Debug', 'Release')] + [string]$Configuration = 'Debug', + [switch]$SkipPerformance +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$projectRoot = Split-Path -Parent $PSScriptRoot +$qtRoot = 'D:\Qt5.15.2\5.15.2\mingw81_64' +$mingwBin = 'D:\Qt5.15.2\Tools\mingw810_64\bin' +$qmakePath = Join-Path $qtRoot 'bin\qmake.exe' +$makePath = Join-Path $mingwBin 'mingw32-make.exe' +$configurationName = $Configuration.ToLowerInvariant() +$testSourceRoot = Join-Path $projectRoot 'app\tests' +$buildRoot = Join-Path $projectRoot "build\tests\$configurationName" + +foreach ($requiredPath in @($qmakePath, $makePath, $testSourceRoot)) { + if (-not (Test-Path -LiteralPath $requiredPath)) { + throw "未找到必要文件:$requiredPath" + } +} + +$targets = @( + 'domain_tests', + 'alarm_service_tests', + 'hmi_editor_service_tests', + 'logic_editor_service_tests', + 'offline_simulation_service_tests', + 'project_management_tests', + 'register_monitor_service_tests', + 'runtime_mode_service_tests', + 'plc_runtime_tests', + 'main_window_tests' +) +if (-not $SkipPerformance) { + $targets += 'performance_tests' +} + +$originalPath = $env:Path +$originalPlatform = $env:QT_QPA_PLATFORM +$env:Path = "$qtRoot\bin;$mingwBin;$env:Path" +$env:QT_QPA_PLATFORM = 'offscreen' + +try { + foreach ($target in $targets) { + $projectFile = Join-Path $testSourceRoot "$target.pro" + $buildDirectory = Join-Path $buildRoot $target + $executablePath = Join-Path $buildDirectory "$configurationName\$target.exe" + New-Item -ItemType Directory -Path $buildDirectory -Force | Out-Null + + Push-Location $buildDirectory + try { + & $qmakePath $projectFile "CONFIG+=$configurationName" "CONFIG-=$(if ($configurationName -eq 'debug') { 'release' } else { 'debug' })" + if ($LASTEXITCODE -ne 0) { + throw "qmake 失败:$target,退出码:$LASTEXITCODE" + } + + & $makePath -j2 + if ($LASTEXITCODE -ne 0) { + throw "构建失败:$target,退出码:$LASTEXITCODE" + } + + if (-not (Test-Path -LiteralPath $executablePath)) { + throw "未生成测试可执行文件:$executablePath" + } + + Write-Host "运行 $target" + & $executablePath + if ($LASTEXITCODE -ne 0) { + throw "测试失败:$target,退出码:$LASTEXITCODE" + } + } + finally { + Pop-Location + } + } +} +finally { + $env:Path = $originalPath + if ($null -eq $originalPlatform) { + Remove-Item Env:QT_QPA_PLATFORM -ErrorAction SilentlyContinue + } else { + $env:QT_QPA_PLATFORM = $originalPlatform + } +}