From e3a1a67c6637b35a68aa99db2529e9868714664f Mon Sep 17 00:00:00 2001 From: Beat Hangartner Date: Wed, 7 Oct 2026 21:08:25 +0200 Subject: [PATCH] Recognise seqhf_1 when loading sequential numbers saved the old way Before the element, an element's or conductor's sequential numbers were saved as the attributes sequ_1, sequf_1, seqt_1, seqtf_1, seqh_1 and seqhf_1. The readers check for one of them to take the old route, and all three lists had the same slip: sequf_1 twice, seqhf_1 never (Element::fromXml, Conductor::fromXml, and readSequence() in the project database). A file whose only old sequence was the hundred-folio one took the new route, found no , and lost it; the database built its labels from an empty sequence instead of refusing the fast path as it does for the other five attributes. Name seqhf_1 in the three lists. No file-format change: nothing is written differently, and a file with any of the other five attributes loads exactly as before. Tests: tst_legacysequentialattributes runs the binary's --resave on a fixture whose element and conductor carry only seqhf_1 (and a second pair carrying sequ_1 as a control) and reads the written back. The two seqhf_1 cases fail on master. Co-Authored-By: Claude Fable 5.1 Signed-off-by: Beat Hangartner --- sources/dataBase/projectdatabase.cpp | 2 +- sources/qetgraphicsitem/conductor.cpp | 2 +- sources/qetgraphicsitem/element.cpp | 2 +- tests/qttest/CMakeLists.txt | 13 + .../fixtures/legacy_sequential_attributes.qet | 351 ++++++++++++++++++ .../qttest/tst_legacysequentialattributes.cpp | 126 +++++++ 6 files changed, 493 insertions(+), 3 deletions(-) create mode 100644 tests/qttest/fixtures/legacy_sequential_attributes.qet create mode 100644 tests/qttest/tst_legacysequentialattributes.cpp diff --git a/sources/dataBase/projectdatabase.cpp b/sources/dataBase/projectdatabase.cpp index e93d53061..6bbf6598f 100644 --- a/sources/dataBase/projectdatabase.cpp +++ b/sources/dataBase/projectdatabase.cpp @@ -236,7 +236,7 @@ struct DocumentElement //the attributes files written before carry. bool readSequence(const QDomElement &item, autonum::sequentialNumbers *sequence) { - for (const char *name : {"sequ_1", "sequf_1", "seqt_1", "seqtf_1", "seqh_1"}) { + for (const char *name : {"sequ_1", "sequf_1", "seqt_1", "seqtf_1", "seqh_1", "seqhf_1"}) { if (item.hasAttribute(QLatin1String(name))) { return false; } diff --git a/sources/qetgraphicsitem/conductor.cpp b/sources/qetgraphicsitem/conductor.cpp index c51c4f8ce..a1cc60205 100644 --- a/sources/qetgraphicsitem/conductor.cpp +++ b/sources/qetgraphicsitem/conductor.cpp @@ -1142,7 +1142,7 @@ bool Conductor::fromXml(QDomElement &dom_element) pr.fromXml(dom_element); //Load Sequential Values - if (dom_element.hasAttribute("sequ_1") || dom_element.hasAttribute("sequf_1") || dom_element.hasAttribute("seqt_1") || dom_element.hasAttribute("seqtf_1") || dom_element.hasAttribute("seqh_1") || dom_element.hasAttribute("sequf_1")) + if (dom_element.hasAttribute("sequ_1") || dom_element.hasAttribute("sequf_1") || dom_element.hasAttribute("seqt_1") || dom_element.hasAttribute("seqtf_1") || dom_element.hasAttribute("seqh_1") || dom_element.hasAttribute("seqhf_1")) ConductorXmlRetroCompatibility::loadSequential(dom_element, this); else m_autoNum_seq.fromXml(dom_element.firstChildElement("sequentialNumbers")); diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 139ccb1ae..80804145e 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -1055,7 +1055,7 @@ bool Element::fromXml(QDomElement &e, || e.hasAttribute(QStringLiteral("seqt_1")) || e.hasAttribute(QStringLiteral("seqtf_1")) || e.hasAttribute(QStringLiteral("seqh_1")) - || e.hasAttribute(QStringLiteral("sequf_1"))) + || e.hasAttribute(QStringLiteral("seqhf_1"))) ElementXmlRetroCompatibility::loadSequential(e, this); else m_autoNum_seq.fromXml(e.firstChildElement(QStringLiteral("sequentialNumbers"))); diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index bd8a82c01..c6fb19dd0 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -922,6 +922,19 @@ target_compile_definitions(tst_terminaluuids PRIVATE "QET_TEST_BINARY_PATH=\"$\"" "QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"") +# Sequential numbers saved the old way, as sequ_1/.../seqhf_1 attributes, +# are all read: the real binary's --resave on +# fixtures/legacy_sequential_attributes.qet, whose element and conductor +# carry only seqhf_1. +add_executable( + tst_legacysequentialattributes + tst_legacysequentialattributes.cpp) +add_test(NAME tst_legacysequentialattributes COMMAND tst_legacysequentialattributes) +add_dependencies(tst_legacysequentialattributes qelectrotech) +target_link_libraries(tst_legacysequentialattributes PRIVATE Qt::Test Qt::Xml) +target_compile_definitions(tst_legacysequentialattributes PRIVATE + "QET_TEST_BINARY_PATH=\"$\"") + # ConductorProperties::applyChanges() -- the Selection properties panel # writes only the fields the user edited. add_executable( diff --git a/tests/qttest/fixtures/legacy_sequential_attributes.qet b/tests/qttest/fixtures/legacy_sequential_attributes.qet new file mode 100644 index 000000000..966f75b90 --- /dev/null +++ b/tests/qttest/fixtures/legacy_sequential_attributes.qet @@ -0,0 +1,351 @@ + + + 10/4/26 + 04-10-2026 + 2026-10-04 + skeleton + 10:28 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 14 + + + 11 + + + + label + + + + + + + + + + + + + 14 + + + 11 + + + + label + + + + + + + + + + + + + 14 + + + 11 + + + + label + + + + + + + + + + + + + 14 + + + 11 + + + + label + + + + + + + + + + + + + + + + + + + Elements importats + Zavedené prvky + Importerede elementer + Importierte Elemente + Εισηγμένα στοιχεία + Imported elements + Elementos importados + Éléments importés + Uvezeni elementi + Elementi importati + インバートされた要素 + 전기 심볼 요소 + 전기 심볼 요소 + Elementen geïmporteerd + Elementen geïmporteerd + Elementy importowane + Elementos importados + Elementos importados + Elemente importate + Импортированные элементы + Uvoženi elementi + Importerade element + İthal öğeler + Імпортовані елементи + 导入元件 + + + + كهربائي + Elèctric + Elektrotechnika + Elektrisk + Elektrik + Ηλεκτρικά + Electric + Eléctrica + Sähkö + Electrique + Električno + Elektromos + Elettrica + 電気 + 전기 + Цахилгаан + Elektrisk + Elektrotechniek + Elektrisch + Elektrotechnika + Elétrico + Elétrica + Electric + Электротехника + Elektrické + Elektrika + Електрика + El + Elektrik + Електрика + 10_电气 + 电气工程 + + + + متعدد الأقطاب + Multifilar + Vícežilový + Flere ledere + Allpolig + Πολυγραμμικό + All-pole + Multifilar + Yleisnapa + Multifilaire + Univerzalni + Többvonalas szimbólumok + Multifilare + 複線 + 다-극 + Бүх туйл + Alle poler + Veel polig + All-pole + Schematy wieloliniowe + Multipolar + Multifilar + Bipolar + Многополюсные + Všepólové + Vsi poli + Свe-полни + Allpolig + Çok kutuplu + Багатополюсні + 10_全极 + + + + المرحّلات والموصّلات وجهات الاتصال + Relés, contactors i contactes + Relé, stykače a kontakty + Relæer, kontaktorer og kontakter + Relais, Schütze und Kontakte + Ηλεκτρονόμοι και επαφές + Relays, contactors and contacts + Relevadores, contactores y contactos + Releet, kontaktorit ja koskettimet + Relais, contacteurs et contacts + Releji, kontaktori i kontakti + Relék, kontaktorok és érintkezők + Relè, contattori e contatti + 継電器、接触器、接点 + 릴레이, 커넥터및 접점 + Реле, контактор болон контакт + Reléer, kontaktorere og kontakter + Relais, contactoren en contacten + Relais, contacteurs en contacten + Przekaźniki i styczniki + Relés, contatores e contactos + Relés, Contatores e contatos + Relee, contactoare și contacte + Реле, пускатели и контакты + Relé, stykače a kontakty + Releji, kontaktorji in kontakti + Релеји, контактори и контакти + Reläer, kontaktorer och kontakter + Röleler, kontaktörler ve kontaklar + Реле, контактори та контакти + 继电器、接触器与触点 + + + + جهات الاتصال + Contactes + Kontakty + Kontaktpersoner + Kontakte + Επαφές + Contacts + Contactos + Kosketimet + Contacts + Kontakti + Kapcsolatok + Contatti + 連絡先 + 접점 + Харилцагчид + Kontakter + Contacten + Contacten + Łączność + Contatos + Contatos + Contacte + Контакты + Kontakty + Stiki + Контакти + Kontakter + Kişiler + Контакти + 触点 + + + + + + عدّاد مفتوح عادة + Taulell normalment obert + Kontakt čítače (NO) + Normalt åben tæller + Öffner Zähler betätigt + Ανοικτή επαφή μετρητή + Normally open counter + Contador NA + Normaalisti avoin laskuri + Actionné par comptage NO + Brojač normalno otvoren + Alaphelyzetben nyitott számláló + Contatto azionato da un contatore (NO) + 常開カウンター + 평상시 열림 카운터 + Ихэвчлэн нээлттэй тоолуур + Normalt åpen teller + Teller contact (NO) + Normaal open teller + Zestyk zwierny licznika + Contador normalmente aberto + Contato NA acionado por contador + Contor normal deschis + Счётчик с нормально открытым контактом + Počítadlo s kontaktom NO + Števec normalno odprt + Бројач нормално отворен + Räknare med slutande kontakt + Normalde açık sayaç + Лічильник нормально розімкнений + 计数驱动常开触点 + + Author: The QElectroTech team +License: see http://qelectrotech.org/wiki/doc/elements_license + + + + + 14 + + + 11 + + + + label + + + + + + + + + + + + + + + + diff --git a/tests/qttest/tst_legacysequentialattributes.cpp b/tests/qttest/tst_legacysequentialattributes.cpp new file mode 100644 index 000000000..905793f7d --- /dev/null +++ b/tests/qttest/tst_legacysequentialattributes.cpp @@ -0,0 +1,126 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include +#include +#include +#include + +/** + Projects saved before the element keep an element's + or conductor's sequential numbers as attributes: sequ_1, sequf_1, seqt_1, + seqtf_1, seqh_1 and seqhf_1. The loaders look for one of them to take + the old route; the list named sequf_1 twice and seqhf_1 never, so a + file whose only sequence was the hundred-folio one lost it. Runs the + real binary's --resave on fixtures/legacy_sequential_attributes.qet + and reads the it writes. +*/ +class tst_legacysequentialattributes : public QObject +{ + Q_OBJECT + + QTemporaryDir m_dir; + QDomDocument m_resaved; + + static bool runQet(const QStringList &arguments) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); + QProcess proc; + proc.setProcessEnvironment(env); + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), arguments); + return proc.waitForFinished(60000) + && proc.exitStatus() == QProcess::NormalExit + && proc.exitCode() == 0; + } + + /// The first @p tag element of the saved project with this uuid. + QDomElement item(const QString &tag, const QString &uuid) const + { + const QDomNodeList list = m_resaved.elementsByTagName(tag); + for (int i = 0; i < list.size(); ++i) { + const QDomElement e = list.at(i).toElement(); + if (e.attribute(QStringLiteral("uuid")) == uuid) return e; + } + return QDomElement(); + } + + /// The text of <@p part> under @p e. + static QString sequence(const QDomElement &e, const QString &part) + { + return e.firstChildElement(QStringLiteral("sequentialNumbers")) + .firstChildElement(part).text(); + } + +private slots: + void initTestCase() + { + QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found"); + QVERIFY(m_dir.isValid()); + const QString fixture = QFINDTESTDATA("fixtures/legacy_sequential_attributes.qet"); + QVERIFY2(!fixture.isEmpty(), "fixture project not found"); + const QString out = m_dir.filePath(QStringLiteral("resaved.qet")); + QVERIFY2(runQet({QStringLiteral("--resave"), fixture, out}), "--resave failed"); + QFile file(out); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(m_resaved.setContent(&file)); + } + + // The element and the conductor whose only old attribute is seqhf_1. + void hundredFolioSequenceIsKept_data() + { + QTest::addColumn("tag"); + QTest::addColumn("uuid"); + QTest::addColumn("value"); + QTest::newRow("element") << "element" << "{6c58a5a1-aa4e-40c4-9e9d-a76524f76e52}" << "5"; + QTest::newRow("conductor") << "conductor" << "{f5162c59-0c59-4e94-b6a6-4cf259a52d91}" << "7"; + } + + void hundredFolioSequenceIsKept() + { + QFETCH(QString, tag); + QFETCH(QString, uuid); + QFETCH(QString, value); + const QDomElement e = item(tag, uuid); + QVERIFY2(!e.isNull(), "item not found in the resaved project"); + QVERIFY2(!e.hasAttribute(QStringLiteral("seqhf_1")), "the old attribute is not written back"); + QCOMPARE(sequence(e, QStringLiteral("hundredFolio")), value); + } + + // The unit sequence, whose attribute was already in the list, as before. + void unitSequenceIsKept_data() + { + QTest::addColumn("tag"); + QTest::addColumn("uuid"); + QTest::addColumn("value"); + QTest::newRow("element") << "element" << "{29b32d7b-5ec3-42c8-afda-788e9614c0fd}" << "3"; + QTest::newRow("conductor") << "conductor" << "{89cacce1-b12a-4ec3-aadf-d3f8feb7494d}" << "4"; + } + + void unitSequenceIsKept() + { + QFETCH(QString, tag); + QFETCH(QString, uuid); + QFETCH(QString, value); + const QDomElement e = item(tag, uuid); + QVERIFY2(!e.isNull(), "item not found in the resaved project"); + QCOMPARE(sequence(e, QStringLiteral("unit")), value); + } +}; + +QTEST_MAIN(tst_legacysequentialattributes) +#include "tst_legacysequentialattributes.moc"