mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 05:44:14 +02:00
Scripting: list a folio's conductors by uuid and find their ends
A script could name a conductor only by one of its ends, "the conductor on
terminal N of element X", which fails where two conductors meet at one
terminal and cannot follow a conductor that is re-connected.
qet.conductorUuids(folio) lists the folio's conductor uuids, in the order
qet.conductors() lists them. qet.conductorEnds(folio, uuid) returns that
conductor's two ends as "{element uuid} terminal N" -- the form
conductors() prints and the conductor calls take -- or an empty list if
the folio has no such conductor. The end formatting conductors() already
did is shared rather than copied.
Conductors of older projects have no saved uuid yet, so theirs change
from one load to the next until that is settled (discussion #1103); new
conductors keep theirs.
tst_scriptconductoruuid runs a script through --run on a fixture: every
conductor has a distinct uuid, and its ends match the conductors() line
at the same position; an unknown uuid, a malformed one and a folio that
does not exist give empty lists. It fails with the two ends swapped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -844,6 +844,18 @@ bool QetScriptApi::addConductor(int folioIndex,
|
||||
exists so a script, or a person reading its output, can see what is
|
||||
there before changing it.
|
||||
*/
|
||||
namespace {
|
||||
/// "{element uuid} terminal N", the form conductors() prints an end in and
|
||||
/// the conductor calls take as element uuid + terminal index.
|
||||
QString describeEnd(Terminal *t)
|
||||
{
|
||||
if (!t || !t->parentElement()) return QStringLiteral("?");
|
||||
return QStringLiteral("%1 terminal %2")
|
||||
.arg(t->parentElement()->uuid().toString())
|
||||
.arg(t->parentElement()->terminals().indexOf(t));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
QStringList QetScriptApi::conductors(int folioIndex) const
|
||||
{
|
||||
QStringList list;
|
||||
@@ -851,23 +863,57 @@ QStringList QetScriptApi::conductors(int folioIndex) const
|
||||
const QList<Diagram *> diagrams = m_project->diagrams();
|
||||
if (folioIndex < 0 || folioIndex >= diagrams.count()) return list;
|
||||
|
||||
auto describe = [](Terminal *t) -> QString {
|
||||
if (!t || !t->parentElement()) return QStringLiteral("?");
|
||||
return QStringLiteral("%1 terminal %2")
|
||||
.arg(t->parentElement()->uuid().toString())
|
||||
.arg(t->parentElement()->terminals().indexOf(t));
|
||||
};
|
||||
|
||||
DiagramContent content(diagrams.at(folioIndex), false);
|
||||
const QList<Conductor *> all = content.conductors(DiagramContent::AnyConductor);
|
||||
for (Conductor *c : all)
|
||||
{
|
||||
list << QStringLiteral("%1 -- %2 : num='%3'")
|
||||
.arg(describe(c->terminal1), describe(c->terminal2), c->properties().text);
|
||||
.arg(describeEnd(c->terminal1), describeEnd(c->terminal2), c->properties().text);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::conductorUuids
|
||||
The uuid of every conductor on the folio, in the order conductors()
|
||||
lists them.
|
||||
*/
|
||||
QStringList QetScriptApi::conductorUuids(int folioIndex) const
|
||||
{
|
||||
QStringList list;
|
||||
if (!m_project) return list;
|
||||
const QList<Diagram *> diagrams = m_project->diagrams();
|
||||
if (folioIndex < 0 || folioIndex >= diagrams.count()) return list;
|
||||
|
||||
DiagramContent content(diagrams.at(folioIndex), false);
|
||||
for (Conductor *c : content.conductors(DiagramContent::AnyConductor))
|
||||
list << c->uuid().toString();
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QetScriptApi::conductorEnds
|
||||
The two ends of the conductor carrying @p uuid, each as
|
||||
"{element uuid} terminal N" -- the element uuid and terminal index the
|
||||
conductor calls take -- or an empty list if the folio has no such
|
||||
conductor. A uuid names one conductor even where two meet at a terminal,
|
||||
which an element uuid + terminal index cannot.
|
||||
*/
|
||||
QStringList QetScriptApi::conductorEnds(int folioIndex, const QString &uuid) const
|
||||
{
|
||||
if (!m_project) return {};
|
||||
const QList<Diagram *> diagrams = m_project->diagrams();
|
||||
if (folioIndex < 0 || folioIndex >= diagrams.count()) return {};
|
||||
const QUuid wanted(uuid);
|
||||
if (wanted.isNull()) return {};
|
||||
|
||||
DiagramContent content(diagrams.at(folioIndex), false);
|
||||
for (Conductor *c : content.conductors(DiagramContent::AnyConductor))
|
||||
if (c->uuid() == wanted)
|
||||
return {describeEnd(c->terminal1), describeEnd(c->terminal2)};
|
||||
return {};
|
||||
}
|
||||
|
||||
QString QetScriptApi::conductorProperty(int folioIndex, const QString &elementUuid,
|
||||
int terminalIndex, const QString &property) const
|
||||
{
|
||||
|
||||
@@ -396,6 +396,8 @@ class QetScriptApi : public QObject
|
||||
|
||||
// -- conductor properties, applied to the whole potential --
|
||||
Q_INVOKABLE QStringList conductors(int folioIndex) const;
|
||||
Q_INVOKABLE QStringList conductorUuids(int folioIndex) const;
|
||||
Q_INVOKABLE QStringList conductorEnds(int folioIndex, const QString &uuid) const;
|
||||
Q_INVOKABLE QString conductorProperty(int folioIndex, const QString &elementUuid,
|
||||
int terminalIndex, const QString &property) const;
|
||||
Q_INVOKABLE bool setConductorProperty(int folioIndex, const QString &elementUuid,
|
||||
|
||||
@@ -328,3 +328,15 @@ target_include_directories(tst_conductorselfretrace PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_conductorselfretrace PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_conductorselfretrace PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# qet.conductorUuids() / qet.conductorEnds(): a script lists a folio's
|
||||
# conductors by uuid and finds each one's two ends. Runs a script through
|
||||
# the real binary's --run on fixtures/qet_bug_repro_resaved.qet.
|
||||
add_executable(
|
||||
tst_scriptconductoruuid
|
||||
tst_scriptconductoruuid.cpp)
|
||||
add_test(NAME tst_scriptconductoruuid COMMAND tst_scriptconductoruuid)
|
||||
add_dependencies(tst_scriptconductoruuid qelectrotech)
|
||||
target_link_libraries(tst_scriptconductoruuid PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_scriptconductoruuid PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTemporaryDir>
|
||||
#include <QUuid>
|
||||
|
||||
// qet.conductorUuids(folio) and qet.conductorEnds(folio, uuid): a script can
|
||||
// list a folio's conductors by uuid and find where each one runs, in the
|
||||
// "{element} terminal N" form conductors() prints and the conductor calls
|
||||
// take. Runs a script through the real binary's --run.
|
||||
class tst_scriptconductoruuid : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
// Run @p script on the fixture in a sandbox of its own and return the
|
||||
// JSON object it logged.
|
||||
QJsonObject run(const QString &script)
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
QDir().mkpath(home);
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) return {};
|
||||
f.write(script.toUtf8());
|
||||
f.close();
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), path,
|
||||
QFINDTESTDATA("fixtures/qet_bug_repro_resaved.qet")});
|
||||
if (!proc.waitForFinished(60000)) return {};
|
||||
const QString out = QString::fromUtf8(proc.readAllStandardOutput()
|
||||
+ proc.readAllStandardError());
|
||||
const QString mark = QStringLiteral("PROBE ");
|
||||
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
||||
const int i = line.indexOf(mark);
|
||||
if (i >= 0)
|
||||
return QJsonDocument::fromJson(line.mid(i + mark.size()).toUtf8()).object();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
}
|
||||
|
||||
void uuidsAndEndsMatchConductors()
|
||||
{
|
||||
const QJsonObject r = run(QStringLiteral(
|
||||
"var uuids = qet.conductorUuids(0);\n"
|
||||
"var ends = uuids.map(function (u) { return qet.conductorEnds(0, u); });\n"
|
||||
"qet.log('PROBE ' + JSON.stringify({lines: qet.conductors(0), uuids: uuids, ends: ends,\n"
|
||||
" unknown: qet.conductorEnds(0, '{00000000-0000-0000-0000-000000000001}'),\n"
|
||||
" junk: qet.conductorEnds(0, 'not a uuid'),\n"
|
||||
" badFolio: qet.conductorUuids(99)}));\n"));
|
||||
QVERIFY2(!r.isEmpty(), "the script logged nothing");
|
||||
|
||||
const QJsonArray lines = r.value(QStringLiteral("lines")).toArray();
|
||||
const QJsonArray uuids = r.value(QStringLiteral("uuids")).toArray();
|
||||
const QJsonArray ends = r.value(QStringLiteral("ends")).toArray();
|
||||
QCOMPARE(lines.size(), 7); // the fixture's conductors
|
||||
QCOMPARE(uuids.size(), lines.size());
|
||||
QSet<QString> distinct;
|
||||
for (int i = 0; i < uuids.size(); ++i) {
|
||||
const QString u = uuids.at(i).toString();
|
||||
QVERIFY2(!QUuid(u).isNull(), qPrintable(u));
|
||||
distinct.insert(u);
|
||||
// same order as conductors(), and the same two ends it prints
|
||||
const QJsonArray e = ends.at(i).toArray();
|
||||
QCOMPARE(e.size(), 2);
|
||||
const QString expected = e.at(0).toString() + QStringLiteral(" -- ")
|
||||
+ e.at(1).toString() + QStringLiteral(" : ");
|
||||
QVERIFY2(lines.at(i).toString().startsWith(expected),
|
||||
qPrintable(lines.at(i).toString() + QStringLiteral(" | ") + expected));
|
||||
}
|
||||
QCOMPARE(distinct.size(), uuids.size());
|
||||
|
||||
QVERIFY(r.value(QStringLiteral("unknown")).toArray().isEmpty());
|
||||
QVERIFY(r.value(QStringLiteral("junk")).toArray().isEmpty());
|
||||
QVERIFY(r.value(QStringLiteral("badFolio")).toArray().isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_scriptconductoruuid)
|
||||
|
||||
#include "tst_scriptconductoruuid.moc"
|
||||
Reference in New Issue
Block a user