mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-17 19:34:13 +02:00
e8a5d6aec0
Saving replaced the project file whatever had happened to it since it was opened. If another user, another program or a second QET had saved the same file in between (a shared drive, a copy synced from another PC), their work was lost without a word. QETProject now remembers the project file's size and modification time when it reads it and after each save of its own. Before writing, write() compares them with the file on disk and, if they differ, writes nothing and returns "someone else changed the file on disk at <time> since it was opened". - Interactive save (Ctrl+S, and the save on closing a project) asks: "Someone else changed <file> on disk at <time> since you opened it" with Save as a copy (opens Save As), Overwrite, or Cancel. - Scripts (--run), the command line, MCP and the autosave timer have no one to ask, so they keep the refusal (autosave logs it). Setting QET_OVERWRITE_CHANGED=1 makes a script overwrite on purpose. - A file that does not exist yet, and a Save As to another name, are not a conflict; after Save As the remembered state is the new file's. No change to what is written, so .qet and .qetz stay as they were. Evidence: new tst_changedondisk (unchanged file saves twice; a file written behind the open project is refused; the override overwrites; a Save As target is no conflict) fails on the changed-file case without the source change and passes with it. Full ctest 107/107. Physical check with qet-gui-dialog.sh: file modified behind the open project, Ctrl+S shows the three-button dialog. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgPFANhWgD66V3v9KVKV58
108 lines
4.2 KiB
C++
108 lines
4.2 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
#include <QtTest>
|
|
|
|
#include <QFile>
|
|
#include <QProcess>
|
|
#include <QProcessEnvironment>
|
|
#include <QTemporaryDir>
|
|
|
|
// Saving a project replaced the file whatever had happened to it since it was
|
|
// opened: another user's save, or another program's, was lost without a word.
|
|
// QETProject now remembers the file as it read or wrote it and write() refuses
|
|
// when the file on disk is no longer that one. Driven through the real
|
|
// binary's --run: qet.save(<the project's own path>) writes the file behind
|
|
// the open project without updating what it remembers, which is exactly "someone
|
|
// else saved"; qet.save('') is the project's own save.
|
|
class tst_changedondisk : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
QTemporaryDir m_dir;
|
|
QString m_project; // a .qetz made from examples/741.qet
|
|
|
|
QString run(const QString &script, const QString &project, bool overwrite = false)
|
|
{
|
|
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
|
const QString home = m_dir.filePath(QStringLiteral("home"));
|
|
QDir().mkpath(home);
|
|
QFile f(path);
|
|
if (!f.open(QIODevice::WriteOnly)) return QString();
|
|
f.write(script.toUtf8());
|
|
f.close();
|
|
|
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
|
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
|
env.insert(QStringLiteral("HOME"), home);
|
|
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
|
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
|
env.insert(QStringLiteral("TMPDIR"), m_dir.path());
|
|
env.remove(QStringLiteral("QET_OVERWRITE_CHANGED"));
|
|
if (overwrite)
|
|
env.insert(QStringLiteral("QET_OVERWRITE_CHANGED"), QStringLiteral("1"));
|
|
QProcess proc;
|
|
proc.setProcessEnvironment(env);
|
|
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
|
{QStringLiteral("--run"), path, project});
|
|
if (!proc.waitForFinished(120000)) return QString();
|
|
return QString::fromUtf8(proc.readAllStandardOutput() + proc.readAllStandardError());
|
|
}
|
|
|
|
private slots:
|
|
void initTestCase()
|
|
{
|
|
QVERIFY(m_dir.isValid());
|
|
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
|
const QString qet = m_dir.filePath(QStringLiteral("p.qet"));
|
|
QVERIFY(QFile::copy(QStringLiteral(QET_EXAMPLES_DIR "/741.qet"), qet));
|
|
// a .qet is saved as a .qetz beside it; that .qetz is the project below
|
|
const QString out = run(QStringLiteral("qet.log('R ' + qet.save(''));\n"), qet);
|
|
QVERIFY2(out.contains(QStringLiteral("R true")), qPrintable(out));
|
|
m_project = m_dir.filePath(QStringLiteral("p.qetz"));
|
|
QVERIFY(QFile::exists(m_project));
|
|
}
|
|
|
|
void unchangedFileSaves()
|
|
{
|
|
const QString out = run(QStringLiteral(
|
|
"qet.log('R1 ' + qet.save(''));\nqet.log('R2 ' + qet.save(''));\n"), m_project);
|
|
QVERIFY2(out.contains(QStringLiteral("R1 true")), qPrintable(out));
|
|
// the project's own save updates what it remembers: a second one is no change
|
|
QVERIFY2(out.contains(QStringLiteral("R2 true")), qPrintable(out));
|
|
}
|
|
|
|
void changedFileIsNotOverwritten()
|
|
{
|
|
const QString script = QStringLiteral(
|
|
"qet.save(%1);\nqet.log('R ' + qet.save(''));\n")
|
|
.arg(QStringLiteral("'") + m_project + QStringLiteral("'"));
|
|
const QString out = run(script, m_project);
|
|
QVERIFY2(out.contains(QStringLiteral("R false")), qPrintable(out));
|
|
QVERIFY2(out.contains(QStringLiteral("changed the file on disk")), qPrintable(out));
|
|
}
|
|
|
|
void overrideOverwrites()
|
|
{
|
|
const QString script = QStringLiteral(
|
|
"qet.save(%1);\nqet.log('R ' + qet.save(''));\n")
|
|
.arg(QStringLiteral("'") + m_project + QStringLiteral("'"));
|
|
const QString out = run(script, m_project, true);
|
|
QVERIFY2(out.contains(QStringLiteral("R true")), qPrintable(out));
|
|
}
|
|
|
|
void saveAsTargetIsNotAConflict()
|
|
{
|
|
const QString other = m_dir.filePath(QStringLiteral("other.qetz"));
|
|
const QString script = QStringLiteral(
|
|
"qet.log('R1 ' + qet.save(%1));\nqet.log('R2 ' + qet.save(%1));\n")
|
|
.arg(QStringLiteral("'") + other + QStringLiteral("'"));
|
|
const QString out = run(script, m_project);
|
|
QVERIFY2(out.contains(QStringLiteral("R1 true")), qPrintable(out));
|
|
QVERIFY2(out.contains(QStringLiteral("R2 true")), qPrintable(out));
|
|
}
|
|
};
|
|
|
|
QTEST_APPLESS_MAIN(tst_changedondisk)
|
|
|
|
#include "tst_changedondisk.moc"
|