| @@ -55,10 +55,11 @@ tmp/ | |||||
| Thumbs.db | Thumbs.db | ||||
| Desktop.ini | Desktop.ini | ||||
| /scripts | |||||
| /scripts/* | |||||
| !/scripts/run_qt_tests.ps1 | |||||
| /docs/images | /docs/images | ||||
| /docs/类图Mermaid代码.md | /docs/类图Mermaid代码.md | ||||
| /other | /other | ||||
| 设计方案书.doc | 设计方案书.doc | ||||
| /学习 | /学习 | ||||
| /json | |||||
| /json | |||||
| @@ -3,6 +3,7 @@ | |||||
| #include "services/alarm_editor_service.h" | #include "services/alarm_editor_service.h" | ||||
| #include "services/alarm_service.h" | #include "services/alarm_service.h" | ||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "support/test_support.h" | |||||
| #include <algorithm> | #include <algorithm> | ||||
| #include <exception> | #include <exception> | ||||
| @@ -12,27 +13,8 @@ | |||||
| namespace { | 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 AlarmRecord *findRecord( | ||||
| const AlarmService &service, const std::string &definition_id) | 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 += \ | SOURCES += \ | ||||
| alarm_service_tests.cpp \ | 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 += \ | 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_address.h" | ||||
| #include "domain/register_repository.h" | #include "domain/register_repository.h" | ||||
| #include "domain/runtime_state.h" | #include "domain/runtime_state.h" | ||||
| #include "support/test_support.h" | |||||
| #include <cstdint> | #include <cstdint> | ||||
| #include <exception> | #include <exception> | ||||
| @@ -17,13 +18,7 @@ | |||||
| namespace { | namespace { | ||||
| void require(bool condition, const std::string &message) | |||||
| { | |||||
| if (!condition) | |||||
| { | |||||
| throw std::runtime_error(message); | |||||
| } | |||||
| } | |||||
| using TestSupport::require; | |||||
| void testRegisterAddressBoundaries() | 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 += \ | SOURCES += \ | ||||
| domain_tests.cpp \ | 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 += \ | 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_runtime_service.h" | ||||
| #include "services/hmi_navigation_service.h" | #include "services/hmi_navigation_service.h" | ||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "support/test_support.h" | |||||
| #include <exception> | #include <exception> | ||||
| #include <iostream> | #include <iostream> | ||||
| @@ -12,28 +13,8 @@ | |||||
| namespace { | 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() | 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 += \ | SOURCES += \ | ||||
| hmi_editor_service_tests.cpp \ | 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 += \ | 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 "domain/project_limits.h" | ||||
| #include "services/logic_editor_service.h" | #include "services/logic_editor_service.h" | ||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "support/test_support.h" | |||||
| #include <algorithm> | #include <algorithm> | ||||
| #include <iostream> | #include <iostream> | ||||
| @@ -9,27 +10,8 @@ | |||||
| namespace { | 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) | ContactNodeConfig contact(int address) | ||||
| { | { | ||||
| @@ -299,11 +281,17 @@ void testConditionColumnLimit() | |||||
| require(service.appendCondition(logic_id, rung_id, contact(column)).succeeded, | require(service.appendCondition(logic_id, rung_id, contact(column)).succeeded, | ||||
| "the first ten condition columns must be editable"); | "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( | const LogicEditorResult overflow = service.appendCondition( | ||||
| logic_id, rung_id, contact(ProjectLimits::kMaximumConditionColumns)); | logic_id, rung_id, contact(ProjectLimits::kMaximumConditionColumns)); | ||||
| require(!overflow.succeeded | require(!overflow.succeeded | ||||
| && overflow.error == LogicEditorError::InvalidOperation, | && overflow.error == LogicEditorError::InvalidOperation, | ||||
| "the eleventh condition column must be rejected by the editor service"); | "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); | const LadderRung *rung = service.findRung(logic_id, rung_id); | ||||
| require(rung != nullptr && rung->condition.has_value() | require(rung != nullptr && rung->condition.has_value() | ||||
| && rung->condition->kind == ConditionExpressionKind::Series | && rung->condition->kind == ConditionExpressionKind::Series | ||||
| @@ -311,6 +299,22 @@ void testConditionColumnLimit() | |||||
| == static_cast<std::size_t>( | == static_cast<std::size_t>( | ||||
| ProjectLimits::kMaximumConditionColumns), | ProjectLimits::kMaximumConditionColumns), | ||||
| "a rejected eleventh column must leave the ten-column network unchanged"); | "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() | 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 += \ | SOURCES += \ | ||||
| logic_editor_service_tests.cpp \ | 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 += \ | 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/plc_connection_dialog.h" | ||||
| #include "ui/runtime_monitor_widget.h" | #include "ui/runtime_monitor_widget.h" | ||||
| #include "ui/runtime_monitor_window.h" | #include "ui/runtime_monitor_window.h" | ||||
| #include "support/test_support.h" | |||||
| #include <QAction> | #include <QAction> | ||||
| #include <QApplication> | #include <QApplication> | ||||
| @@ -55,20 +56,7 @@ | |||||
| namespace { | 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 | class RepeatedStatusGateway final : public PlcCommunicationGateway | ||||
| { | { | ||||
| @@ -265,13 +253,7 @@ public: | |||||
| std::function<void(const std::string &)> error_reported; | 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> | template<typename ObjectType> | ||||
| ObjectType *requiredChild(MainWindow &window, const char *name) | 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 | TARGET = main_window_tests | ||||
| CONFIG += console c++17 warn_on | |||||
| CONFIG -= app_bundle | |||||
| INCLUDEPATH += ../src | |||||
| QT += widgets serialport testlib | |||||
| SOURCES += \ | SOURCES += \ | ||||
| main_window_tests.cpp \ | 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 += \ | 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/hmi_runtime_service.h" | ||||
| #include "services/offline_simulation_service.h" | #include "services/offline_simulation_service.h" | ||||
| #include "services/software_logic_executor.h" | #include "services/software_logic_executor.h" | ||||
| #include "support/test_support.h" | |||||
| #include <QCoreApplication> | #include <QCoreApplication> | ||||
| @@ -14,13 +15,7 @@ | |||||
| namespace { | 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, | LogicNode contact(const std::string &id, int address, | ||||
| ContactMode mode = ContactMode::NormallyOpen) | 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 | TARGET = offline_simulation_service_tests | ||||
| CONFIG += console c++17 warn_on | |||||
| CONFIG -= app_bundle | |||||
| INCLUDEPATH += ../src | |||||
| QT += core | |||||
| QT -= gui | |||||
| SOURCES += \ | SOURCES += \ | ||||
| offline_simulation_service_tests.cpp \ | 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/hmi_runtime_service.cpp \ | ||||
| ../src/services/software_logic_executor.cpp \ | |||||
| ../src/services/offline_simulation_service.cpp | |||||
| $$SERVICE_OFFLINE_SOURCES | |||||
| HEADERS += \ | 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/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 "domain/project_storage.h" | ||||
| #include "infrastructure/plc_communication_error_classifier.h" | #include "infrastructure/plc_communication_error_classifier.h" | ||||
| #include "infrastructure/plc_communication_service.h" | #include "infrastructure/plc_communication_service.h" | ||||
| #include "support/test_support.h" | |||||
| #include "infrastructure/plc_register_repository.h" | #include "infrastructure/plc_register_repository.h" | ||||
| #include "services/offline_simulation_service.h" | #include "services/offline_simulation_service.h" | ||||
| #include "services/plc_communication_gateway.h" | #include "services/plc_communication_gateway.h" | ||||
| @@ -16,19 +17,7 @@ | |||||
| namespace { | 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 | class FakePlcGateway final : public PlcCommunicationGateway | ||||
| { | { | ||||
| @@ -130,13 +119,7 @@ public: | |||||
| std::function<void(const std::string &)> error_reported; | 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() | 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 | TARGET = plc_runtime_tests | ||||
| QT += core serialbus serialport | |||||
| CONFIG += console c++17 testcase warn_on | |||||
| CONFIG -= app_bundle | |||||
| INCLUDEPATH += ../src | |||||
| CONFIG += testcase | |||||
| SOURCES += \ | SOURCES += \ | ||||
| plc_runtime_tests.cpp \ | 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 += \ | 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/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/project_storage.h" | ||||
| #include "domain/hmi_model.h" | #include "domain/hmi_model.h" | ||||
| #include "infrastructure/json_project_storage.h" | #include "infrastructure/json_project_storage.h" | ||||
| #include "support/test_support.h" | |||||
| #include "services/register_comment_service.h" | #include "services/register_comment_service.h" | ||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "domain/project_limits.h" | #include "domain/project_limits.h" | ||||
| @@ -19,13 +20,7 @@ | |||||
| namespace { | namespace { | ||||
| void require(bool condition, const std::string &message) | |||||
| { | |||||
| if (!condition) | |||||
| { | |||||
| throw std::runtime_error(message); | |||||
| } | |||||
| } | |||||
| using TestSupport::require; | |||||
| Project makeExampleProject() | 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 | QT += core | ||||
| INCLUDEPATH += ../src | |||||
| CONFIG += testcase | |||||
| SOURCES += \ | SOURCES += \ | ||||
| project_management_tests.cpp \ | 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 += \ | 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_monitor_model.h" | ||||
| #include "domain/register_repository.h" | #include "domain/register_repository.h" | ||||
| #include "services/register_monitor_service.h" | #include "services/register_monitor_service.h" | ||||
| #include "support/test_support.h" | |||||
| #include <iostream> | #include <iostream> | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| @@ -9,13 +10,7 @@ | |||||
| namespace { | namespace { | ||||
| void require(bool condition, const std::string &message) | |||||
| { | |||||
| if (!condition) | |||||
| { | |||||
| throw std::runtime_error(message); | |||||
| } | |||||
| } | |||||
| using TestSupport::require; | |||||
| void testRangeManagement() | 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 | TARGET = register_monitor_service_tests | ||||
| CONFIG += console c++17 warn_on | |||||
| CONFIG -= app_bundle | |||||
| INCLUDEPATH += ../src | |||||
| QT -= gui | |||||
| SOURCES += \ | SOURCES += \ | ||||
| register_monitor_service_tests.cpp \ | 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 | ../src/services/register_monitor_service.cpp | ||||
| HEADERS += \ | 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/active_register_repository.h" | ||||
| #include "domain/project_storage.h" | #include "domain/project_storage.h" | ||||
| #include "domain/register_repository.h" | #include "domain/register_repository.h" | ||||
| #include "support/test_support.h" | |||||
| #include <functional> | #include <functional> | ||||
| #include <iostream> | #include <iostream> | ||||
| @@ -13,19 +14,7 @@ | |||||
| namespace { | 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 | class ReadyPlcGateway final : public PlcCommunicationGateway | ||||
| { | { | ||||
| @@ -97,13 +86,7 @@ private: | |||||
| std::vector<RegisterAddress> poll_addresses; | 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() | 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 | TARGET = runtime_mode_service_tests | ||||
| CONFIG += console c++17 warn_on | |||||
| CONFIG -= app_bundle | |||||
| INCLUDEPATH += ../src | |||||
| QT -= gui | |||||
| SOURCES += \ | SOURCES += \ | ||||
| runtime_mode_service_tests.cpp \ | 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 += \ | 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 | |||||
| } | |||||
| } | |||||