|
- #include "domain/project_storage.h"
- #include "services/logic_editor_service.h"
- #include "services/project_service.h"
-
- #include <iostream>
- #include <stdexcept>
-
- 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);
- }
- }
-
- ContactNodeConfig contact(int address)
- {
- return {RegisterAddress{RegisterArea::M, address}, ContactMode::NormallyOpen};
- }
-
- void testStructuredEditingAndNormalization()
- {
- TestProjectStorage storage;
- ProjectService project_service(storage);
- LogicEditorService service(project_service);
- const std::string logic_id = service.ensureDefaultLogic().id;
- const std::string rung_id = service.firstRungId(logic_id);
-
- const LogicEditorResult first = service.appendCondition(logic_id, rung_id, contact(0));
- const LogicEditorResult second = service.appendCondition(logic_id, rung_id, contact(1));
- require(first.succeeded && second.succeeded, "series append must succeed");
-
- const LadderRung *rung = service.findRung(logic_id, rung_id);
- require(rung->condition->kind == ConditionExpressionKind::Series
- && rung->condition->children.size() == 2U,
- "two appended nodes must form a series expression");
-
- const std::string second_expression_id = second.id;
- const LogicEditorResult parallel = service.addParallelCondition(
- logic_id, rung_id, second_expression_id, contact(2));
- require(parallel.succeeded, "parallel insertion must succeed");
- rung = service.findRung(logic_id, rung_id);
- const ConditionExpression *parallel_expression = service.findExpression(
- logic_id, rung_id, rung->condition->children.at(1).id);
- require(parallel_expression != nullptr
- && parallel_expression->kind == ConditionExpressionKind::Parallel,
- "selected node must become a parallel expression");
-
- const LogicEditorResult nested_series = service.insertCondition(
- logic_id,
- rung_id,
- parallel.id,
- SeriesInsertPosition::After,
- contact(3));
- require(nested_series.succeeded, "a parallel branch must accept a series node");
- rung = service.findRung(logic_id, rung_id);
- require(rung->condition->kind == ConditionExpressionKind::Series,
- "root must remain a series expression");
- const ConditionExpression &nested_parallel_expression = rung->condition->children.at(1);
- require(nested_parallel_expression.kind == ConditionExpressionKind::Parallel
- && nested_parallel_expression.children.at(1).kind
- == ConditionExpressionKind::Series,
- "editor must express A AND (B OR (C AND D))");
-
- require(service.removeNode(logic_id, nested_series.id).succeeded,
- "nested series node deletion must succeed");
- rung = service.findRung(logic_id, rung_id);
- require(rung->condition->children.at(1).kind == ConditionExpressionKind::Parallel
- && rung->condition->children.at(1).children.at(1).kind
- == ConditionExpressionKind::Node,
- "single-child series container must collapse after deletion");
-
- require(service.removeNode(logic_id, parallel.id).succeeded,
- "parallel leaf deletion must succeed");
- rung = service.findRung(logic_id, rung_id);
- require(rung->condition->kind == ConditionExpressionKind::Series
- && rung->condition->children.size() == 2U,
- "single-child parallel container must collapse after deletion");
-
- require(service.setOutput(
- logic_id,
- rung_id,
- CoilNodeConfig{RegisterAddress{RegisterArea::M, 10}, CoilMode::Normal})
- .succeeded,
- "output coil must be set");
- require(service.findLogic(logic_id)->validate(),
- "structured editing result must remain a valid draft");
- }
-
- void testBranchLevelParallelInsertion()
- {
- TestProjectStorage storage;
- ProjectService project_service(storage);
- LogicEditorService service(project_service);
- const std::string logic_id = service.ensureDefaultLogic().id;
- const std::string rung_id = service.firstRungId(logic_id);
- service.appendCondition(logic_id, rung_id, contact(0));
- service.appendCondition(logic_id, rung_id, contact(1));
-
- const std::string series_id = service.findRung(logic_id, rung_id)->condition->id;
- const LogicEditorResult branch = service.addParallelCondition(
- logic_id, rung_id, series_id, contact(2));
- require(branch.succeeded, "a whole series branch must accept a parallel condition");
- const ConditionExpression &root = *service.findRung(logic_id, rung_id)->condition;
- require(root.kind == ConditionExpressionKind::Parallel
- && root.children.front().kind == ConditionExpressionKind::Series,
- "branch-level insertion must express (A AND B) OR C");
- }
-
- } // namespace
-
- int main()
- {
- try
- {
- testStructuredEditingAndNormalization();
- testBranchLevelParallelInsertion();
- }
- catch (const std::exception &error)
- {
- std::cerr << "logic editor service tests failed: " << error.what() << '\n';
- return 1;
- }
- std::cout << "logic editor service tests passed\n";
- return 0;
- }
|