Change a wire's properties by uuid without building its folios (stage D5)

Scripts get conductorPropertyByUuid() and setConductorPropertyByUuid(),
the wire named by its uuid (conductorUuids()) rather than by a symbol's
terminal. A change goes to every wire at the wire's potential, as
setConductorProperty() does. When those wires are all on folios not
built, none is numbered by a formula, and what changes in each is what a
.qetz database holds in its own columns (number, function, voltage,
colour and section of the wire, cable, bus), it goes through the project
database: the store and the conductor rows, undoable by uuid. The
potential is followed in the database (projectDataBase::potentialWires():
through wire ends, folio reports and terminal blocks, as
Conductor::relatedPotentialConductors() follows it; a terminal block that
isolates its terminals answers nothing). Building a wire takes its change
(Conductor::fromXml()); a .qetz save writes it in the database, the folio
file copied; a folio written from its XML gets it there; a .qet builds.
Anything else builds the folio, as before.

tst_lazyfolios: every wire's potential from the database = as followed,
1 342 of 1 342 (industrial, eager and from a .qetz); a cable set by uuid
on a 14-wire potential builds no folio, reaches every wire, saves as when
built, undoes, shows once built (fails when building ignores the change,
and when the .qetz writer does). The test's run limit is raised (500 s ->
1 500 s, ctest 1 800 s): under load it ran past 500 s. ctest 96/96 both
ways.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-12 23:26:39 +13:00
parent b8390c853c
commit 798010cc83
10 changed files with 507 additions and 2 deletions
+17
View File
@@ -818,6 +818,23 @@ QByteArray QetContainerDb::extract(QDomElement project, QString *error, const QB
? QVariant(i.attribute(QStringLiteral("name"))) : QVariant(),
value, attributesJson(i, QStringLiteral("name"), true)});
}
}
//Its wires whose properties changed since: their columns
for (const QDomElement &edited : children(d, QStringLiteral("qetz-edited-conductor"))) {
QStringList set;
QVariantList values;
for (const QString &column : ConductorData) {
if (column == QLatin1String("uuid") || column.startsWith(QLatin1String("element"))
|| column.startsWith(QLatin1String("terminal")) || column == QLatin1String("freezeLabel")
|| !edited.hasAttribute(column))
continue;
set << QStringLiteral("\"%1\" = ?").arg(column);
values << edited.attribute(column);
}
values << id << edited.attribute(QStringLiteral("uuid"));
Statement update(db, QStringLiteral("UPDATE conductor SET %1 WHERE folio_id = ? AND uuid = ?")
.arg(set.join(QStringLiteral(", "))));
ok = ok && !set.isEmpty() && update.run(values) && sqlite3_changes(db.m_db) == 1;
}
if (!ok) {
setError(error, QStringLiteral("folio %1 kept as it was is not in its database").arg(id));
+173
View File
@@ -1317,6 +1317,77 @@ QString projectDataBase::terminalName(const QUuid &terminal, const QUuid &elemen
return query.exec() && query.next() ? query.value(0).toString() : QString();
}
/**
@brief projectDataBase::potentialWires
@return the wires at the potential of @p wire, @p wire included, read
from the conductor, element and link tables as they are (see
elementTypesOnFolios()), followed as Conductor::relatedPotentialConductors()
follows them over every folio: through a wire's ends, from a folio
report to the terminals of the first symbol it is linked to, and from a
terminal block's terminal to its others. Nothing when a terminal block
on the way isolates its terminals (their groups are in its definition,
not in the database) or @p wire has no row.
*/
std::optional<QSet<QUuid>> projectDataBase::potentialWires(const QUuid &wire) const
{
using Node = std::pair<QString, QString>; //element uuid, terminal uuid
QHash<QString, std::pair<Node, Node>> ends;
QHash<Node, QStringList> wires_at;
QHash<QString, QList<Node>> nodes_of;
{
QSqlQuery query(m_data_base);
query.exec(QStringLiteral("SELECT uuid, terminal1_element_uuid, terminal1_uuid, "
"terminal2_element_uuid, terminal2_uuid FROM conductor"));
while (query.next()) {
const Node a{query.value(1).toString(), query.value(2).toString()};
const Node b{query.value(3).toString(), query.value(4).toString()};
const QString uuid = query.value(0).toString();
ends.insert(uuid, {a, b});
for (const Node &n : {a, b}) {
wires_at[n] << uuid;
if (!nodes_of.value(n.first).contains(n)) nodes_of[n.first] << n;
}
}
}
const auto start = ends.constFind(wire.toString());
if (start == ends.constEnd()) {
return std::nullopt;
}
QHash<QString, ElementData::Type> kinds;
{
QSqlQuery query(m_data_base);
query.exec(QStringLiteral("SELECT uuid, type FROM element"));
while (query.next())
kinds.insert(query.value(0).toString(), ElementData::typeFromString(query.value(1).toString()));
}
QSet<QUuid> wires;
QSet<Node> seen;
QList<Node> todo{start->first, start->second};
while (!todo.isEmpty()) {
const Node node = todo.takeLast();
if (seen.contains(node)) continue;
seen.insert(node);
for (const QString &w : wires_at.value(node)) {
if (wires.contains(QUuid(w))) continue;
wires.insert(QUuid(w));
const auto e = ends.value(w);
todo << e.first << e.second;
}
const ElementData::Type kind = kinds.value(node.first, ElementData::Simple);
if (kind & ElementData::AllReport) {
const QList<QUuid> partners = linkedSymbols(QUuid(node.first));
if (!partners.isEmpty())
todo << nodes_of.value(partners.first().toString());
} else if (kind & ElementData::Terminal) {
if (QET::infoFlagIsTrue(m_element_information.value(QUuid(node.first))
.value(QStringLiteral("potential_isolating")).toString()))
return std::nullopt;
todo << nodes_of.value(node.first);
}
}
return wires;
}
/**
@brief projectDataBase::linkedSymbols
@return the symbols @p element is linked to, read from the link table
@@ -1467,9 +1538,111 @@ QSet<QUuid> projectDataBase::foliosWithUnbuiltEdits() const
{
QSet<QUuid> folios;
for (const auto &edit : m_unbuilt_edits) folios.insert(edit.folio);
for (const auto &edit : m_unbuilt_wire_edits) folios.insert(edit.folio);
return folios;
}
/**
@brief projectDataBase::unbuiltWire
@return true if the wire @p wire is on a folio not built (lazy folios):
it has a row and its properties are stored, and no wire built holds its
uuid. @p folio is set to its folio.
*/
bool projectDataBase::unbuiltWire(const QUuid &wire, QUuid *folio) const
{
if (placedConductorCount(wire) != 0 || !hasConductorProperties(wire)) {
return false;
}
QSqlQuery query(m_data_base);
query.prepare(QStringLiteral("SELECT diagram_uuid FROM conductor WHERE uuid = :uuid"));
query.bindValue(QStringLiteral(":uuid"), wire.toString());
if (!query.exec() || !query.next()) {
return false;
}
if (folio) *folio = QUuid(query.value(0).toString());
return !query.next();
}
/**
@brief projectDataBase::editUnbuiltConductorProperties
Give the wire @p wire, on a folio not built (unbuiltWire()), the
properties @p properties as Conductor::setProperties() would: the store,
its rows and its conductor row's text. Building it takes them
(takeUnbuiltWireEdit()); a .qetz saves them in its database (QETProject::
toXml()), anything else builds its folio to save.
*/
void projectDataBase::editUnbuiltConductorProperties(const QUuid &wire, const ConductorProperties &properties)
{
QUuid folio;
if (!unbuiltWire(wire, &folio)) {
return;
}
m_unbuilt_wire_edits.insert(wire, {properties, folio});
if (!storeConductorProperties(wire, properties)) {
return;
}
m_content_changed = true;
if (!m_update_blocked) {
m_update_conductor_query.bindValue(QStringLiteral(":uuid"), wire.toString());
m_update_conductor_query.bindValue(QStringLiteral(":text"), properties.text);
if (!m_update_conductor_query.exec()) {
qDebug() << "projectDataBase::editUnbuiltConductorProperties update error : " << m_update_conductor_query.lastError();
}
}
emit dataBaseUpdated();
}
/**
@brief projectDataBase::takeUnbuiltWireEdit
@return the properties @p wire was given while its folio was not built,
forgotten here: the wire being built takes them
*/
std::optional<ConductorProperties> projectDataBase::takeUnbuiltWireEdit(const QUuid &wire)
{
const auto edit = m_unbuilt_wire_edits.constFind(wire);
if (edit == m_unbuilt_wire_edits.constEnd()) {
return std::nullopt;
}
const ConductorProperties properties = edit->properties;
m_unbuilt_wire_edits.erase(edit);
return properties;
}
/**
@brief projectDataBase::unbuiltWireEditsOnFolio
@return the properties given to the wires of the folio @p folio while it
was not built, by wire
*/
QHash<QUuid, ConductorProperties> projectDataBase::unbuiltWireEditsOnFolio(const QUuid &folio) const
{
QHash<QUuid, ConductorProperties> edits;
for (auto it = m_unbuilt_wire_edits.cbegin() ; it != m_unbuilt_wire_edits.cend() ; ++it)
if (it->folio == folio) edits.insert(it.key(), it->properties);
return edits;
}
/**
@brief projectDataBase::setPlacedConductorProperties
Give the wire @p wire its properties @p properties: through the wire when
it is built, through the store when it is not (unbuiltWire()). What
undoing and redoing a change of wires not built does, the wires having
perhaps been built since.
@return false if neither can
*/
bool projectDataBase::setPlacedConductorProperties(const QUuid &wire, const ConductorProperties &properties)
{
const QList<QPointer<Conductor>> placed = m_placed_conductors.values(wire);
if (placed.size() == 1 && placed.first()) {
placed.first()->setProperties(properties);
return true;
}
if (placed.isEmpty() && unbuiltWire(wire)) {
editUnbuiltConductorProperties(wire, properties);
return true;
}
return false;
}
/**
@brief projectDataBase::unbuiltEditsOnFolio
@return the information changed through the store of the symbols on the
+9
View File
@@ -186,6 +186,11 @@ class projectDataBase : public QObject
QSet<QUuid> foliosWithUnbuiltEdits() const;
QHash<QUuid, DiagramContext> unbuiltEditsOnFolio(const QUuid &folio, bool *need_build = nullptr) const;
bool setPlacedElementInformation(const QUuid &element, const DiagramContext &information);
bool unbuiltWire(const QUuid &wire, QUuid *folio = nullptr) const;
void editUnbuiltConductorProperties(const QUuid &wire, const ConductorProperties &properties);
std::optional<ConductorProperties> takeUnbuiltWireEdit(const QUuid &wire);
QHash<QUuid, ConductorProperties> unbuiltWireEditsOnFolio(const QUuid &folio) const;
bool setPlacedConductorProperties(const QUuid &wire, const ConductorProperties &properties);
QHash<QUuid, QString> elementTypesOnFolios(const QSet<QUuid> &folios) const;
/// A symbol placed on a folio, as the element table has it
struct FolioSymbol {
@@ -203,6 +208,7 @@ class projectDataBase : public QObject
QList<FolioWire> wiresOnFolios(const QList<QUuid> &folios) const;
QString elementLabel(const QUuid &element) const;
QString terminalName(const QUuid &terminal, const QUuid &element) const;
std::optional<QSet<QUuid>> potentialWires(const QUuid &wire) const;
static QVariant sequenceValue(const autonum::sequentialNumbers &sequence);
static autonum::sequentialNumbers sequenceFromValue(const QVariant &value);
@@ -346,6 +352,9 @@ class projectDataBase : public QObject
/// each is on: building the symbol takes it (takeUnbuiltEdit())
struct UnbuiltEdit { DiagramContext information; QUuid folio; bool had_information; };
QHash<QUuid, UnbuiltEdit> m_unbuilt_edits;
/// The same for wires (editUnbuiltConductorProperties())
struct UnbuiltWireEdit { ConductorProperties properties; QUuid folio; };
QHash<QUuid, UnbuiltWireEdit> m_unbuilt_wire_edits;
QHash<QUuid, TitleBlockProperties> m_folio_titleblocks;
QHash<QUuid, QPointer<Diagram>> m_placed_folios;
QHash<QUuid, ConductorProperties> m_conductor_properties;
+5
View File
@@ -1154,6 +1154,11 @@ bool Conductor::fromXml(QDomElement &dom_element)
m_freeze_label = dom_element.attribute("freezeLabel") == "true" ? true : false;
//Changed through the project database while its folio was not
//built (lazy folios): the change, not what the file says
if (diagram() && diagram()->project())
if (const auto edited = diagram()->project()->dataBase()->takeUnbuiltWireEdit(m_uuid))
pr = *edited;
setProperties(pr);
return retval;
+21
View File
@@ -2512,6 +2512,17 @@ QDomDocument QETProject::toXml(QHash<int, QByteArray> *kept_folios, QStringList
const QDomElement old_information = element.firstChildElement(QStringLiteral("elementInformations"));
element.replaceChild(information, old_information); //it has one (unbuiltEditsOnFolio())
}
//Its wires whose properties changed
const QHash<QUuid, ConductorProperties> wire_edits = m_data_base.unbuiltWireEditsOnFolio(diagram->uuid());
if (!wire_edits.isEmpty())
for (const QDomElement &list : QET::findInDomElement(xml_diagram, QStringLiteral("conductors")))
for (QDomElement wire = list.firstChildElement(QStringLiteral("conductor")) ; !wire.isNull() ;
wire = wire.nextSiblingElement(QStringLiteral("conductor"))) {
const auto edit = wire_edits.constFind(QUuid(wire.attribute(QStringLiteral("uuid"))));
if (edit == wire_edits.constEnd()) continue;
for (const auto &attribute : edit->attributes())
wire.setAttribute(attribute.first, attribute.second);
}
xml_diagram.setAttribute("order", order_num ++);
project_root.appendChild(xml_diagram);
continue;
@@ -2530,6 +2541,16 @@ QDomDocument QETProject::toXml(QHash<int, QByteArray> *kept_folios, QStringList
it.value().toXml(information, QStringLiteral("elementInformation"));
symbol.appendChild(information);
placeholder.appendChild(symbol);
}
//Its wires whose properties changed: what the .qetz database
//holds of them (only that can change, QetScriptApi)
const QHash<QUuid, ConductorProperties> wire_edits = m_data_base.unbuiltWireEditsOnFolio(diagram->uuid());
for (auto it = wire_edits.cbegin() ; it != wire_edits.cend() ; ++it) {
auto wire = xml_doc.createElement(QStringLiteral("qetz-edited-conductor"));
wire.setAttribute(QStringLiteral("uuid"), it.key().toString());
for (const auto &attribute : it.value().attributes())
wire.setAttribute(attribute.first, attribute.second);
placeholder.appendChild(wire);
}
kept_parts->append(diagram->deferredPart());
project_root.appendChild(placeholder);
+178
View File
@@ -1265,6 +1265,16 @@ bool QetScriptApi::setConductorProperty(int folioIndex, const QString &elementUu
.arg(caller, value, property));
return false;
}
return applyConductorProperties(conductor, properties);
}
/**
@brief QetScriptApi::applyConductorProperties
Give @p conductor and every wire at its potential the properties
@p properties, one undo step
*/
bool QetScriptApi::applyConductorProperties(Conductor *conductor, const ConductorProperties &properties)
{
if (properties == conductor->properties()) return true; // already so
QSet<Conductor *> potential = conductor->relatedPotentialConductors(true);
@@ -1282,6 +1292,174 @@ bool QetScriptApi::setConductorProperty(int folioIndex, const QString &elementUu
return true;
}
namespace {
//The wire properties a .qetz's database holds in its own columns, which
//nothing on a folio computes from but a wire's number formula
const QStringList &storedConductorPropertyNames()
{
static const QStringList names {
QStringLiteral("num"), QStringLiteral("function"), QStringLiteral("bus"),
QStringLiteral("cable"), QStringLiteral("tension_protocol"),
QStringLiteral("conductor_color"), QStringLiteral("conductor_section")};
return names;
}
//true if @p a and @p b differ only in storedConductorPropertyNames()
bool differOnlyInStored(const ConductorProperties &a, const ConductorProperties &b)
{
QHash<QString, QString> left, right;
for (const auto &attribute : a.attributes()) left.insert(attribute.first, attribute.second);
for (const auto &attribute : b.attributes()) right.insert(attribute.first, attribute.second);
QSet<QString> names(left.keyBegin(), left.keyEnd());
for (const QString &name : right.keys()) names.insert(name);
for (const QString &name : std::as_const(names))
if (left.value(name) != right.value(name) && !storedConductorPropertyNames().contains(name))
return false;
return true;
}
//Wires given their properties by uuid, built or not (lazy folios):
//projectDataBase::setPlacedConductorProperties()
class ChangeStoredConductorPropertiesCommand : public QUndoCommand
{
public:
ChangeStoredConductorPropertiesCommand(QETProject *project,
const QHash<QUuid, std::pair<ConductorProperties, ConductorProperties>> &changes) :
m_project(project), m_changes(changes)
{ setText(QObject::tr("Edit the conductor properties")); }
void undo() override { apply(false); }
void redo() override { apply(true); }
private:
void apply(bool forward) {
if (!m_project) return;
for (auto it = m_changes.cbegin() ; it != m_changes.cend() ; ++it)
m_project->dataBase()->setPlacedConductorProperties(
it.key(), forward ? it->second : it->first);
}
QPointer<QETProject> m_project;
QHash<QUuid, std::pair<ConductorProperties, ConductorProperties>> m_changes;
};
}
/**
@brief QetScriptApi::findConductorByUuid
@return the wire @p conductorUuid of the folio @p folioIndex, the folio
built, or nullptr (said in the log)
*/
Conductor *QetScriptApi::findConductorByUuid(int folioIndex, const QString &conductorUuid,
const QString &caller)
{
if (!m_project || folioIndex < 0 || folioIndex >= m_project->folios().count()) {
log(QStringLiteral("qet.%1: no folio at index %2").arg(caller).arg(folioIndex));
return nullptr;
}
const QUuid wanted(conductorUuid);
for (Conductor *c : built(m_project->folios().at(folioIndex))->conductors())
if (c->uuid() == wanted) return c;
log(QStringLiteral("qet.%1: no conductor %2 on folio %3").arg(caller, conductorUuid).arg(folioIndex));
return nullptr;
}
/**
@brief QetScriptApi::unbuiltConductorChange
Change the property @p property of the wire @p wire, on the folio
@p folioIndex not built (lazy folios), and of every wire at its potential,
through the project database, as applyConductorProperties() would: when
they are all on folios not built, none is numbered by a formula, and what
changes in each is only what the .qetz database holds
(storedConductorPropertyNames()) -- nothing then is computed from it.
@return true if done; false for the folio to be built, @p refused set when
the value itself is not one
*/
bool QetScriptApi::unbuiltConductorChange(int folioIndex, const QUuid &wire, const QString &property,
const QString &value, bool *refused)
{
*refused = false;
projectDataBase *database = m_project->dataBase();
Diagram *folio = m_project->folios().at(folioIndex);
QUuid on;
if (folio->isLoaded() || !database->rowsFromDocument() || !storedConductorPropertyNames().contains(property)
|| !database->unbuiltWire(wire, &on) || on != folio->uuid()) {
return false;
}
ConductorProperties properties = database->conductorProperties(wire);
if (!setConductorPropertyValue(properties, property, value)) {
*refused = true;
return false;
}
if (!properties.m_formula.isEmpty()) {
return false;
}
const auto potential = database->potentialWires(wire);
if (!potential) {
return false;
}
QHash<QUuid, std::pair<ConductorProperties, ConductorProperties>> changes;
for (const QUuid &other : *potential) {
if (!database->unbuiltWire(other)) return false;
const ConductorProperties old = database->conductorProperties(other);
if (!differOnlyInStored(old, properties)) return false;
if (!(old == properties)) changes.insert(other, {old, properties});
}
if (!changes.isEmpty())
m_project->undoStack()->push(new ChangeStoredConductorPropertiesCommand(m_project, changes));
return true;
}
QString QetScriptApi::conductorPropertyByUuid(int folioIndex, const QString &conductorUuid,
const QString &property) const
{
if (!m_project || folioIndex < 0 || folioIndex >= m_project->folios().count()) return QString();
Diagram *folio = m_project->folios().at(folioIndex);
projectDataBase *database = m_project->dataBase();
QUuid on;
if (!folio->isLoaded() && database->rowsFromDocument()
&& database->unbuiltWire(QUuid(conductorUuid), &on) && on == folio->uuid())
return conductorPropertyValue(database->conductorProperties(QUuid(conductorUuid)), property);
auto *self = const_cast<QetScriptApi *>(this);
Conductor *conductor = self->findConductorByUuid(folioIndex, conductorUuid,
QStringLiteral("conductorPropertyByUuid"));
return conductor ? conductorPropertyValue(conductor->properties(), property) : QString();
}
bool QetScriptApi::setConductorPropertyByUuid(int folioIndex, const QString &conductorUuid,
const QString &property, const QString &value)
{
if (!m_project) return false;
const QString caller = QStringLiteral("setConductorPropertyByUuid");
if (m_project->isReadOnly()) {
log(QStringLiteral("qet.%1: project is read-only").arg(caller));
return false;
}
if (!conductorPropertyNames().contains(property)) {
log(QStringLiteral("qet.%1: unknown property '%2'; expected one of %3")
.arg(caller, property, conductorPropertyNames().join(QStringLiteral(", "))));
return false;
}
if (folioIndex < 0 || folioIndex >= m_project->folios().count()) {
log(QStringLiteral("qet.%1: no folio at index %2").arg(caller).arg(folioIndex));
return false;
}
//The folio left unbuilt when the change can go through the project
//database (stage D5)
bool refused = false;
if (unbuiltConductorChange(folioIndex, QUuid(conductorUuid), property, value, &refused))
return true;
if (refused) {
log(QStringLiteral("qet.%1: '%2' is not a valid value for %3").arg(caller, value, property));
return false;
}
Conductor *conductor = findConductorByUuid(folioIndex, conductorUuid, caller);
if (!conductor) return false;
ConductorProperties properties = conductor->properties();
if (!setConductorPropertyValue(properties, property, value)) {
log(QStringLiteral("qet.%1: '%2' is not a valid value for %3")
.arg(caller, value, property));
return false;
}
return applyConductorProperties(conductor, properties);
}
/**
@brief QetScriptApi::conductorSegments
List the drawn path of the conductor on a terminal, one line per
+10
View File
@@ -34,6 +34,7 @@ class DiagramView;
class Element;
class Terminal;
class Conductor;
class ConductorProperties;
class IndependentTextItem;
class QetShapeItem;
class DiagramImageItem;
@@ -440,6 +441,11 @@ class QetScriptApi : public QObject
Q_INVOKABLE bool setConductorProperty(int folioIndex, const QString &elementUuid,
int terminalIndex, const QString &property,
const QString &value);
// The same, the wire named by its uuid (conductorUuids())
Q_INVOKABLE QString conductorPropertyByUuid(int folioIndex, const QString &conductorUuid,
const QString &property) const;
Q_INVOKABLE bool setConductorPropertyByUuid(int folioIndex, const QString &conductorUuid,
const QString &property, const QString &value);
// -- a conductor's own drawn path, not the whole potential's
// properties above -- one conductor only, addressed the same way --
@@ -648,6 +654,10 @@ class QetScriptApi : public QObject
private:
bool runFlag(const QString &flag, const QStringList &args);
Diagram *built(Diagram *folio) const;
Conductor *findConductorByUuid(int folioIndex, const QString &conductorUuid, const QString &caller);
bool applyConductorProperties(Conductor *conductor, const ConductorProperties &properties);
bool unbuiltConductorChange(int folioIndex, const QUuid &wire, const QString &property,
const QString &value, bool *refused);
std::optional<DiagramContext> unbuiltInformation(int folioIndex, const QString &elementUuid) const;
Element *findElement(int folioIndex, const QString &elementUuid) const;
Terminal *findTerminal(int folioIndex, const QString &elementUuid, int terminalIndex,
+1 -1
View File
@@ -92,7 +92,7 @@ if(Python3_Interpreter_FOUND AND CMAKE_GENERATOR STREQUAL "Ninja")
--build ${CMAKE_BINARY_DIR} --binary $<TARGET_FILE:qelectrotech>
--source ${CMAKE_CURRENT_SOURCE_DIR}/lazy_folios_probe.cpp
--fixture ${CMAKE_CURRENT_SOURCE_DIR}/../../examples/industrial.qet)
set_tests_properties(tst_lazyfolios PROPERTIES TIMEOUT 600 RUN_SERIAL TRUE
set_tests_properties(tst_lazyfolios PROPERTIES TIMEOUT 1800 RUN_SERIAL TRUE
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
# Deleting a cable, undoing the delete twice and then editing anything
# has both cable commands tearing down the same cable. Runs under the
+92
View File
@@ -777,6 +777,28 @@ int main(int argc, char **argv)
std::printf(".qetz drawn items through their view: %d lazily, %d when built\n", lazily, built);
check(built > 0, "the project has drawn items");
check(lazily == built, "the drawn items' view as when built");
}
//Every wire's potential from the database as QElectroTech
//follows it with every folio built
{
QETProject indexed_potentials(zipped);
int same = 0, differ = 0, unknown = 0;
for (Diagram *folio : eager.folios())
for (Conductor *wire : folio->conductors()) {
QSet<QUuid> live{wire->uuid()};
for (Conductor *c : wire->relatedPotentialConductors(true)) live.insert(c->uuid());
for (QETProject *project : {&eager, &indexed_potentials}) {
const auto stored = project->dataBase()->potentialWires(wire->uuid());
if (!stored) { ++unknown; continue; }
if (*stored == live) { ++same; continue; }
if (differ++ < 3)
std::printf(" potential of %s: %d wires followed, %d from the database\n",
qPrintable(wire->uuid().toString()), int(live.size()), int(stored->size()));
}
}
std::printf("potentials from the database: %d same, %d differ, %d not answered\n", same, differ, unknown);
check(differ == 0 && same > 0, "every wire's potential from the database as followed");
check(indexed_potentials.unloadedFolioCount() > 0, "potentials read without building");
}
//The folios' rows as saved, not as reading their headers one
//by one numbers them ("1/1" for the first of three, D7)
@@ -997,6 +1019,76 @@ int main(int argc, char **argv)
revisionEveryFolio(zipped, output, "revised", path);
revisionEveryFolio(zipped, output, "relabelled", path, busiest);
//A script changes a wire's cable by its uuid, on folios not
//built, the wire and its potential numbered by hand: through
//the project database, no folio built; saved as when built;
//undone; the folio built afterwards shows it (D5)
{
QETProject cabled(zipped);
const int unbuilt = cabled.unloadedFolioCount();
int folio_index = -1;
QString uuid;
QSet<QUuid> potential;
for (int i = 0 ; i < folios.size() && uuid.isEmpty() ; ++i) {
if (cabled.folios().at(i)->isLoaded()) continue;
for (Conductor *wire : folios.at(i)->conductors()) {
QSet<Conductor *> wires = wire->relatedPotentialConductors(true);
wires << wire;
bool usable = true;
for (Conductor *c : std::as_const(wires))
if (!c->properties().m_formula.isEmpty()
|| cabled.folios().at(eager.folioIndex(c->diagram()))->isLoaded())
usable = false;
if (!usable || wires.size() < 2) continue;
folio_index = i;
uuid = wire->uuid().toString();
for (Conductor *c : std::as_const(wires)) potential.insert(c->uuid());
break;
}
}
check(!uuid.isEmpty(), "a wire numbered by hand, its potential on folios not built");
QetScriptApi api(&cabled);
const QString before = api.conductorPropertyByUuid(folio_index, uuid, "cable");
const QString value = QStringLiteral("W-42");
check(api.setConductorPropertyByUuid(folio_index, uuid, "cable", value), "a script changes a wire not built");
check(api.conductorPropertyByUuid(folio_index, uuid, "cable") == value, "the wire's change read back");
for (const QUuid &wire : std::as_const(potential))
check(cabled.dataBase()->conductorProperties(wire).m_cable == value, "every wire of the potential changed");
check(cabled.unloadedFolioCount() == unbuilt, "changing wires not built builds no folio");
const QString cabled_path = output + "/cabled.qetz";
check(cabled.writeContainerFile(cabled_path, &error), "saved after the wire change");
std::printf(".qetz wire on folio %d, potential of %d wires, cable changed by uuid: 0 folios built, %d by saving\n",
folio_index + 1, int(potential.size()), unbuilt - cabled.unloadedFolioCount());
qputenv("QET_EAGER_FOLIOS", "1");
const QString usual_path = output + "/cabled-usual.qetz";
{
QETProject usual_cabled(zipped);
check(QetScriptApi(&usual_cabled).setConductorPropertyByUuid(folio_index, uuid, "cable", value),
"the same wire change when built");
check(usual_cabled.writeContainerFile(usual_path, &error), "saved after the wire change when built");
}
QETProject cabled_back(cabled_path), usual_back(usual_path);
qunsetenv("QET_EAGER_FOLIOS");
auto lines = [&path](QETProject &project) {
QStringList list = sortedLines(project.toXml().toString(), path);
list.removeIf([](const QString &line) { return line.contains(QLatin1String("name=\"saved")); });
return list;
};
check(lines(cabled_back) == lines(usual_back), "a wire changed through the database saved as when built");
cabled.undoStack()->undo();
check(api.conductorPropertyByUuid(folio_index, uuid, "cable") == before, "the wire change undone");
cabled.undoStack()->redo();
Conductor *built_wire = nullptr;
for (Conductor *wire : cabled.folios().at(folio_index)->conductors())
if (wire->uuid().toString() == uuid) built_wire = wire;
check(!built_wire, "the folio not built yet");
cabled.buildFolioToShow(cabled.folios().at(folio_index));
for (Conductor *wire : cabled.folios().at(folio_index)->conductors())
if (wire->uuid().toString() == uuid) built_wire = wire;
check(built_wire && built_wire->properties().m_cable == value,
"a wire changed before its folio was built shows the change");
}
//The wire numbers and wiring list exports, and the terminal
//numbering dialog: as when built, building only the folios
//holding a terminal (D5)
+1 -1
View File
@@ -54,7 +54,7 @@ subprocess.run([ninja, '-C', str(build), '-f', probe_manifest.name, target], che
output = build / 'lazy-folios'
output.mkdir(exist_ok=True)
report = output / 'results.txt'
result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=500)
result = subprocess.run([str(probe_binary), str(fixture), str(report), str(output)], timeout=1500)
if report.exists():
print(report.read_text(encoding='utf-8', errors='replace'))
sys.exit(result.returncode)