Procházet zdrojové kódy

test: 优化功能和性能测试程序

main
suyu před 3 týdny
rodič
revize
240ce7ac03
15 změnil soubory, kde provedl 306 přidání a 313 odebrání
  1. +4
    -13
      app/tests/alarm_service_tests.cpp
  2. +8
    -17
      app/tests/application_settings_tests.cpp
  3. +20
    -30
      app/tests/domain_tests.cpp
  4. +24
    -38
      app/tests/hmi_editor_service_tests.cpp
  5. +29
    -38
      app/tests/logic_editor_service_tests.cpp
  6. +21
    -29
      app/tests/offline_simulation_service_tests.cpp
  7. +96
    -27
      app/tests/performance_tests.cpp
  8. +5
    -14
      app/tests/plc_connection_dialog_tests.cpp
  9. +9
    -17
      app/tests/plc_runtime_tests.cpp
  10. +12
    -21
      app/tests/project_management_tests.cpp
  11. +13
    -16
      app/tests/register_monitor_service_tests.cpp
  12. +5
    -15
      app/tests/runtime_mode_service_tests.cpp
  13. +15
    -25
      app/tests/runtime_panel_controller_tests.cpp
  14. +3
    -13
      app/tests/runtime_project_bundle_tests.cpp
  15. +42
    -0
      app/tests/support/test_support.h

+ 4
- 13
app/tests/alarm_service_tests.cpp Zobrazit soubor

@@ -140,17 +140,8 @@ void testConfiguredAlarmLimit()

int main()
{
try
{
testAlarmDefinitionsAndRuntimeLifecycle();
testConfiguredAlarmLimit();
}
catch (const std::exception &error)
{
std::cerr << "alarm service tests failed: " << error.what() << '\n';
return 1;
}

std::cout << "alarm service tests passed\n";
return 0;
return TestSupport::runTestSuite("alarm service tests", {
{"testAlarmDefinitionsAndRuntimeLifecycle", testAlarmDefinitionsAndRuntimeLifecycle},
{"testConfiguredAlarmLimit", testConfiguredAlarmLimit},
});
}

+ 8
- 17
app/tests/application_settings_tests.cpp Zobrazit soubor

@@ -232,21 +232,12 @@ void testCreationFailureKeepsTheApplicationStartable()
int main(int argc, char *argv[])
{
QCoreApplication application(argc, argv);
try
{
testMissingFileCreatesDefaults();
testCompleteConfigurationLoadsEveryPublicField();
testMissingAndUnknownFieldsDoNotInvalidateConfiguration();
testDuplicateOrInvalidFieldsRejectTheWholeFile();
testVersionAndEncodingErrorsRejectTheWholeFile();
testCreationFailureKeepsTheApplicationStartable();
}
catch (const std::exception &error)
{
std::cerr << "application settings tests failed: "
<< error.what() << '\n';
return 1;
}
std::cout << "application settings tests passed\n";
return 0;
return TestSupport::runTestSuite("application settings tests", {
{"testMissingFileCreatesDefaults", testMissingFileCreatesDefaults},
{"testCompleteConfigurationLoadsEveryPublicField", testCompleteConfigurationLoadsEveryPublicField},
{"testMissingAndUnknownFieldsDoNotInvalidateConfiguration", testMissingAndUnknownFieldsDoNotInvalidateConfiguration},
{"testDuplicateOrInvalidFieldsRejectTheWholeFile", testDuplicateOrInvalidFieldsRejectTheWholeFile},
{"testVersionAndEncodingErrorsRejectTheWholeFile", testVersionAndEncodingErrorsRejectTheWholeFile},
{"testCreationFailureKeepsTheApplicationStartable", testCreationFailureKeepsTheApplicationStartable},
});
}

+ 20
- 30
app/tests/domain_tests.cpp Zobrazit soubor

@@ -1120,34 +1120,24 @@ void testRuntimeConfiguredProjectLimits()

