| @@ -55,10 +55,11 @@ tmp/ | |||
| Thumbs.db | |||
| Desktop.ini | |||
| /scripts | |||
| /scripts/* | |||
| !/scripts/run_qt_tests.ps1 | |||
| /docs/images | |||
| /docs/类图Mermaid代码.md | |||
| /other | |||
| 设计方案书.doc | |||
| /学习 | |||
| /json | |||
| /json | |||
| @@ -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 <algorithm> | |||
| #include <exception> | |||
| @@ -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) | |||
| @@ -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 | |||
| @@ -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 <cstdint> | |||
| #include <exception> | |||
| @@ -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() | |||
| { | |||
| @@ -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 | |||
| @@ -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 <exception> | |||
| #include <iostream> | |||
| @@ -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() | |||
| { | |||
| @@ -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 | |||
| @@ -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 <algorithm> | |||
| #include <iostream> | |||
| @@ -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<std::size_t>( | |||
| 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() | |||
| @@ -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 | |||
| @@ -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 <QAction> | |||
| #include <QApplication> | |||
| @@ -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<void(const std::string &)> error_reported; | |||
| }; | |||
| void require(bool condition, const std::string &message) | |||
| { | |||
| if (!condition) | |||
| { | |||
| throw std::runtime_error(message); | |||
| } | |||
| } | |||
| using TestSupport::require; | |||
| template<typename ObjectType> | |||
| ObjectType *requiredChild(MainWindow &window, const char *name) | |||
| @@ -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 | |||
| @@ -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 <QCoreApplication> | |||
| @@ -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) | |||
| @@ -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 | |||
| @@ -0,0 +1,97 @@ | |||
| #include "domain/control_logic_model.h" | |||
| #include "domain/register_repository.h" | |||
| #include "services/software_logic_executor.h" | |||
| #include <QTest> | |||
| #include <string> | |||
| #include <vector> | |||
| 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<ControlLogic> 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" | |||
| @@ -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 | |||
| @@ -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<void(const std::string &)> error_reported; | |||
| }; | |||
| void require(bool condition, const std::string &message) | |||
| { | |||
| if (!condition) | |||
| { | |||
| throw std::runtime_error(message); | |||
| } | |||
| } | |||
| using TestSupport::require; | |||
| void testPlcCacheAndWriteForwarding() | |||
| { | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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() | |||
| { | |||
| @@ -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 | |||
| @@ -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 <iostream> | |||
| #include <stdexcept> | |||
| @@ -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() | |||
| { | |||
| @@ -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 | |||
| @@ -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 <functional> | |||
| #include <iostream> | |||
| @@ -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<RegisterAddress> poll_addresses; | |||
| }; | |||
| void require(bool condition, const std::string &message) | |||
| { | |||
| if (!condition) | |||
| { | |||
| throw std::runtime_error(message); | |||
| } | |||
| } | |||
| using TestSupport::require; | |||
| void testModeTransitions() | |||
| { | |||
| @@ -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 | |||
| @@ -0,0 +1,34 @@ | |||
| #pragma once | |||
| #include "domain/project_storage.h" | |||
| #include <stdexcept> | |||
| #include <string> | |||
| 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 | |||
| @@ -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 | |||
| @@ -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 | |||
| } | |||
| } | |||