mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-15 10:04:13 +02:00
9078e4e321
The scripting API (scripts and the assistant's live mode) reached a folio through diagrams(), which builds every folio, for every call. A folio's own data -- count, title, uuid, title block, border, wire defaults -- now comes from folios(), built or not; a call reading or changing a folio's items builds that folio, as showing it does (built()); selecting a symbol by uuid builds the folio the project database places it on. Search and replace and the continuity check still build every folio. DiagramContent worked out one wire of each potential when made, which follows potentials through folio reports and built the folios at the other end, for every caller; it is now worked out when first asked (Search and replace asks; a merge asks both sides, as before). tst_lazyfolios: on a .qetz, a script reads every folio's title, uuid and border building none (fails with diagrams()), and the symbols and wires of the folio with the most links to other folios as when built, building that folio only (27 before potentials were lazy). ctest 96/96 but modal_quit_regression in both modes: a race in that test (gdb interrupts the main thread inside malloc while the folio view repaints during the dialog, master too), 2 of 23 runs here, 0 of 10 on master, passing alone. 342 corpus projects save the same in all four arms, and export the same wiring list and wire numbers lazily. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
909 lines
42 KiB
C++
909 lines
42 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/container/qetzip.h"
|
|
#include <QSqlDatabase>
|
|
#include <QMap>
|
|
#include "../../sources/TerminalStrip/terminalstrip.h"
|
|
#include "../../sources/ui/elementpropertieswidget.h"
|
|
#include "../../sources/conductorautonumerotation.h"
|
|
#include "../../sources/conductornumexport.h"
|
|
#include "../../sources/scripting/qetscriptapi.h"
|
|
#include "../../sources/wiringlistexport.h"
|
|
#include "../../sources/ui/terminalnumberingdialog.h"
|
|
#include "../../sources/diagramcontent.h"
|
|
#include "../../sources/diagramview.h"
|
|
#include "../../sources/diagramevent/diagrameventaddpaste.h"
|
|
#include <QKeyEvent>
|
|
#include "../../sources/qetgraphicsitem/conductor.h"
|
|
#include "../../sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.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);
|
|
}
|
|
|
|
//@p xml's lines in order, any uuid not in the file @p path "new": a
|
|
//pasted copy's and a new wire's are random, and a folio's symbols are
|
|
//saved in uuid order
|
|
static QStringList sortedLines(QString xml, const QString &path)
|
|
{
|
|
QFile file(path); file.open(QIODevice::ReadOnly);
|
|
const QString before = QString::fromUtf8(file.readAll());
|
|
static const QRegularExpression uuid_re(QStringLiteral("\\{[0-9a-f-]{36}\\}"));
|
|
for (auto it = uuid_re.globalMatch(QString(xml)) ; it.hasNext() ; ) {
|
|
const QString uuid = it.next().captured(0);
|
|
if (!before.contains(uuid)) xml.replace(uuid, QStringLiteral("new"));
|
|
}
|
|
QStringList list = xml.split(QLatin1Char('\n'));
|
|
list.sort();
|
|
return list;
|
|
}
|
|
|
|
//The saved links of a .qetz, symbol by symbol in their order: what a
|
|
//symbol saves of its partners, as its file holds it
|
|
static QStringList savedLinks(const QString &qetz, const QString &path)
|
|
{
|
|
QFile file(qetz); file.open(QIODevice::ReadOnly);
|
|
QList<QetZip::Entry> entries;
|
|
QString error;
|
|
check(QetZip::fromBytes(file.readAll(), &entries, &error), "a .qetz read");
|
|
QTemporaryDir dir;
|
|
const QString db_path = dir.filePath(QStringLiteral("project.sqlite"));
|
|
for (const QetZip::Entry &entry : entries)
|
|
if (entry.name == QLatin1String("project.sqlite")) {
|
|
QFile db(db_path); db.open(QIODevice::WriteOnly); db.write(entry.data);
|
|
}
|
|
QMap<QString, QStringList> links; //symbol -> its partners in order
|
|
{
|
|
QSqlDatabase db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("saved_links"));
|
|
db.setDatabaseName(db_path);
|
|
check(db.open(), "a .qetz's database opened");
|
|
QSqlQuery query(db);
|
|
check(query.exec(QStringLiteral("SELECT element_key, linked_uuid FROM doc_link ORDER BY element_key, ord")),
|
|
"saved links read");
|
|
while (query.next())
|
|
links[query.value(0).toString()] << query.value(1).toString();
|
|
}
|
|
QSqlDatabase::removeDatabase(QStringLiteral("saved_links"));
|
|
QStringList rows;
|
|
for (auto it = links.cbegin() ; it != links.cend() ; ++it)
|
|
rows << it.key() + QStringLiteral(": ") + it.value().join(QLatin1Char(','));
|
|
return sortedLines(rows.join(QLatin1Char('\n')), path);
|
|
}
|
|
|
|
//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");
|
|
{
|
|
//Wire numbers exported from the database filled from the
|
|
//document, as when built (D5)
|
|
QETProject exported(path);
|
|
check(ConductorNumExport(&exported).wiresNum() == ConductorNumExport(&eager).wiresNum(),
|
|
"wire numbers of a .qet exported as when built");
|
|
const QString wiring = WiringListExport(&exported).toCsvString(), usual_wiring = WiringListExport(&eager).toCsvString();
|
|
if (wiring != usual_wiring) {
|
|
const QStringList a = wiring.split('\n'), b = usual_wiring.split('\n');
|
|
for (const QString &line : b) if (!a.contains(line)) std::printf(" wiring when built: %s\n", qPrintable(line));
|
|
for (const QString &line : a) if (!b.contains(line)) std::printf(" wiring lazily: %s\n", qPrintable(line));
|
|
std::printf(" %d lines lazily, %d when built\n", int(a.size()), int(b.size()));
|
|
}
|
|
check(wiring == usual_wiring, "wiring list of a .qet exported as when built");
|
|
check(exported.unloadedFolioCount() == folios.size() - strip_folios.size(),
|
|
"exporting a .qet's wire numbers and wiring list builds no folio");
|
|
}
|
|
//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");
|
|
}
|
|
//A .qetz opened from its index (stage D2): no
|
|
//folio read, each read from its part when shown, drawn and
|
|
//linked as when built, saved as the .qet is
|
|
{
|
|
const QString zipped = output + "/lazy_folios.qetz";
|
|
QString error;
|
|
check(eager.writeContainerFile(zipped, &error), "saved as a .qetz");
|
|
QETProject indexed(zipped);
|
|
check(indexed.state() == QETProject::Ok, "the .qetz opens");
|
|
check(indexed.openedFromIndex(), "the .qetz opens from its index");
|
|
std::printf(".qetz opened from its index: %d of %d folios not built (strip folios %d)\n",
|
|
indexed.unloadedFolioCount(), int(folios.size()), int(strip_folios.size()));
|
|
check(indexed.unloadedFolioCount() == folios.size() - strip_folios.size(),
|
|
"the .qetz opens building only the folios of the terminal strip's terminals");
|
|
check(numbering(indexed) == numbering(eager), "symbols' numbering from the index as when built");
|
|
for (int i = 0; i < folios.size(); ++i)
|
|
same(indexed, i, "shown from the .qetz");
|
|
check(indexed.toXml().toString() == eager.toXml().toString(), "the .qetz saves as when built");
|
|
//A folio whose table continues one on another folio, shown
|
|
//first: the index, not the folios' parts, says which folios
|
|
//its table is built with (D5)
|
|
int continued = 0;
|
|
for (int i = 0; i < folios.size(); ++i) {
|
|
bool continues = false;
|
|
for (QGraphicsItem *item : folios.at(i)->items())
|
|
if (item->type() == QetGraphicsTableItem::Type
|
|
&& !static_cast<QetGraphicsTableItem *>(item)->previousTableUuid().isNull())
|
|
continues = true;
|
|
if (!continues) continue;
|
|
QETProject fresh(zipped);
|
|
same(fresh, i, "a continued table shown first from the .qetz");
|
|
++continued;
|
|
}
|
|
check(continued > 0, "a folio whose table continues another");
|
|
|
|
//Numbering, the searches for symbols and the unused symbols
|
|
//are answered from the database: no folio file is read
|
|
//but those of the folios built (D5)
|
|
{
|
|
QETProject asked(zipped);
|
|
check(numbering(asked) == numbering(eager), "numbering of a .qetz as when built");
|
|
for (const QString &title : eager.elementAutoNum().keys())
|
|
check(gaps(asked, title) == gaps(eager, title), "a .qetz scheme's gaps as when built");
|
|
auto locations = [](const QList<ElementsLocation> &list) {
|
|
QStringList strings;
|
|
for (const ElementsLocation &location : list) strings << location.toString();
|
|
strings.sort();
|
|
return strings;
|
|
};
|
|
check(locations(asked.unusedElements()) == locations(eager.unusedElements()),
|
|
"a .qetz's unused symbols as when built");
|
|
const ElementProvider usual_search(&eager), search(&asked);
|
|
for (const ElementData::Types kinds : {ElementData::Types(ElementData::Slave),
|
|
ElementData::Types(ElementData::Master),
|
|
ElementData::Types(ElementData::AllReport)})
|
|
check(found(search.freeElement(kinds)) == found(usual_search.freeElement(kinds)),
|
|
"free symbols of a .qetz found as when built");
|
|
const int built = int(folios.size()) - asked.unloadedFolioCount();
|
|
std::printf(".qetz asked numbering, searches, unused symbols: %d folio files read, %d folios built\n",
|
|
asked.folioPartsRead(), built);
|
|
check(asked.unloadedFolioCount() > 0, "asking a .qetz does not build every folio");
|
|
check(asked.folioPartsRead() == built, "asking a .qetz reads no file of a folio not built");
|
|
}
|
|
|
|
//A folio added, moved and removed: no other folio built, the
|
|
//folios' rows as when every folio is built (D5)
|
|
{
|
|
auto shuffle = [](QETProject &project) {
|
|
Diagram *added = project.addNewDiagram();
|
|
project.diagramOrderChanged(1, 0);
|
|
project.removeDiagram(added);
|
|
};
|
|
auto folioRows = [](QETProject &project) {
|
|
QStringList rows;
|
|
for (const QString &row : tables(project))
|
|
if (row.startsWith("diagram")) rows << row;
|
|
return rows;
|
|
};
|
|
QETProject shuffled(zipped);
|
|
const int unbuilt = shuffled.unloadedFolioCount();
|
|
shuffle(shuffled);
|
|
std::printf(".qetz folio added, moved, removed: %d folios built\n",
|
|
unbuilt - shuffled.unloadedFolioCount());
|
|
check(shuffled.unloadedFolioCount() == unbuilt, "adding, moving and removing a folio builds no other");
|
|
qunsetenv("QET_LAZY_FOLIOS");
|
|
QETProject usual_shuffled(zipped);
|
|
qputenv("QET_LAZY_FOLIOS", "1");
|
|
shuffle(usual_shuffled);
|
|
check(folioRows(shuffled) == folioRows(usual_shuffled), "folios' rows after a move as when built");
|
|
}
|
|
|
|
//The tables a table can continue (its properties editor) and
|
|
//the wiring list's count of wires left out: only the folios
|
|
//holding a table built (D5)
|
|
{
|
|
QETProject listed(zipped);
|
|
const int unbuilt = listed.unloadedFolioCount();
|
|
auto tableUuids = [](const QVector<QetGraphicsTableItem *> &list) {
|
|
QStringList uuids;
|
|
for (QetGraphicsTableItem *table : list) uuids << table->uuid().toString();
|
|
uuids.sort();
|
|
return uuids;
|
|
};
|
|
const QStringList lazy_tables = tableUuids(ElementProvider(&listed).table(nullptr, nullptr));
|
|
check(!lazy_tables.isEmpty(), "the project has tables");
|
|
check(lazy_tables == tableUuids(ElementProvider(&eager).table(nullptr, nullptr)),
|
|
"the project's tables listed as when built");
|
|
check(listed.dataBase()->excludedConductorCount() == eager.dataBase()->excludedConductorCount(),
|
|
"wires left out of the wiring list counted as when built");
|
|
int with_tables = 0;
|
|
for (Diagram *folio : folios) {
|
|
for (QGraphicsItem *item : folio->items())
|
|
if (item->type() == QetGraphicsTableItem::Type) { ++with_tables; break; }
|
|
}
|
|
std::printf(".qetz tables listed: %d folios built, %d hold a table\n",
|
|
unbuilt - listed.unloadedFolioCount(), with_tables);
|
|
check(listed.unloadedFolioCount() > 0, "listing the tables does not build every folio");
|
|
}
|
|
|
|
//A script (and the assistant's live mode) asking about the
|
|
//folios and one folio's items: that folio built, no other (D5)
|
|
{
|
|
QETProject scripted(zipped);
|
|
const int unbuilt = scripted.unloadedFolioCount();
|
|
QetScriptApi api(&scripted), usual_api(&eager);
|
|
check(api.folioCount() == usual_api.folioCount(), "a script counts the folios as when built");
|
|
for (int i = 0 ; i < folios.size() ; ++i) {
|
|
check(api.folioTitle(i) == usual_api.folioTitle(i) && api.folioUuid(i) == usual_api.folioUuid(i)
|
|
&& api.folioBorder(i, "columns") == usual_api.folioBorder(i, "columns"),
|
|
"a script reads a folio's own data as when built");
|
|
}
|
|
check(scripted.unloadedFolioCount() == unbuilt, "reading the folios' own data builds none");
|
|
//The folio with the most links to other folios
|
|
const int target = busiest;
|
|
check(!scripted.folios().at(target)->isLoaded(), "a folio not built");
|
|
QStringList lazy_uuids = api.elementUuids(target), usual_uuids = usual_api.elementUuids(target);
|
|
lazy_uuids.sort(); usual_uuids.sort();
|
|
check(lazy_uuids == usual_uuids, "a script lists a folio's symbols as when built");
|
|
QStringList lazy_wires = api.conductors(target), usual_wires = usual_api.conductors(target);
|
|
lazy_wires.sort(); usual_wires.sort();
|
|
check(lazy_wires == usual_wires, "a script lists a folio's wires as when built");
|
|
check(scripted.folios().at(target)->isLoaded(), "the folio a script reads is built");
|
|
std::printf(".qetz script on folio %d: %d folios built\n", target + 1,
|
|
unbuilt - scripted.unloadedFolioCount());
|
|
check(unbuilt - scripted.unloadedFolioCount() == 1, "a script reading one folio builds that folio only");
|
|
}
|
|
|
|
//The wire numbers and wiring list exports, and the terminal
|
|
//numbering dialog: as when built, building only the folios
|
|
//holding a terminal (D5)
|
|
{
|
|
QETProject exported(zipped);
|
|
const int unbuilt = exported.unloadedFolioCount();
|
|
check(ConductorNumExport(&exported).wiresNum() == ConductorNumExport(&eager).wiresNum(),
|
|
"wire numbers exported as when built");
|
|
const QString wiring = WiringListExport(&exported).toCsvString(), usual_wiring = WiringListExport(&eager).toCsvString();
|
|
if (wiring != usual_wiring) {
|
|
const QStringList a = wiring.split('\n'), b = usual_wiring.split('\n');
|
|
for (const QString &line : b) if (!a.contains(line)) std::printf(" wiring when built: %s\n", qPrintable(line));
|
|
for (const QString &line : a) if (!b.contains(line)) std::printf(" wiring lazily: %s\n", qPrintable(line));
|
|
}
|
|
check(wiring == usual_wiring, "wiring list exported as when built");
|
|
check(exported.unloadedFolioCount() == unbuilt, "exporting wire numbers and the wiring list builds no folio");
|
|
{ TerminalNumberingDialog dialog(nullptr, &exported); }
|
|
int with_terminals = 0;
|
|
for (Diagram *folio : folios)
|
|
for (Element *element : folio->elements())
|
|
if (element->elementData().m_type == ElementData::Terminal) { ++with_terminals; break; }
|
|
std::printf(".qetz terminal numbering: %d folios built, %d hold a terminal\n",
|
|
unbuilt - exported.unloadedFolioCount(), with_terminals);
|
|
check(exported.folios().size() - exported.unloadedFolioCount() <= with_terminals + (folios.size() - unbuilt),
|
|
"terminal numbering builds only the folios holding a terminal");
|
|
}
|
|
|
|
//Saved as a .qetz without building the folios not touched
|
|
//(D4): their parts and rows copied as they were
|
|
QETProject untouched(zipped);
|
|
const int unbuilt = untouched.unloadedFolioCount();
|
|
const QString kept_path = output + "/kept.qetz";
|
|
check(untouched.writeContainerFile(kept_path, &error), "saved as a .qetz without building");
|
|
std::printf(".qetz saved untouched: %d of %d folios not built before, %d after\n",
|
|
unbuilt, int(folios.size()), untouched.unloadedFolioCount());
|
|
check(untouched.unloadedFolioCount() > 0, "saving an untouched .qetz does not build every folio");
|
|
qunsetenv("QET_LAZY_FOLIOS");
|
|
QETProject kept_back(kept_path);
|
|
qputenv("QET_LAZY_FOLIOS", "1");
|
|
check(kept_back.toXml().toString() == eager.toXml().toString(),
|
|
"a .qetz saved without building holds the project");
|
|
|
|
//After everyday edits on a folio
|
|
QETProject edited_z(zipped);
|
|
edit(edited_z, busiest);
|
|
const QString edited_path = output + "/edited.qetz";
|
|
check(edited_z.writeContainerFile(edited_path, &error), "edited .qetz saved");
|
|
std::printf(".qetz saved after the edits: %d folios not built\n", edited_z.unloadedFolioCount());
|
|
check(edited_z.unloadedFolioCount() > 0, "saving an edited .qetz does not build every folio");
|
|
//Against the same edits made with every folio built, saved and
|
|
//read back too: what a save and a reading do to a project is
|
|
//not what is checked here
|
|
const QString usual_path = output + "/edited-usual.qetz";
|
|
qunsetenv("QET_LAZY_FOLIOS");
|
|
{
|
|
QETProject edited_usual(path);
|
|
edit(edited_usual, busiest);
|
|
check(edited_usual.writeContainerFile(usual_path, &error), "edits saved with every folio built");
|
|
}
|
|
QETProject edited_back(edited_path), usual_back(usual_path);
|
|
qputenv("QET_LAZY_FOLIOS", "1");
|
|
check(sortedLines(edited_back.toXml().toString(), path) == sortedLines(usual_back.toXml().toString(), path),
|
|
"an edited .qetz saved without building holds what the edits made");
|
|
//Each symbol's links as saved, partners on folios not built
|
|
//included, in their order (D5a)
|
|
check(savedLinks(edited_path, path) == savedLinks(usual_path, path),
|
|
"an edited .qetz saved without building keeps every symbol's links");
|
|
}
|
|
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;
|
|
}
|