int main()
{
try
{
// 每个测试函数独立覆盖一个领域边界,首个异常即终止测试进程
testRegisterAddressBoundaries();
testRegisterAddressParsing();
testRegisterRepositorySeparatesAreas();
testMultiWordCodecsAndBlockAccess();
testHmiControlRegistryCompleteness();
testStatusTextDomainRules();
testMultiWordHmiBoundaries();
testHmiAppearancePropertyBoundaries();
testButtonEnableConditionBoundaries();
testLogicNodeConfigurationBoundaries();
testEdgeAndCommentBoundaries();
testDataInstructionBoundaries();
testLadderLogicBoundaries();
testModelsValidateBindingsAndIdentifiers();
testMultiPageAndLogicDomainRules();
testQuantityBoundaries();
testRuntimeConfiguredProjectLimits();
testRuntimeStateBoundaries();
}
catch (const std::exception &error)
{
std::cerr << "domain tests failed: " << error.what() << '\n';
return 1;
}

std::cout << "domain tests passed\n";
return 0;
return TestSupport::runTestSuite("domain tests", {
{"testRegisterAddressBoundaries", testRegisterAddressBoundaries},
{"testRegisterAddressParsing", testRegisterAddressParsing},
{"testRegisterRepositorySeparatesAreas", testRegisterRepositorySeparatesAreas},
{"testMultiWordCodecsAndBlockAccess", testMultiWordCodecsAndBlockAccess},
{"testHmiControlRegistryCompleteness", testHmiControlRegistryCompleteness},
{"testStatusTextDomainRules", testStatusTextDomainRules},
{"testMultiWordHmiBoundaries", testMultiWordHmiBoundaries},
{"testHmiAppearancePropertyBoundaries", testHmiAppearancePropertyBoundaries},
{"testButtonEnableConditionBoundaries", testButtonEnableConditionBoundaries},
{"testLogicNodeConfigurationBoundaries", testLogicNodeConfigurationBoundaries},
{"testEdgeAndCommentBoundaries", testEdgeAndCommentBoundaries},
{"testDataInstructionBoundaries", testDataInstructionBoundaries},
{"testLadderLogicBoundaries", testLadderLogicBoundaries},
{"testModelsValidateBindingsAndIdentifiers", testModelsValidateBindingsAndIdentifiers},
{"testMultiPageAndLogicDomainRules", testMultiPageAndLogicDomainRules},
{"testQuantityBoundaries", testQuantityBoundaries},
{"testRuntimeConfiguredProjectLimits", testRuntimeConfiguredProjectLimits},
{"testRuntimeStateBoundaries", testRuntimeStateBoundaries},
});
}

+ 24
- 38
app/tests/hmi_editor_service_tests.cpp Zobrazit soubor

@@ -290,6 +290,12 @@ void testRuntimeUsesRegisterRepository()
const HmiRuntimeReadResult indicator_value = runtime_service.readControl(indicator);
require(indicator_value.succeeded && indicator_value.bit_value,
"indicators must read M values through the repository");
}

void testNumericControlsUseRegisterRepository()
{
VirtualRegisterRepository repository;
HmiRuntimeService runtime_service(repository);

HmiControl numeric_input;
numeric_input.id = "target";
@@ -325,30 +331,20 @@ void testRuntimeUsesRegisterRepository()
int32_input.binding = RegisterAddress{RegisterArea::D, 30};
require(runtime_service.writeNumericInput(int32_input, 305419896).succeeded,
"Int32 numeric input must write two consecutive D words");
const WordsReadResult int32_words = repository.readWords(*int32_input.binding, 2);
const HmiRuntimeReadResult int32_value = runtime_service.readControl(int32_input);
require(int32_words.succeeded
&& static_cast<std::uint16_t>(int32_words.values[0]) == 0x5678U
&& static_cast<std::uint16_t>(int32_words.values[1]) == 0x1234U
&& int32_value.succeeded
require(int32_value.succeeded
&& std::get<std::int32_t>(int32_value.numeric_value) == 0x12345678,
"Int32 HMI read/write must use low-word-first Xinje ordering");
"Int32 HMI read/write must preserve the typed value");

HmiControl double_input = numeric_input;
double_input.dataType = RegisterDataType::Float64;
double_input.binding = RegisterAddress{RegisterArea::D, 40};
require(runtime_service.writeNumericInput(double_input, 1.0).succeeded,
"Double numeric input must write four consecutive D words");
const WordsReadResult double_words = repository.readWords(*double_input.binding, 4);
const HmiRuntimeReadResult double_value = runtime_service.readControl(double_input);
require(double_words.succeeded
&& static_cast<std::uint16_t>(double_words.values[0]) == 0x0000U
&& static_cast<std::uint16_t>(double_words.values[1]) == 0x0000U
&& static_cast<std::uint16_t>(double_words.values[2]) == 0x0000U
&& static_cast<std::uint16_t>(double_words.values[3]) == 0x3ff0U
&& double_value.succeeded
require(double_value.succeeded
&& std::get<double>(double_value.numeric_value) == 1.0,
"Double HMI read/write must use four low-address-first words");
"Double HMI read/write must preserve the typed value");
require(!runtime_service.writeNumericInput(
double_input, std::numeric_limits<double>::quiet_NaN()).succeeded,
"Double numeric input must reject NaN");
@@ -356,7 +352,6 @@ void testRuntimeUsesRegisterRepository()
require(runtime_service.writeNumericInput(double_input, 1.0).error
== HmiRuntimeError::InvalidBinding,
"Double numeric input must reject odd or overflowing start addresses");

}

