#include "services/plc_discovery_gateway.h" #include "support/test_support.h" #include "ui/plc_connection_dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace { using TestSupport::require; class FakePlcDiscoveryGateway final : public PlcDiscoveryGateway { public: PlcCommunicationResult startDiscovery( const PlcSerialConfiguration &preferred) override { ++start_count; last_preferred = preferred; discovering = true; return {true, {}}; } void cancelDiscovery() override { if (!discovering) { return; } ++cancel_count; discovering = false; if (finished_callback) { finished_callback({false, true, {}, {}}); } } bool isDiscovering() const override { return discovering; } void setCallbacks( std::function progress_changed, std::function discovery_finished) override { progress_callback = std::move(progress_changed); finished_callback = std::move(discovery_finished); } void reportProgress( const PlcSerialConfiguration &candidate, std::size_t current, std::size_t total) { if (progress_callback) { progress_callback({candidate, current, total}); } } void reportFound(const PlcSerialConfiguration &configuration) { discovering = false; if (finished_callback) { finished_callback({true, false, configuration, "found"}); } } bool discovering = false; int start_count = 0; int cancel_count = 0; PlcSerialConfiguration last_preferred; std::function progress_callback; std::function finished_callback; }; void testDiscoveryFindsAndAcceptsConfiguration() { FakePlcDiscoveryGateway gateway; PlcSerialConfiguration initial; initial.portName = "COM1"; int prepare_count = 0; PlcConnectionDialog dialog( initial, gateway, [&prepare_count] { ++prepare_count; }); QPushButton *search_button = dialog.findChild("autoSearchButton"); QProgressBar *progress_bar = dialog.findChild( "discoveryProgressBar"); QLabel *status_label = dialog.findChild("discoveryStatusLabel"); require(search_button != nullptr && progress_bar != nullptr && status_label != nullptr, "PLC dialog must expose automatic discovery controls"); search_button->click(); require(prepare_count == 1 && gateway.start_count == 1 && gateway.discovering, "automatic discovery must release the formal connection and start once"); require(search_button->text() == QStringLiteral("停止搜索") && !progress_bar->isHidden(), "the dialog must show searchable progress and a stop command"); PlcSerialConfiguration found = initial; found.portName = "COM3"; found.serverAddress = 1; found.baudRate = 19200; found.dataBits = 8; found.parity = 0; found.stopBits = 1; gateway.reportProgress(found, 4, 180); require(progress_bar->maximum() == 180 && progress_bar->value() == 4 && status_label->text().contains(QStringLiteral("COM3")) && status_label->text().contains(QStringLiteral("8N1")), "discovery progress must identify the active port and serial frame"); gateway.reportFound(found); QCoreApplication::processEvents(QEventLoop::AllEvents); const PlcSerialConfiguration selected = dialog.configuration(); require(dialog.result() == QDialog::Accepted, "a successful discovery must automatically accept the dialog"); require(selected.portName == found.portName && selected.serverAddress == found.serverAddress && selected.baudRate == found.baudRate && selected.dataBits == found.dataBits && selected.parity == found.parity && selected.stopBits == found.stopBits, "a successful discovery must copy every detected serial parameter"); } void testDiscoveryCanBeCancelled() { FakePlcDiscoveryGateway gateway; PlcSerialConfiguration initial; initial.portName = "COM1"; PlcConnectionDialog dialog(initial, gateway, [] {}); QPushButton *search_button = dialog.findChild("autoSearchButton"); QLabel *status_label = dialog.findChild("discoveryStatusLabel"); search_button->click(); search_button->click(); require(gateway.cancel_count == 1 && !gateway.discovering, "the stop command must cancel the active discovery"); require(search_button->isEnabled() && search_button->text() == QStringLiteral("自动搜索") && status_label->text() == QStringLiteral("已停止自动搜索"), "the dialog must return to an editable state after cancellation"); } void testPortRefreshRemovesUnavailableSelection() { FakePlcDiscoveryGateway gateway; PlcSerialConfiguration initial; initial.portName = "COM_STALE_TEST_999"; PlcConnectionDialog dialog(initial, gateway, [] {}); QComboBox *port_combo = dialog.findChild("serialPortComboBox"); QToolButton *refresh_button = dialog.findChild( "refreshPortsButton"); require(port_combo != nullptr && refresh_button != nullptr, "PLC dialog must expose a serial port refresh command"); require(port_combo->findText(QString::fromLatin1(initial.portName.c_str())) < 0, "opening the dialog must not restore an unavailable saved port"); const QString stale_port = QStringLiteral("COM_STALE_TEST_998"); port_combo->addItem(stale_port); port_combo->setCurrentText(stale_port); refresh_button->click(); require(port_combo->findText(stale_port) < 0 && port_combo->currentText() != stale_port, "refreshing ports must remove an unplugged or unavailable selection"); } } // namespace int main(int argc, char *argv[]) { qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("offscreen")); QApplication application(argc, argv); return TestSupport::runTestSuite("PLC connection dialog tests", { {"testDiscoveryFindsAndAcceptsConfiguration", testDiscoveryFindsAndAcceptsConfiguration}, {"testDiscoveryCanBeCancelled", testDiscoveryCanBeCancelled}, {"testPortRefreshRemovesUnavailableSelection", testPortRefreshRemovesUnavailableSelection}, }); }