mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-29 22:24:13 +02:00
a0d5c4fa9c
The project database -- behind parts lists, summary tables and the wiring list -- was filled by walking the built folios: every element and conductor object of every scene. Filling it from the document the project was just read from instead is what the database needs before a project could be opened without building every folio (qelectrotech-docker DB-FROM-XML-SCOPE.md). updateDB(document), called by readProjectXml(), fills diagram, diagram_info, element, element_info, terminal and conductor from the document, through the code the folios use: a BorderTitleBlock read from each folio's XML gives the title-block values and each element's grid cell, the embedded collection gives each definition, and the binders are shared with the live path. Shapes, texts and pictures still come from the folios (their boxes need fonts and pens). It falls back to the folios, saying why in the log, when the document does not carry what that needs: an item without a saved uuid (older files -- the folios derive them on load), a conductor naming its ends the older way, a %autonum folio number, a terminal showing its master's label, a missing or unbuildable definition. A symbol label computed from a formula is the one saved in the file, which QElectroTech writes as it computes it on every save. QET_DATABASE_FROM_FOLIOS=1 forces the folio path. tst_databasefromdocument saves every example once, then fills both ways and requires identical tables (24/24, filled from the document each time), and checks that an older file falls back with its reason. Red when the document path is made to write a wrong grid cell. Database phase of loading unchanged: industrial.qet 0.146 s vs 0.160 s, Polonez 0.040 s vs 0.038 s (median of 5). QETProject::projectWideProperties() is split out of updateDiagramsFolioData() so both fills use the same title-block context. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
132 lines
4.9 KiB
C++
132 lines
4.9 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
#include <QtTest>
|
|
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QProcess>
|
|
#include <QProcessEnvironment>
|
|
#include <QTemporaryDir>
|
|
|
|
// A project's database, filled from the file as it is read, holds exactly
|
|
// what the same database filled from the built folios holds. Every example
|
|
// is saved once (so that it carries the uuids a current QElectroTech
|
|
// writes), then opened twice through the real binary's --run: once as is,
|
|
// once with QET_DATABASE_FROM_FOLIOS=1, and the six tables compared.
|
|
class tst_databasefromdocument : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
QTemporaryDir m_dir;
|
|
int m_run = 0;
|
|
|
|
QProcessEnvironment env()
|
|
{
|
|
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(m_run++));
|
|
QDir().mkpath(home);
|
|
QProcessEnvironment e = QProcessEnvironment::systemEnvironment();
|
|
e.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
|
e.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
|
e.insert(QStringLiteral("HOME"), home);
|
|
e.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
|
e.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
|
e.insert(QStringLiteral("TMPDIR"), m_dir.path());
|
|
e.remove(QStringLiteral("QET_DATABASE_FROM_FOLIOS"));
|
|
return e;
|
|
}
|
|
|
|
QString run(const QStringList &args, bool from_folios = false)
|
|
{
|
|
QProcessEnvironment e = env();
|
|
if (from_folios)
|
|
e.insert(QStringLiteral("QET_DATABASE_FROM_FOLIOS"), QStringLiteral("1"));
|
|
QProcess proc;
|
|
proc.setProcessEnvironment(e);
|
|
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), args);
|
|
if (!proc.waitForFinished(180000)) return {};
|
|
return QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
|
}
|
|
|
|
// The six tables, each as a sorted list of its rows, and which way the
|
|
// database was filled.
|
|
QJsonObject dump(const QString &project, bool from_folios, QString *how)
|
|
{
|
|
const QString out = run({QStringLiteral("--run"), m_dir.filePath(QStringLiteral("dump.js")),
|
|
project}, from_folios);
|
|
QJsonObject tables;
|
|
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
|
if (line.contains(QStringLiteral("Project database filled")))
|
|
*how = line.mid(line.indexOf(QStringLiteral("Project database filled")));
|
|
const int i = line.indexOf(QStringLiteral("DUMP "));
|
|
if (i >= 0) {
|
|
const QJsonObject raw = QJsonDocument::fromJson(line.mid(i + 5).toUtf8()).object();
|
|
for (auto it = raw.begin(); it != raw.end(); ++it) {
|
|
QStringList rows;
|
|
for (const QJsonValue &row : it.value().toArray())
|
|
rows << QString::fromUtf8(QJsonDocument(row.toObject()).toJson(QJsonDocument::Compact));
|
|
rows.sort();
|
|
tables.insert(it.key(), QJsonArray::fromStringList(rows));
|
|
}
|
|
}
|
|
}
|
|
return tables;
|
|
}
|
|
|
|
private slots:
|
|
void initTestCase()
|
|
{
|
|
QVERIFY(m_dir.isValid());
|
|
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
|
QFile js(m_dir.filePath(QStringLiteral("dump.js")));
|
|
QVERIFY(js.open(QIODevice::WriteOnly));
|
|
js.write("var out = {};\n"
|
|
"['diagram', 'diagram_info', 'element', 'element_info', 'terminal', 'conductor']"
|
|
".forEach(function (t) { out[t] = qet.query('SELECT * FROM ' + t); });\n"
|
|
"qet.log('DUMP ' + JSON.stringify(out));\n");
|
|
}
|
|
|
|
void sameTablesBothWays_data()
|
|
{
|
|
QTest::addColumn<QString>("project");
|
|
const QDir examples(QStringLiteral(QET_EXAMPLES_DIR));
|
|
for (const QString &f : examples.entryList({QStringLiteral("*.qet")}, QDir::Files, QDir::Name))
|
|
QTest::newRow(f.toUtf8().constData()) << examples.filePath(f);
|
|
}
|
|
|
|
void sameTablesBothWays()
|
|
{
|
|
QFETCH(QString, project);
|
|
const QString saved = m_dir.filePath(QStringLiteral("saved%1.qet").arg(m_run));
|
|
run({QStringLiteral("--resave"), project, saved});
|
|
QVERIFY2(QFile::exists(saved), "--resave failed");
|
|
|
|
QString how_document, how_folios;
|
|
const QJsonObject document = dump(saved, false, &how_document);
|
|
const QJsonObject folios = dump(saved, true, &how_folios);
|
|
QVERIFY2(how_document == QLatin1String("Project database filled from the document"),
|
|
qPrintable(how_document));
|
|
QVERIFY2(how_folios.contains(QStringLiteral("QET_DATABASE_FROM_FOLIOS")), qPrintable(how_folios));
|
|
QCOMPARE(document.keys().size(), 6);
|
|
for (const QString &table : folios.keys()) {
|
|
const QJsonArray a = document.value(table).toArray(), b = folios.value(table).toArray();
|
|
QVERIFY2(a == b, qPrintable(QStringLiteral("%1: %2 rows from the document, %3 from the folios")
|
|
.arg(table).arg(a.size()).arg(b.size())));
|
|
}
|
|
}
|
|
|
|
// A file whose items carry no saved uuid is filled from the folios,
|
|
// which work those uuids out as they are built, and says why.
|
|
void olderFileFallsBack()
|
|
{
|
|
QString how;
|
|
dump(QStringLiteral(QET_EXAMPLES_DIR "/tremie_vibrante.qet"), false, &how);
|
|
QCOMPARE(how, QStringLiteral("Project database filled from the folios: a folio has no saved uuid"));
|
|
}
|
|
};
|
|
|
|
QTEST_APPLESS_MAIN(tst_databasefromdocument)
|
|
|
|
#include "tst_databasefromdocument.moc"
|