void testStatusTextRuntimeMapping()
@@ -756,27 +751,18 @@ void testConfiguredPageDefaultsAndLimits()

int main()
{
try
{
// 编辑和运行场景分别验证服务层两条独立职责
testControlEditing();
testBatchControlAlignment();
testHistoryAndAtomicBatchDelete();
testBatchPasteControls();
testRuntimeValidationAndPasteBoundaries();
testAppearanceEditing();
testRuntimeUsesRegisterRepository();
testStatusTextRuntimeMapping();
testPageLifecycleAndNavigation();
testPageResizeIsAtomicAndUndoable();
testConfiguredPageDefaultsAndLimits();
}
catch (const std::exception &error)
{
std::cerr << "HMI editor service tests failed: " << error.what() << '\n';
return 1;
}

std::cout << "HMI editor service tests passed\n";
return 0;
return TestSupport::runTestSuite("HMI editor service tests", {
{"testControlEditing", testControlEditing},
{"testBatchControlAlignment", testBatchControlAlignment},
{"testHistoryAndAtomicBatchDelete", testHistoryAndAtomicBatchDelete},
{"testBatchPasteControls", testBatchPasteControls},
{"testRuntimeValidationAndPasteBoundaries", testRuntimeValidationAndPasteBoundaries},
{"testAppearanceEditing", testAppearanceEditing},
{"testRuntimeUsesRegisterRepository", testRuntimeUsesRegisterRepository},
{"testNumericControlsUseRegisterRepository", testNumericControlsUseRegisterRepository},
{"testStatusTextRuntimeMapping", testStatusTextRuntimeMapping},
{"testPageLifecycleAndNavigation", testPageLifecycleAndNavigation},
{"testPageResizeIsAtomicAndUndoable", testPageResizeIsAtomicAndUndoable},
{"testConfiguredPageDefaultsAndLimits", testConfiguredPageDefaultsAndLimits},
});
}

+ 29
- 38
app/tests/logic_editor_service_tests.cpp Zobrazit soubor

@@ -1488,42 +1488,33 @@ void testDoubleCoilCheckRemainsIndependent()

int main()
{
try
{
testContinuousGridAndIndependentHorizontalWires();
testIndependentVerticalConnectionsAndNetworkSplit();
testNetworkCommentsFollowNetworkHeadsAndMergeAtomically();
testInsertRowSplitsVerticalEdges();
testDeleteRowMergesOnlyContinuousEdges();
testParallelBranchCreatesConnectedVisualRow();
testParallelBranchReusesExistingEdges();
testNodeDeletionAndHistoryAreAtomic();
testSelectionDeletionIsAtomic();
testInvalidSelectionDeletionDoesNotMutateOrRecordHistory();
testCommandInputMapsToGridCoordinates();
testProjectRungLimitAppliesToBranchAndPaste();
testConfiguredRowLimit();
testFirstEditOnEmptyLogicIsAtomic();
testCursorAdvanceAndOutputTransaction();
testOutputAutomaticallyCompletesTrailingWires();
testOutputAdvancesPastTheWholeNetworkGroup();
testOutputLimitFailureLeavesNoPartialEdit();
testSingleWireClipboardPasteAndUndo();
testMixedAndSparseGridClipboardFragments();
testGridClipboardFailuresAreAtomic();
testOutputAndVerticalClipboardRules();
testWholeRowClipboardInsertionAndLimit();
testSyntaxCheckNormalizesUnusedWiresAsOneEdit();
testSyntaxCheckPreservesValidParallelPath();
testSyntaxCheckKeepsBrokenOutputForErrorLocation();
testDoubleCoilCheckRemainsIndependent();
}
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;
return TestSupport::runTestSuite("logic editor service tests", {
{"testContinuousGridAndIndependentHorizontalWires", testContinuousGridAndIndependentHorizontalWires},
{"testIndependentVerticalConnectionsAndNetworkSplit", testIndependentVerticalConnectionsAndNetworkSplit},
{"testNetworkCommentsFollowNetworkHeadsAndMergeAtomically", testNetworkCommentsFollowNetworkHeadsAndMergeAtomically},
{"testInsertRowSplitsVerticalEdges", testInsertRowSplitsVerticalEdges},
{"testDeleteRowMergesOnlyContinuousEdges", testDeleteRowMergesOnlyContinuousEdges},
{"testParallelBranchCreatesConnectedVisualRow", testParallelBranchCreatesConnectedVisualRow},
{"testParallelBranchReusesExistingEdges", testParallelBranchReusesExistingEdges},
{"testNodeDeletionAndHistoryAreAtomic", testNodeDeletionAndHistoryAreAtomic},
{"testSelectionDeletionIsAtomic", testSelectionDeletionIsAtomic},
{"testInvalidSelectionDeletionDoesNotMutateOrRecordHistory", testInvalidSelectionDeletionDoesNotMutateOrRecordHistory},
{"testCommandInputMapsToGridCoordinates", testCommandInputMapsToGridCoordinates},
{"testProjectRungLimitAppliesToBranchAndPaste", testProjectRungLimitAppliesToBranchAndPaste},
{"testConfiguredRowLimit", testConfiguredRowLimit},
{"testFirstEditOnEmptyLogicIsAtomic", testFirstEditOnEmptyLogicIsAtomic},
{"testCursorAdvanceAndOutputTransaction", testCursorAdvanceAndOutputTransaction},
{"testOutputAutomaticallyCompletesTrailingWires", testOutputAutomaticallyCompletesTrailingWires},
{"testOutputAdvancesPastTheWholeNetworkGroup", testOutputAdvancesPastTheWholeNetworkGroup},
{"testOutputLimitFailureLeavesNoPartialEdit", testOutputLimitFailureLeavesNoPartialEdit},
{"testSingleWireClipboardPasteAndUndo", testSingleWireClipboardPasteAndUndo},
{"testMixedAndSparseGridClipboardFragments", testMixedAndSparseGridClipboardFragments},
{"testGridClipboardFailuresAreAtomic", testGridClipboardFailuresAreAtomic},
{"testOutputAndVerticalClipboardRules", testOutputAndVerticalClipboardRules},
{"testWholeRowClipboardInsertionAndLimit", testWholeRowClipboardInsertionAndLimit},
{"testSyntaxCheckNormalizesUnusedWiresAsOneEdit", testSyntaxCheckNormalizesUnusedWiresAsOneEdit},
{"testSyntaxCheckPreservesValidParallelPath", testSyntaxCheckPreservesValidParallelPath},
{"testSyntaxCheckKeepsBrokenOutputForErrorLocation", testSyntaxCheckKeepsBrokenOutputForErrorLocation},
{"testDoubleCoilCheckRemainsIndependent", testDoubleCoilCheckRemainsIndependent},
});
}

