Merge pull request #1521 from ispyisail/fix/joined-wire-uuid

Fix a deleted symbol's joining wire getting a random uuid on every run
This commit is contained in:
ispyisail
2026-10-18 21:54:26 +13:00
committed by GitHub
3 changed files with 132 additions and 0 deletions
@@ -259,6 +259,22 @@ void DeleteQGraphicsItemCommand::setPotentialsOfRemovedElements()
qInfo() << "m_connected_terminals" << m_connected_terminals;
Conductor *new_cond = new Conductor(new_pair.first, new_pair.second);
new_cond->setProperties(properties);
//The wire that joins the two left by the removed
//symbol gets its uuid from its two ends, as a wire
//saved without one does on load (Diagram::fromXml()):
//the same deletion then saves the same file every
//time, not a random uuid each run (F115)
if (QETProject *wire_project = new_pair.second->diagram()
? new_pair.second->diagram()->project() : nullptr) {
auto end = [](const Terminal *t) {
return t->parentElement()->uuid().toString()
+ QLatin1Char('/') + t->stableUuid().toString();
};
QStringList ends{end(new_pair.first), end(new_pair.second)};
ends.sort();
new_cond->setUuid(wire_project->derivedItemUuid(
QStringLiteral("conductor"), ends.join(QLatin1Char('\n'))));
}
new AddGraphicsObjectCommand(new_cond, new_pair.second->diagram(), QPointF(), this);
}
}
+13
View File
@@ -901,6 +901,19 @@ if(QET_HAS_SCRIPTING)
target_compile_definitions(tst_savedstamps PRIVATE
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
# Deleting a symbol whose terminal held several wires joins them with a
# new wire; its uuid comes from its two ends, so the same deletion saves
# the same file every time (F115): examples/affuteuse_250h.qet.
add_executable(
tst_joinedwireuuid
tst_joinedwireuuid.cpp)
add_test(NAME tst_joinedwireuuid COMMAND tst_joinedwireuuid)
add_dependencies(tst_joinedwireuuid qelectrotech)
target_link_libraries(tst_joinedwireuuid PRIVATE Qt::Test)
target_compile_definitions(tst_joinedwireuuid PRIVATE
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
# qet.terminalPosition() and qet.conductorPath(), checked against each
# other on a fixture's conductors.
add_executable(
+103
View File
@@ -0,0 +1,103 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <QtTest>
#include <QProcess>
#include <QRegularExpression>
#include <QTemporaryDir>
/**
Deleting a symbol whose terminal held several wires joins the wires
left behind with a new one. That wire got a random uuid, so the same
deletion saved a different file on every run (F115). Its uuid now
comes from its two ends, as a wire saved without a uuid gets on load.
Runs the real binary's --run on examples/affuteuse_250h.qet (wires
saved without uuids): the coil KA1 on folio 3 deleted, twice, in two
separate runs. Both saves must be identical, and no wire may carry a
random (version 4) uuid.
*/
class tst_joinedwireuuid : public QObject
{
Q_OBJECT
QTemporaryDir m_dir;
QByteArray deleteAndSave(const QString &name)
{
const QString out = m_dir.filePath(name + QStringLiteral(".qet"));
const QString js = m_dir.filePath(name + QStringLiteral(".js"));
QFile script(js);
if (!script.open(QIODevice::WriteOnly)) return {};
script.write(QStringLiteral(
"qet.log('DELETED ' + qet.deleteElement(3, '{d8ba5ae4-c76f-4936-bb05-f719eec2be81}'));\n"
"qet.save('%1');\n").arg(out).toUtf8());
script.close();
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
//Its own settings and single-instance socket: another test's
//QElectroTech running at the same time would otherwise be
//handed this run (SingleApplication)
const QString home = m_dir.filePath(QStringLiteral("home"));
QDir().mkpath(home);
env.insert(QStringLiteral("HOME"), home);
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
env.insert(QStringLiteral("TMPDIR"), m_dir.path());
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
QProcess proc;
proc.setProcessEnvironment(env);
proc.setProcessChannelMode(QProcess::MergedChannels);
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
{QStringLiteral("--run"), js,
QStringLiteral(QET_EXAMPLES_DIR "/affuteuse_250h.qet")});
if (!proc.waitForFinished(180000) || proc.exitCode() != 0
|| !proc.readAll().contains("DELETED true"))
return {};
QFile saved(out);
return saved.open(QIODevice::ReadOnly) ? saved.readAll() : QByteArray();
}
private slots:
void initTestCase()
{
QVERIFY2(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)), "qelectrotech binary not found");
QVERIFY(m_dir.isValid());
}
void sameDeletionSavesTheSameFile()
{
const QByteArray first = deleteAndSave(QStringLiteral("first"));
const QByteArray second = deleteAndSave(QStringLiteral("second"));
QVERIFY2(!first.isEmpty() && !second.isEmpty(), "the coil was not deleted and saved");
//No wire with a random uuid: every one is derived
const QRegularExpression wire(QStringLiteral(
"<conductor [^>]*\\buuid=\"\\{[0-9a-f]{8}-[0-9a-f]{4}-([0-9a-f])"));
int wires = 0, random = 0;
for (auto it = wire.globalMatch(QString::fromUtf8(first)); it.hasNext(); ++wires)
if (it.next().captured(1) == QLatin1String("4")) ++random;
QVERIFY(wires > 200);
QCOMPARE(random, 0);
QVERIFY(first == second);
}
};
QTEST_GUILESS_MAIN(tst_joinedwireuuid)
#include "tst_joinedwireuuid.moc"