Files
qelectrotech-source-mirror/tests/qttest/lazy_folios_probe.cpp
T
ispyisail 00e200b504 Remove references to notes that are not in this repository
Comments added with the lazy folios cited planning notes kept outside the
QElectroTech repository, which a reader of the source cannot open. The
sentences now stand on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:03:14 +13:00

620 lines
27 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// Lazy folios (QET_LAZY_FOLIOS),
// linked against the application's objects: a folio shown in a project
// opened without building its folios draws and links as in the project
// opened as usual, building only it and the folios its links and tables
// reach, and the project saves the same.
#include <QApplication>
#include <QFile>
#include <QFontDatabase>
#include <QImage>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QRegularExpression>
#include <QPainter>
#include <QSettings>
#include <QTemporaryDir>
#include <cstdio>
#include <stdexcept>
#include "../../sources/diagram.h"
#include "../../sources/qetproject.h"
#include "../../sources/qetresult.h"
#include "../../sources/qetmessagebox.h"
#include "../../sources/qetgraphicsitem/element.h"
#include "../../sources/autoNum/elementautonumschemecommand.h"
#include "../../sources/elementprovider.h"
#include "../../sources/TerminalStrip/terminalstrip.h"
#include "../../sources/ui/elementpropertieswidget.h"
#include "../../sources/conductorautonumerotation.h"
#include "../../sources/diagramcontent.h"
#include "../../sources/diagramview.h"
#include "../../sources/diagramevent/diagrameventaddpaste.h"
#include <QKeyEvent>
#include "../../sources/qetgraphicsitem/conductor.h"
#include "../../sources/qetgraphicsitem/terminal.h"
#include "../../sources/undocommand/changeelementinformationcommand.h"
#include "../../sources/undocommand/changetitleblockcommand.h"
#include "../../sources/undocommand/deleteqgraphicsitemcommand.h"
#include "../../sources/factory/elementfactory.h"
#include "../../sources/undocommand/addgraphicsobjectcommand.h"
#include "../../sources/dataBase/projectdatabase.h"
#include <QDomDocument>
static void check(bool value, const char *message) { if (!value) throw std::runtime_error(message); }
//Every symbol's numbering, in a stable order
static QStringList numbering(const QETProject &project)
{
QStringList list;
for (const auto &symbol : project.symbolNumbering())
list << QStringList{symbol.label, symbol.formula, symbol.scheme.toString(),
symbol.sequence.unit.join(';'), symbol.sequence.ten.join(';'),
symbol.sequence.hundred.join(';'),
QString::number(symbol.numbered)}.join('|');
list.sort();
return list;
}
//What the scheme @p title tells of the numbers its symbols carry
static QString gaps(const QETProject &project, const QString &title)
{
QStringList list;
for (const auto &gap : ElementAutoNumSchemeCommand::gapRanges(&project, title))
list << QString("%1-%2").arg(gap.from).arg(gap.to);
return list.join(',');
}
//A symbol of @p location placed on folio @p i as the folio's editor
//places one (DiagramEventAddElement::addElement()), numbered with the
//scheme @p title: its label, and the numbers then free for it
static QString place(QETProject &project, int i, const QString &location, const QString &title)
{
Diagram *folio = project.folios().at(i);
project.buildFolioToShow(folio);
project.setCurrrentElementAutonum(title);
int state = 0;
Element *element = ElementFactory::Instance()->createElement(
ElementsLocation(location, &project), nullptr, &state);
check(!state, "symbol made");
element->setPos(QPointF(-500, -500));
folio->addItem(element);
auto *undo = new QUndoCommand("insert");
new AddGraphicsObjectCommand(element, folio, element->pos(), undo);
element->setUpFormula(true, undo);
folio->undoStack().push(undo);
element->freezeNewAddedElement();
QStringList free;
for (int n : ElementAutoNumSchemeCommand::freeNumbers(&project, title, element))
free << QString::number(n);
return element->actualLabel() + " free " + free.join(',');
}
//The symbols a search of a project finds, by uuid
static QStringList found(const QVector<QPointer<Element>> &elements)
{
QStringList list;
for (const auto &element : elements)
list << element->uuid().toString();
list.sort();
return list;
}
//Everyday edits on folio @p i, as its editor makes them: a symbol's
//information, a wire, the folio's title block, a linked symbol
//deleted, then one undo and one redo
static void edit(QETProject &project, int i)
{
Diagram *folio = project.folios().at(i);
project.buildFolioToShow(folio);
QList<Element *> elements = folio->elements();
std::sort(elements.begin(), elements.end(), [](Element *a, Element *b) {
return a->uuid() < b->uuid(); });
check(elements.size() > 2, "a folio with symbols to edit");
const DiagramContext old_info = elements.first()->elementInformations();
DiagramContext new_info = old_info;
new_info.addValue(QStringLiteral("comment"), QStringLiteral("edited lazily"));
folio->undoStack().push(new ChangeElementInformationCommand(elements.first(), old_info, new_info));
Terminal *ends[2] = {nullptr, nullptr};
for (Element *element : elements) {
for (Terminal *terminal : element->terminals()) {
if (terminal->conductors().isEmpty()
&& (!ends[0] || ends[0]->parentElement() != element)) {
ends[ends[0] ? 1 : 0] = terminal;
break;
}
}
if (ends[1]) break;
}
if (ends[1]) {
auto *undo = new QUndoCommand(QStringLiteral("wire"));
auto *wire = new Conductor(ends[0], ends[1]);
new AddGraphicsObjectCommand(wire, folio, QPointF(), undo);
ConductorAutoNumerotation numbering(wire, folio, undo);
numbering.numerate();
folio->undoStack().push(undo);
}
const TitleBlockProperties old_block = folio->border_and_titleblock.exportTitleBlock();
TitleBlockProperties new_block = old_block;
new_block.title += QStringLiteral(" (edited)");
folio->undoStack().push(new ChangeTitleBlockCommand(folio, old_block, new_block));
//Copy three symbols and paste them, as Ctrl+C then Ctrl+Shift+V
//and Return do
{
DiagramView view(folio);
folio->clearSelection();
for (int n = 0 ; n < 3 ; ++n)
elements.at(n)->setSelected(true);
view.copy();
auto *paste = new DiagramEventAddPaste(folio, QPointF(), DiagramEventAddPaste::AtOrigin);
folio->setEventInterface(paste);
QKeyEvent drop(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
paste->keyPressEvent(&drop);
folio->clearEventInterface();
}
Element *linked = nullptr;
for (Element *element : elements)
if (!element->linkedElements().isEmpty()) { linked = element; break; }
folio->clearSelection();
(linked ? linked : elements.last())->setSelected(true);
folio->undoStack().push(new DeleteQGraphicsItemCommand(folio, DiagramContent(folio, true)));
project.undoStack()->undo();
project.undoStack()->redo();
}
//The project as saving writes it: folios not built written as read
//when building them would write the same (QETProject::toXml())
static QString saved(QETProject &project)
{
QHash<int, QByteArray> kept;
const QDomDocument document = project.toXml(&kept);
return QETProject::spliceKeptFolios(document.toString(4), kept);
}
//The rows of the project database's tables, in their order, read as
//every caller reads them (newQuery(): brought up to date first)
static QStringList tables(QETProject &project)
{
QStringList rows;
for (const char *table : {"diagram", "diagram_info", "element", "element_info",
"terminal", "conductor", "link"}) {
QSqlQuery query = project.dataBase()->newQuery(
QStringLiteral("SELECT * FROM %1 ORDER BY rowid").arg(QLatin1String(table)));
check(query.exec(), "a table read");
QStringList table_rows;
while (query.next()) {
QStringList row;
for (int i = 0 ; i < query.record().count() ; ++i)
row << query.value(i).toString();
table_rows << QLatin1String(table) + ": " + row.join('|');
}
//Rows are written in the order the scene gives a folio's items,
//which is not the same from one opening to the next: two usual
//opens given the same edits differ in it (637 rows of 3 394 on
//industrial.qet). What a table holds is compared, not its order
table_rows.sort();
rows << table_rows;
}
return rows;
}
static QImage render(Diagram *folio)
{
const QRectF rect = folio->border_and_titleblock.borderAndTitleBlockRect().adjusted(-5, -5, 5, 5);
QImage image(rect.size().toSize(), QImage::Format_RGB32);
image.fill(Qt::white);
QPainter painter(&image);
folio->render(&painter, QRectF(QPointF(0, 0), rect.size()), rect);
return image;
}
//Each symbol's partners, in their order
static QStringList links(Diagram *folio)
{
QStringList list;
for (Element *element : folio->elements()) {
QStringList partners;
for (Element *partner : element->linkedElements())
partners << partner->uuid().toString();
list << element->uuid().toString() + ' ' + partners.join(',');
}
list.sort();
return list;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv); std::freopen(argv[2], "w", stdout);
QTemporaryDir settings; QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, settings.path());
QCoreApplication::setOrganizationName("QETLazyFoliosRegression");
QETProject::setBackupEnabled(false); QET::QetMessageBox::setNonInteractive(true);
QFontDatabase::addApplicationFont(":/fonts/LiberationSans-Regular.ttf");
//Folios not built saved as read (QETProject::toXml()): this
//fixture is written by this version's own save
qputenv("QET_LAZY_SAVE_KEPT", "1");
try {
const QString output = QString::fromLocal8Bit(argv[3]);
qunsetenv("QET_LAZY_FOLIOS");
//A file saved by this version: its folios carry their uuids,
//which opening without building them needs
const QString path = output + "/lazy_folios.qet";
QSet<int> strip_folios;
{
QETProject source(QString::fromLocal8Bit(argv[1]));
check(source.state() == QETProject::Ok, "fixture opens");
//A terminal strip of terminals on several folios: it is
//read with the project, before any folio is built
TerminalStrip *strip = source.newTerminalStrip(QStringLiteral("=A1"), QStringLiteral("+B1"),
QStringLiteral("X99"));
int added = 0;
for (Diagram *folio : source.folios())
for (Element *element : folio->elements())
if (element->elementData().m_type == ElementData::Terminal && added < 12
&& strip->addTerminal(element)) {
++added;
strip_folios.insert(source.folioIndex(folio));
}
check(added > 3, "a terminal strip with terminals");
source.setFilePath(path);
check(source.write().isOk(), "fixture saved by this version");
}
QETProject eager(path);
check(eager.state() == QETProject::Ok, "project opens");
{
qputenv("QET_LAZY_FOLIOS", "1");
QETProject lazy_strips(path);
qunsetenv("QET_LAZY_FOLIOS");
auto strip_of = [](const QETProject &project) {
for (TerminalStrip *strip : project.terminalStrip())
if (strip->name() == QStringLiteral("X99")) return strip->physicalTerminalCount();
return -1;
};
check(strip_of(lazy_strips) > 3 && strip_of(lazy_strips) == strip_of(eager),
"a terminal strip keeps its terminals when the folios are not built");
}
const QList<Diagram *> folios = eager.folios();
check(folios.size() > 3, "several folios");
QList<QImage> images; QList<QStringList> partners;
int linked = 0;
for (Diagram *folio : folios) {
images << render(folio);
partners << links(folio);
for (Element *element : folio->elements())
linked += !element->linkedElements().isEmpty();
}
check(linked > 0, "the project has linked symbols");
qputenv("QET_LAZY_FOLIOS", "1");
auto same = [&](QETProject &lazy, int i, const char *what) {
Diagram *folio = lazy.folios().at(i);
lazy.buildFolioToShow(folio);
if (render(folio) != images.at(i) || links(folio) != partners.at(i)) {
std::printf("folio %d, %s: differs\n", i + 1, what);
throw std::runtime_error("a folio shown lazily draws or links as opened as usual");
}
};
//Each folio first after opening
for (int i = 0; i < folios.size(); ++i) {
QETProject lazy(path);
check(lazy.unloadedFolioCount() == folios.size() - strip_folios.size(),
"opened building only the folios of the terminal strip's terminals");
same(lazy, i, "shown first");
check(lazy.unloadedFolioCount() > 0, "showing a folio does not build them all");
}
//Every folio, one after the other, then the save
QETProject lazy(path);
for (int i = 0; i < folios.size(); ++i)
same(lazy, i, "shown in turn");
check(lazy.toXml().toString() == eager.toXml().toString(), "saves as opened as usual");
check(lazy.undoStack()->isClean(), "showing folios changes nothing");
//Numbering asks every symbol of the project: it is answered for
//the folios not built without building them, as when built
QETProject numbered(path);
check(numbering(numbered) == numbering(eager), "symbols' numbering as when built");
const QStringList titles = eager.elementAutoNum().keys();
check(!titles.isEmpty(), "the project has element numbering schemes");
for (const QString &title : titles)
check(gaps(numbered, title) == gaps(eager, title), "a scheme's gaps as when built");
check(numbered.unloadedFolioCount() == folios.size() - strip_folios.size(), "numbering read without building");
//A symbol placed on a folio, numbered by a scheme others follow
QString location, title;
int folio = -1;
for (const auto &symbol : eager.symbolNumbering()) {
if (symbol.numbered && symbol.element && !eager.elementAutoNumTitle(symbol.scheme).isEmpty()) {
location = symbol.element->location().toString();
title = eager.elementAutoNumTitle(symbol.scheme);
folio = eager.folios().indexOf(symbol.element->diagram());
break;
}
}
check(folio >= 0, "a symbol following a scheme");
const QString placed = place(numbered, folio, location, title);
std::printf("placed on folio %d with %s: %s\n", folio + 1, qPrintable(title), qPrintable(placed));
check(numbered.unloadedFolioCount() > 0, "numbering a placed symbol does not build every folio");
QETProject usual(path);
check(placed == place(usual, folio, location, title), "a placed symbol numbered as when built");
//The candidates a link tab lists (ElementProvider): found as
//when built, building only the folios which hold some
const ElementProvider usual_search(&eager);
for (const ElementData::Types kinds : {ElementData::Types(ElementData::Slave),
ElementData::Types(ElementData::Master),
ElementData::Types(ElementData::AllReport),
ElementData::Types(ElementData::Terminal)}) {
QETProject searched(path);
const ElementProvider search(&searched);
const QStringList lazy_free = found(search.freeElement(kinds)), usual_free = found(usual_search.freeElement(kinds));
if (lazy_free != usual_free) {
for (const QString &u : usual_free) if (!lazy_free.contains(u)) std::printf(" only when built: %s\n", qPrintable(u));
for (const QString &u : lazy_free) if (!usual_free.contains(u)) std::printf(" only lazily: %s\n", qPrintable(u));
}
check(lazy_free == usual_free, "free symbols found as when built");
std::printf("free symbols of kinds %d: %d folios built\n", int(kinds),
int(folios.size() - searched.unloadedFolioCount()));
check(found(search.find(kinds)) == found(usual_search.find(kinds)),
"symbols found as when built");
}
{
QETProject searched(path);
ElementProvider(&searched).freeElement(ElementData::Slave);
check(searched.unloadedFolioCount() > 0, "a search for free contacts does not build every folio");
}
//Selecting a symbol shows its properties, a link tab for a
//coil, a contact or a report: the folio and few others built
for (const Element::kind kind : {Element::Master, Element::Slave, Element::NextReport,
Element::PreviousReport}) {
QETProject selected(path);
Element *element = nullptr;
for (int i = 0 ; !element && i < folios.size() ; ++i) {
for (Element *candidate : eager.folios().at(i)->elements()) {
if (candidate->linkType() == kind) {
selected.buildFolioToShow(selected.folios().at(i));
for (Element *e : selected.folios().at(i)->elements())
if (e->uuid() == candidate->uuid()) element = e;
break;
}
}
}
check(element, "a symbol of each kind with a link tab");
const int before = selected.unloadedFolioCount();
delete new ElementPropertiesWidget(element);
std::printf("properties of a symbol of kind %d: %d more folios built, %d not built\n",
int(kind), before - selected.unloadedFolioCount(), selected.unloadedFolioCount());
check(selected.unloadedFolioCount() > 0, "selecting a symbol does not build every folio");
}
//Everyday edits on a folio linked to others: the others stay
//not built, and the project saves as when they are
int busiest = 0, most = -1;
for (int i = 0 ; i < folios.size() ; ++i) {
int partners = 0;
for (Element *element : folios.at(i)->elements())
for (Element *partner : element->linkedElements())
partners += partner->diagram() != folios.at(i);
if (partners > most) { most = partners; busiest = i; }
}
{
QETProject edited(path), usual(path);
qunsetenv("QET_LAZY_FOLIOS");
QETProject usual_edited(path);
qputenv("QET_LAZY_FOLIOS", "1");
edit(edited, busiest);
std::printf("edits on folio %d (%d links to other folios): %d folios not built\n",
busiest + 1, most, edited.unloadedFolioCount());
check(edited.unloadedFolioCount() > 0, "editing a folio does not build every folio");
edit(usual_edited, busiest);
//Made by the edits, a new wire's uuid is random: any uuid the
//file had not is "new" before comparing
QFile saved(path); saved.open(QIODevice::ReadOnly);
const QString before = QString::fromUtf8(saved.readAll());
static const QRegularExpression uuid_re(QStringLiteral("uuid=\"(\\{[0-9a-f-]{36}\\})\""));
auto known = [&before](QString xml) {
QString out;
qsizetype at = 0;
for (auto it = uuid_re.globalMatch(xml) ; it.hasNext() ; ) {
const auto match = it.next();
out += xml.mid(at, match.capturedStart() - at);
out += before.contains(match.captured(1)) ? match.captured(0)
: QStringLiteral("uuid=\"new\"");
at = match.capturedEnd();
}
return out + xml.mid(at);
};
const QStringList lazy_rows = tables(edited), usual_rows = tables(usual_edited);
static const QRegularExpression bare_uuid_re(QStringLiteral("\\{[0-9a-f-]{36}\\}"));
auto known_row = [&before](QString row) {
for (auto it = bare_uuid_re.globalMatch(QString(row)) ; it.hasNext() ; ) {
const QString uuid = it.next().captured(0);
if (!before.contains(uuid)) row.replace(uuid, QStringLiteral("new"));
}
return row;
};
check(edited.unloadedFolioCount() > 0, "reading the database does not build every folio");
QStringList lazy_known, usual_known;
for (const QString &row : lazy_rows) lazy_known << known_row(row);
for (const QString &row : usual_rows) usual_known << known_row(row);
lazy_known.sort();
usual_known.sort();
int differing = 0;
for (int i = 0 ; i < std::max(lazy_known.size(), usual_known.size()) ; ++i)
if (lazy_known.value(i) != usual_known.value(i) && differing++ < 5)
std::printf(" row %d: lazy %s\n usual %s\n", i, qPrintable(lazy_known.value(i).left(160)),
qPrintable(usual_known.value(i).left(160)));
std::printf("database after the edits: %d rows, %d differ\n", int(usual_rows.size()), differing);
check(!differing, "the database after the edits as when every folio is built");
//A pasted copy has a random uuid, and a folio's symbols are
//saved in uuid order: the lines are compared, not their order
auto lines = [&known](const QString &xml) {
QStringList list = known(xml).split(QLatin1Char('\n'));
list.sort();
return list.join(QLatin1Char('\n'));
};
const QString a = lines(edited.toXml().toString()), b = lines(usual_edited.toXml().toString());
if (a != b) {
QFile fa(output + "/edited-lazy.xml"); fa.open(QIODevice::WriteOnly); fa.write(a.toUtf8());
QFile fb(output + "/edited-usual.xml"); fb.open(QIODevice::WriteOnly); fb.write(b.toUtf8());
}
check(a == b,
"edits save as when every folio is built");
}
//Saving writes the folios not built as they were read, the same
//bytes as building them would write
const QString usual_text = saved(eager);
{
QETProject untouched(path);
const int unbuilt = untouched.unloadedFolioCount();
const QString untouched_text = saved(untouched);
if (untouched_text != usual_text) {
QFile fa(output + "/untouched-lazy.xml"); fa.open(QIODevice::WriteOnly); fa.write(untouched_text.toUtf8());
QFile fb(output + "/untouched-usual.xml"); fb.open(QIODevice::WriteOnly); fb.write(usual_text.toUtf8());
}
check(untouched_text == usual_text, "a project saved without building, as when built");
//Only the partners of the folios the terminal strip built
std::printf("saved untouched: %d of %d folios not built built to save\n",
unbuilt - untouched.unloadedFolioCount(), unbuilt);
check(untouched.unloadedFolioCount() > 0, "saving does not build every folio");
}
//After everyday edits on a folio: as when built, others not built
{
QETProject edited(path), usual(path);
qunsetenv("QET_LAZY_FOLIOS");
QETProject usual_edited(path);
qputenv("QET_LAZY_FOLIOS", "1");
edit(edited, busiest);
edit(usual_edited, busiest);
QFile file(path); file.open(QIODevice::ReadOnly);
const QString before = QString::fromUtf8(file.readAll());
static const QRegularExpression uuid_re(QStringLiteral("\\{[0-9a-f-]{36}\\}"));
auto lines = [&before](QString text) {
for (auto it = uuid_re.globalMatch(QString(text)) ; it.hasNext() ; ) {
const QString uuid = it.next().captured(0);
if (!before.contains(uuid)) text.replace(uuid, QStringLiteral("new"));
}
QStringList list = text.split(QLatin1Char('\n'));
list.sort();
return list;
};
const QStringList la = lines(saved(edited)), lb = lines(saved(usual_edited));
if (la != lb) {
int shown = 0;
for (const QString &l : lb) if (!la.contains(l) && shown++ < 6) std::printf(" only usual: %s\n", qPrintable(l.trimmed().left(150)));
shown = 0;
for (const QString &l : la) if (!lb.contains(l) && shown++ < 6) std::printf(" only lazy: %s\n", qPrintable(l.trimmed().left(150)));
}
check(la == lb, "edits saved without building, as when built");
std::printf("saved after the edits: %d folios not built\n", edited.unloadedFolioCount());
check(edited.unloadedFolioCount() > 0, "saving after edits does not build every folio");
}
//A folio not built given a new title: it and the folios its
//links reach are built to be saved, the others not
{
QETProject renamed(path);
qunsetenv("QET_LAZY_FOLIOS");
QETProject usual_renamed(path);
qputenv("QET_LAZY_FOLIOS", "1");
for (QETProject *project : {&renamed, &usual_renamed}) {
Diagram *folio = project->folios().at(busiest);
TitleBlockProperties block = folio->border_and_titleblock.exportTitleBlock();
block.title += QStringLiteral(" (renamed)");
folio->border_and_titleblock.importTitleBlock(block);
}
check(saved(renamed) == saved(usual_renamed), "a renamed folio saved as when built");
std::printf("saved after a folio not built was renamed: %d folios not built\n", renamed.unloadedFolioCount());
check(renamed.unloadedFolioCount() > 0, "renaming a folio does not build every folio to save");
}
//Project properties, which any folio's texts can use: every
//folio is built to be saved
{
QETProject changed(path);
qunsetenv("QET_LAZY_FOLIOS");
QETProject usual_changed(path);
qputenv("QET_LAZY_FOLIOS", "1");
for (QETProject *project : {&changed, &usual_changed}) {
DiagramContext properties = project->projectProperties();
properties.addValue(QStringLiteral("lazy_test"), QStringLiteral("1"));
project->setProjectProperties(properties);
}
check(saved(changed) == saved(usual_changed), "changed project properties saved as when built");
check(changed.unloadedFolioCount() == 0, "changed project properties build every folio to save");
}
//A potential through folio reports is followed as far as when
//every folio is built
{
Conductor *usual_wire = nullptr;
for (Diagram *folio : eager.folios())
for (Element *element : folio->elements())
if ((element->linkType() & Element::AllReport) && !element->conductors().isEmpty()
&& !usual_wire)
usual_wire = element->conductors().first();
check(usual_wire, "a wire on a folio report");
auto potential = [](Conductor *wire) {
QStringList list;
for (Conductor *c : wire->relatedPotentialConductors(true))
list << c->uuid().toString();
list.sort();
return list;
};
QETProject followed(path);
Diagram *folio = followed.folios().at(eager.folioIndex(usual_wire->diagram()));
followed.buildFolioToShow(folio);
Conductor *wire = nullptr;
for (Conductor *c : folio->conductors())
if (c->uuid() == usual_wire->uuid()) wire = c;
check(wire, "the same wire");
check(potential(wire) == potential(usual_wire), "a potential followed as when built");
std::printf("potential through reports: %d wires, %d folios not built\n",
int(potential(wire).size()), followed.unloadedFolioCount());
}
//Two symbols sharing a uuid: the database is filled from the
//folios, which builds every one of them, so the project opens
//with every folio built rather than building them all later
{
QFile file(path);
check(file.open(QIODevice::ReadOnly), "fixture read");
QDomDocument document;
check(bool(document.setContent(&file)), "fixture parsed");
file.close();
check(projectDataBase::documentRefusal(document).isEmpty(), "the fixture fills the database itself");
QDomNodeList elements = document.elementsByTagName(QStringLiteral("element"));
QList<QDomElement> placed;
for (int i = 0; i < elements.size(); ++i) {
QDomElement e = elements.at(i).toElement();
if (Element::valideXml(e)) placed << e;
}
check(placed.size() > 1, "two placed symbols");
placed.last().setAttribute(QStringLiteral("uuid"), placed.first().attribute(QStringLiteral("uuid")));
check(!projectDataBase::documentRefusal(document).isEmpty(), "a shared uuid is seen before the folios are read");
const QString shared = output + "/lazy_folios_shared_uuid.qet";
QFile out(shared);
check(out.open(QIODevice::WriteOnly), "copy written");
out.write(document.toByteArray(4));
out.close();
qputenv("QET_LAZY_FOLIOS", "1");
qputenv("QET_LAZY_FOLIOS_TRACE", "1");
static int built_late; built_late = 0;
const QtMessageHandler previous = qInstallMessageHandler(
[](QtMsgType, const QMessageLogContext &, const QString &message) {
if (message.contains(QStringLiteral("built for a caller of diagrams()"))) ++built_late;
});
QETProject opened(shared);
qInstallMessageHandler(previous);
qunsetenv("QET_LAZY_FOLIOS_TRACE");
check(opened.state() == QETProject::Ok, "a project with a shared uuid opens");
check(opened.unloadedFolioCount() == 0, "a project with a shared uuid opens with every folio built");
check(built_late == 0, "a project with a shared uuid builds its folios as it reads them, not later");
}
std::printf("PASS: %d folios\n", int(folios.size()));
} catch (const std::exception &error) {
std::printf("FAIL: %s\n", error.what());
std::fflush(stdout);
return 1;
}
std::fflush(stdout);
return 0;
}