+ 21
- 29
app/tests/offline_simulation_service_tests.cpp Zobrazit soubor

@@ -901,33 +901,25 @@ void testOnlineMonitorPreservesFaultAfterStopping()
int main(int argc, char *argv[])
{
QCoreApplication application(argc, argv);
try
{
testSeriesParallelContactsAndSequentialVisibility();
testParallelRowsAndColumnPropagation();
testUnconditionalCoil();
testEnabledEmptyRowIsRejectedBeforeScanning();
testWirePassThroughAndPowerTrace();
testMotorForwardReverseSelfHoldAndInterlockTruthTable();
testAllComparisons();
testSetResetAndDisabledLogic();
testMultipleLogicScanOrderAndTraceIsolation();
testEdgeContactsAreOneScanPulsesAndAreLogicScoped();
testMoveAndSaturatingArithmetic();
testSetResetPairOnSameAddress();
testConflictingCoilsAreRejected();
testHmiSimulationClosedLoop();
testSimulationLifecycleSnapshotAndFault();
testSimulationUsesInitialValuesAndDiscardsRuntimeOutputs();
testRepositoryFailureEntersFaultState();
testOnlineMonitorUsesTemporaryRegistersWithoutWritingPlcSource();
testOnlineMonitorPreservesFaultAfterStopping();
}
catch (const std::exception &error)
{
std::cerr << "offline simulation service tests failed: " << error.what() << '\n';
return 1;
}
std::cout << "offline simulation service tests passed\n";
return 0;
return TestSupport::runTestSuite("offline simulation service tests", {
{"testSeriesParallelContactsAndSequentialVisibility", testSeriesParallelContactsAndSequentialVisibility},
{"testParallelRowsAndColumnPropagation", testParallelRowsAndColumnPropagation},
{"testUnconditionalCoil", testUnconditionalCoil},
{"testEnabledEmptyRowIsRejectedBeforeScanning", testEnabledEmptyRowIsRejectedBeforeScanning},
{"testWirePassThroughAndPowerTrace", testWirePassThroughAndPowerTrace},
{"testMotorForwardReverseSelfHoldAndInterlockTruthTable", testMotorForwardReverseSelfHoldAndInterlockTruthTable},
{"testAllComparisons", testAllComparisons},
{"testSetResetAndDisabledLogic", testSetResetAndDisabledLogic},
{"testMultipleLogicScanOrderAndTraceIsolation", testMultipleLogicScanOrderAndTraceIsolation},
{"testEdgeContactsAreOneScanPulsesAndAreLogicScoped", testEdgeContactsAreOneScanPulsesAndAreLogicScoped},
{"testMoveAndSaturatingArithmetic", testMoveAndSaturatingArithmetic},
{"testSetResetPairOnSameAddress", testSetResetPairOnSameAddress},
{"testConflictingCoilsAreRejected", testConflictingCoilsAreRejected},
{"testHmiSimulationClosedLoop", testHmiSimulationClosedLoop},
{"testSimulationLifecycleSnapshotAndFault", testSimulationLifecycleSnapshotAndFault},
{"testSimulationUsesInitialValuesAndDiscardsRuntimeOutputs", testSimulationUsesInitialValuesAndDiscardsRuntimeOutputs},
{"testRepositoryFailureEntersFaultState", testRepositoryFailureEntersFaultState},
{"testOnlineMonitorUsesTemporaryRegistersWithoutWritingPlcSource", testOnlineMonitorUsesTemporaryRegistersWithoutWritingPlcSource},
{"testOnlineMonitorPreservesFaultAfterStopping", testOnlineMonitorPreservesFaultAfterStopping},
});
}

+ 96
- 27
app/tests/performance_tests.cpp Zobrazit soubor

@@ -6,6 +6,7 @@
#include <QDebug>
#include <QTest>

#include <cstdint>
#include <string>
#include <utility>
#include <vector>
@@ -31,29 +32,38 @@ LogicNode coil(const std::string &id, int address)
true};
}

ControlLogic makeLogic(int index)
ControlLogic makeLogic(int index, int rung_count = 1)
{
LadderRung rung;
rung.id = "rung-" + std::to_string(index);
rung.name = rung.id;
for (int column = 0;
column < ProjectLimits::kMaximumConditionColumns;
++column)
ControlLogic logic;
logic.id = "logic-" + std::to_string(index);
logic.name = logic.id;
logic.enabled = true;
for (int rung_index = 0; rung_index < rung_count; ++rung_index)
{
rung.cells.push_back({
rung.id + "-cell-" + std::to_string(column),
column == 0 ? LadderCellKind::Node : LadderCellKind::Wire,
column == 0
? std::optional<LogicNode>{contact(
"contact-" + std::to_string(index), index)}
: std::nullopt});
const int object_index = index * rung_count + rung_index;
LadderRung rung;
rung.id = "rung-" + std::to_string(index) + "-"
+ std::to_string(rung_index);
rung.name = rung.id;
for (int column = 0;
column < ProjectLimits::kMaximumConditionColumns;
++column)
{
rung.cells.push_back({
rung.id + "-cell-" + std::to_string(column),
column == 0 ? LadderCellKind::Node : LadderCellKind::Wire,
column == 0
? std::optional<LogicNode>{contact(
"contact-" + std::to_string(object_index),
object_index)}
: std::nullopt});
}
rung.output = coil(
"coil-" + std::to_string(object_index), object_index + 1000);
logic.rungs.push_back(std::move(rung));
}
rung.output = coil("coil-" + std::to_string(index), index + 100);
return {"logic-" + std::to_string(index),
"logic-" + std::to_string(index),
{rung},
true,
{}};
return logic;
}

class PerformanceTests final : public QObject
@@ -64,14 +74,42 @@ private slots:
void initTestCase()
{
qInfo().noquote()
<< QStringLiteral("性能测试开始:下面两项 RESULT 的耗时均为平均每次执行时间");
<< QStringLiteral("性能测试开始:以下 RESULT 的耗时均为平均每次执行时间");
qInfo().noquote()
<< QStringLiteral("测试项目 1:工程上限规模的软件扫描");
qInfo().noquote()
<< QStringLiteral("测试项目 2:超出工程上限的扫描压力测试");
qInfo().noquote()
<< QStringLiteral("测试项目 1:100 个控制逻辑的软件扫描");
<< QStringLiteral("测试项目 3:4001 个虚拟 M 地址的边界读写");
qInfo().noquote()
<< QStringLiteral("测试项目 2:M0 到 M4000 共 4001 个虚拟 M 地址的完整写入和读取");
<< QStringLiteral("测试项目 4:256 个连续 D 字的轮询规模读写");
}

void softwareExecutor_scansMaximumValidProject()
{
std::vector<ControlLogic> logics;
logics.reserve(ProjectLimits::kMaximumControlLogics);
for (std::size_t index = 0;
index < ProjectLimits::kMaximumControlLogics;
++index)
{
logics.push_back(makeLogic(
static_cast<int>(index), 64));
}

SoftwareLogicExecutor executor;
VirtualRegisterRepository repository;
QVERIFY(executor.validate(logics).succeeded);
QVERIFY(executor.executeScan(logics, repository).succeeded);

QBENCHMARK
{
executor.executeScan(logics, repository);
}
QVERIFY(executor.executeScan(logics, repository).succeeded);
}

void softwareExecutor_scansRepresentativeProject()
void softwareExecutor_scansStressProject()
{
std::vector<ControlLogic> logics;
logics.reserve(100);
@@ -83,15 +121,15 @@ private slots:
SoftwareLogicExecutor executor;
VirtualRegisterRepository repository;
QVERIFY(executor.validate(logics).succeeded);
QVERIFY(executor.executeScan(logics, repository).succeeded);

QBENCHMARK
{
executor.executeScan(logics, repository);
}
QVERIFY(executor.executeScan(logics, repository).succeeded);
}

void virtualRepository_handlesBoundaryWorkload()
void virtualRepository_handlesBoundaryBitWorkload()
{
VirtualRegisterRepository repository;
for (int index = 0; index <= RegisterAddress::kMaximumIndex; ++index)
@@ -111,12 +149,43 @@ private slots:
repository.readBit({RegisterArea::M, index});
}
}
QVERIFY(repository.readBit({RegisterArea::M, 0}).succeeded);
QVERIFY(repository.readBit({RegisterArea::M, RegisterAddress::kMaximumIndex}).succeeded);
}

void virtualRepository_handlesPollSizedWordBlocks()
{
const int word_count = static_cast<int>(ProjectLimits::kMaximumPollAddresses);
std::vector<std::int16_t> values(static_cast<std::size_t>(word_count));
for (int index = 0; index < word_count; ++index)
{
values[static_cast<std::size_t>(index)] =
static_cast<std::int16_t>(index);
}

VirtualRegisterRepository repository;
QVERIFY(repository.writeWords(
{RegisterArea::D, 0}, values)
.succeeded);
QVERIFY(repository.readWords(
{RegisterArea::D, 0}, word_count)
.succeeded);

QBENCHMARK
{
repository.writeWords({RegisterArea::D, 0}, values);
repository.readWords({RegisterArea::D, 0}, word_count);
}

const WordsReadResult result = repository.readWords(
{RegisterArea::D, 0}, word_count);
QVERIFY(result.succeeded && result.values == values);
}

void cleanupTestCase()
{
qInfo().noquote()
<< QStringLiteral("性能测试结束:请结合上方两项 RESULT 和最后的 Totals 判断结果");
<< QStringLiteral("性能测试结束:请结合上方项 RESULT 和最后的 Totals 判断结果");
}
};



+ 5
- 14
app/tests/plc_connection_dialog_tests.cpp Zobrazit soubor

@@ -186,18 +186,9 @@ int main(int argc, char *argv[])
{
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("offscreen"));
QApplication application(argc, argv);
try
{
testDiscoveryFindsAndAcceptsConfiguration();
testDiscoveryCanBeCancelled();
testPortRefreshRemovesUnavailableSelection();
}
catch (const std::exception &error)
{
std::cerr << "PLC connection dialog tests failed: "
<< error.what() << '\n';
return 1;
}
std::cout << "PLC connection dialog tests passed\n";
return 0;
return TestSupport::runTestSuite("PLC connection dialog tests", {
{"testDiscoveryFindsAndAcceptsConfiguration", testDiscoveryFindsAndAcceptsConfiguration},
{"testDiscoveryCanBeCancelled", testDiscoveryCanBeCancelled},
{"testPortRefreshRemovesUnavailableSelection", testPortRefreshRemovesUnavailableSelection},
});
}

+ 9
- 17
app/tests/plc_runtime_tests.cpp Zobrazit soubor

@@ -529,21 +529,13 @@ void testPlcPollQuantityBoundaries()

int main()
{
try
{
testPlcCacheAndWriteForwarding();
testRuntimeRepositorySwitchingAndDisconnect();
testRuntimeFaultRevokesOnlineReadinessAndAllowsReconnect();
testPlcCommunicationErrorClassification();
testPlcConfigurationBoundaries();
testPlcDiscoveryCandidatePriorityAndCoverage();
testPlcPollQuantityBoundaries();
}
catch (const std::exception &error)
{
std::cerr << "PLC runtime tests failed: " << error.what() << '\n';
return 1;
}
std::cout << "PLC runtime tests passed\n";
return 0;
return TestSupport::runTestSuite("PLC runtime tests", {
{"testPlcCacheAndWriteForwarding", testPlcCacheAndWriteForwarding},
{"testRuntimeRepositorySwitchingAndDisconnect", testRuntimeRepositorySwitchingAndDisconnect},
{"testRuntimeFaultRevokesOnlineReadinessAndAllowsReconnect", testRuntimeFaultRevokesOnlineReadinessAndAllowsReconnect},
{"testPlcCommunicationErrorClassification", testPlcCommunicationErrorClassification},
{"testPlcConfigurationBoundaries", testPlcConfigurationBoundaries},
{"testPlcDiscoveryCandidatePriorityAndCoverage", testPlcDiscoveryCandidatePriorityAndCoverage},
{"testPlcPollQuantityBoundaries", testPlcPollQuantityBoundaries},
});
}

+ 12
- 21
app/tests/project_management_tests.cpp Zobrazit soubor

@@ -593,25 +593,16 @@ void testRegisterCommentService()

int main()
{
try
{
testEmptyProjectRoundTrip();
testGridProjectRoundTrip();
testMOffAlarmRoundTrip();
testMultiWordHmiDataTypeRoundTrip();
testStatusTextRoundTrip();
testButtonEnableConditionRoundTrip();
testStrictVersionAndRequiredFields();
testInvalidGridAndConnectionsAreRejected();
testProjectServiceStateAndConfiguredLimits();
testRegisterCommentService();
}
catch (const std::exception &error)
{
std::cerr << "project management tests failed: "
<< error.what() << '\n';
return 1;
}
std::cout << "project management tests passed\n";
return 0;
return TestSupport::runTestSuite("project management tests", {
{"testEmptyProjectRoundTrip", testEmptyProjectRoundTrip},
{"testGridProjectRoundTrip", testGridProjectRoundTrip},
{"testMOffAlarmRoundTrip", testMOffAlarmRoundTrip},
{"testMultiWordHmiDataTypeRoundTrip", testMultiWordHmiDataTypeRoundTrip},
{"testStatusTextRoundTrip", testStatusTextRoundTrip},
{"testButtonEnableConditionRoundTrip", testButtonEnableConditionRoundTrip},
{"testStrictVersionAndRequiredFields", testStrictVersionAndRequiredFields},
{"testInvalidGridAndConnectionsAreRejected", testInvalidGridAndConnectionsAreRejected},
{"testProjectServiceStateAndConfiguredLimits", testProjectServiceStateAndConfiguredLimits},
{"testRegisterCommentService", testRegisterCommentService},
});
}

+ 13
- 16
app/tests/register_monitor_service_tests.cpp Zobrazit soubor

@@ -274,7 +274,11 @@ void testMultiWordMonitoring()
require(std::get<std::int32_t>(values[3].numericValue) == 0x12345678
&& std::get<double>(values[5].numericValue) == 1.25,
"Int32 and Double monitor values must decode to their exact types");
}

void testMultiWordMonitoringBoundaries()
{
VirtualRegisterRepository repository;
RegisterMonitorService boundary_service(repository);
require(boundary_service.addRange(
"D3999", 1, RegisterDataType::Int32).succeeded
@@ -309,20 +313,13 @@ void testMultiWordMonitoring()

int main()
{
try
{
testRangeManagement();
testSharedActiveRepositoryValues();
testEditingPlcMonitorAccess();
testRegisterWrites();
testOfflineInitialValueCapture();
testMultiWordMonitoring();
}
catch (const std::exception &error)
{
std::cerr << "register monitor service tests failed: " << error.what() << '\n';
return 1;
}
std::cout << "register monitor service tests passed\n";
return 0;
return TestSupport::runTestSuite("register monitor service tests", {
{"testRangeManagement", testRangeManagement},
{"testSharedActiveRepositoryValues", testSharedActiveRepositoryValues},
{"testEditingPlcMonitorAccess", testEditingPlcMonitorAccess},
{"testRegisterWrites", testRegisterWrites},
{"testOfflineInitialValueCapture", testOfflineInitialValueCapture},
{"testMultiWordMonitoring", testMultiWordMonitoring},
{"testMultiWordMonitoringBoundaries", testMultiWordMonitoringBoundaries},
});
}

+ 5
- 15
app/tests/runtime_mode_service_tests.cpp Zobrazit soubor

@@ -481,19 +481,9 @@ void testDisconnectedOutputBlocksOfflineAndOnlineRuntime()

int main()
{
try
{
// 运行模式只有这一组状态机边界测试
testModeTransitions();
testMonitorPollRangeRollback();
testDisconnectedOutputBlocksOfflineAndOnlineRuntime();
}
catch (const std::exception &error)
{
std::cerr << "runtime mode service tests failed: " << error.what() << '\n';
return 1;
}

std::cout << "runtime mode service tests passed\n";
return 0;
return TestSupport::runTestSuite("runtime mode service tests", {
{"testModeTransitions", testModeTransitions},
{"testMonitorPollRangeRollback", testMonitorPollRangeRollback},
{"testDisconnectedOutputBlocksOfflineAndOnlineRuntime", testDisconnectedOutputBlocksOfflineAndOnlineRuntime},
});
}

+ 15
- 25
app/tests/runtime_panel_controller_tests.cpp Zobrazit soubor

@@ -1387,29 +1387,19 @@ int main(int argc, char *argv[])
{
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("offscreen"));
QApplication application(argc, argv);
try
{
testAlarmConfigurationOffersMOnAndMOff();
testMonitorOffersAllNumericTypes();
testAlarmListKeepsFixedGeometryWhileRecordsChange();
testHmiPropertyPanelInfersFixedBindingAreas();
testQueuedOfflineTraceIsIgnoredAfterReturningToEditing();
testLogicEditorGridSelectionAndDeletion();
testLadderLayoutAndDragDeletion();
testWireGesturePreviewKeepsAtomicCommit();
testEscapeExitsMouseWireMode();
testCursorAdvanceAndInlineCommandInput();
testVerticalWireShortcutAdvancesDownward();
testSegmentLevelTraceProjection();
testLogicClipboardUsesExplicitObjectAndRowSelection();
}
catch (const std::exception &error)
{
std::cerr << "runtime panel controller tests failed: "
<< error.what() << '\n';
return 1;
}

std::cout << "runtime panel controller tests passed\n";
return 0;
return TestSupport::runTestSuite("runtime panel controller tests", {
{"testAlarmConfigurationOffersMOnAndMOff", testAlarmConfigurationOffersMOnAndMOff},
{"testMonitorOffersAllNumericTypes", testMonitorOffersAllNumericTypes},
{"testAlarmListKeepsFixedGeometryWhileRecordsChange", testAlarmListKeepsFixedGeometryWhileRecordsChange},
{"testHmiPropertyPanelInfersFixedBindingAreas", testHmiPropertyPanelInfersFixedBindingAreas},
{"testQueuedOfflineTraceIsIgnoredAfterReturningToEditing", testQueuedOfflineTraceIsIgnoredAfterReturningToEditing},
{"testLogicEditorGridSelectionAndDeletion", testLogicEditorGridSelectionAndDeletion},
{"testLadderLayoutAndDragDeletion", testLadderLayoutAndDragDeletion},
{"testWireGesturePreviewKeepsAtomicCommit", testWireGesturePreviewKeepsAtomicCommit},
{"testEscapeExitsMouseWireMode", testEscapeExitsMouseWireMode},
{"testCursorAdvanceAndInlineCommandInput", testCursorAdvanceAndInlineCommandInput},
{"testVerticalWireShortcutAdvancesDownward", testVerticalWireShortcutAdvancesDownward},
{"testSegmentLevelTraceProjection", testSegmentLevelTraceProjection},
{"testLogicClipboardUsesExplicitObjectAndRowSelection", testLogicClipboardUsesExplicitObjectAndRowSelection},
});
}

+ 3
- 13
app/tests/runtime_project_bundle_tests.cpp Zobrazit soubor

@@ -68,17 +68,7 @@ void testBundleRoundTripAndValidation()

int main()
{
try
{
testBundleRoundTripAndValidation();
}
catch (const std::exception &error)
{
std::cerr << "runtime project bundle tests failed: "
<< error.what() << '\n';
return 1;
}

std::cout << "runtime project bundle tests passed\n";
return 0;
return TestSupport::runTestSuite("runtime project bundle tests", {
{"testBundleRoundTripAndValidation", testBundleRoundTripAndValidation},
});
}

+ 42
- 0
app/tests/support/test_support.h Zobrazit soubor

@@ -2,6 +2,9 @@

#include "domain/project_storage.h"

#include <cstddef>
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <string>

@@ -16,6 +19,45 @@ inline void require(bool condition, const std::string &message)
}
}

struct TestCase
{
const char *name;
void (*run)();
};

// 每个用例独立记录结果,避免首个失败掩盖同一目标中的其他回归
inline int runTestSuite(
const char *suite_name,
std::initializer_list<TestCase> test_cases)
{
std::size_t failed_count = 0U;
for (const TestCase &test_case : test_cases)
{
try
{
test_case.run();
std::cout << "[PASS] " << test_case.name << '\n';
}
catch (const std::exception &error)
{
++failed_count;
std::cerr << "[FAIL] " << test_case.name << ": "
<< error.what() << '\n';
}
catch (...)
{
++failed_count;
std::cerr << "[FAIL] " << test_case.name
<< ": unknown exception\n";
}
}

const std::size_t passed_count = test_cases.size() - failed_count;
std::cout << suite_name << ": " << passed_count << "/"
<< test_cases.size() << " test cases passed\n";
return failed_count == 0U ? 0 : 1;
}

// 编辑服务测试只验证内存模型和服务契约,不应依赖真实文件系统
class InMemoryProjectStorage final : public ProjectStorage
{


Načítá se…
Zrušit
Uložit