Files
qelectrotech-source-mirror/tests/qttest/tst_settingsdir.cpp
T
ispyisail 65a1080cd3 Keep headless runs' settings in a folder of their own (QET_SETTINGS_DIR)
The MCP server gives each QElectroTech run a private HOME and writes the
element collection path for it (elements_dir) into
~/.config/QElectroTech/QElectroTech.conf there. Only Linux reads that
file: Qt keeps settings in the registry on Windows and in the system
preferences on macOS. So on those systems elements_dir did nothing, and
every run read the user's own settings (#1178).

When QET_SETTINGS_DIR names a folder, QElectroTech now keeps its
settings in <folder>/QElectroTech/QElectroTech.ini on every system. It is
set in main() before the first setting is read. Without the variable
nothing changes.

The server sets it for each run and writes the collection path to the
.ini as well as the .conf, so an older QElectroTech keeps working on
Linux. The path is written with forward slashes: Qt reads a backslash in
these files as an escape. The README notes that elements_dir needs this
on Windows and macOS, and that long arguments go on stdin with "-"
(Windows refuses a command line over 32,767 characters).

tst_settingsdir: a script places a symbol that only the folder's settings
can resolve, while the usual settings file points at an empty collection.
It fails on master and passes here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:00:55 +13:00

113 lines
4.3 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
#include <QtTest>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QProcessEnvironment>
#include <QRegularExpression>
#include <QTemporaryDir>
// QET_SETTINGS_DIR makes QElectroTech keep its settings in an INI file in
// that folder (issue #1178). On Windows and macOS that is the only way to
// give a headless run its own settings: HOME and XDG_CONFIG_HOME move them
// on Linux alone. Checked here on Linux by making the usual settings file
// point at an empty collection and the one in QET_SETTINGS_DIR at a
// collection holding the symbol a script places: the symbol resolves only
// if the folder's file is the one read.
class tst_settingsdir : public QObject
{
Q_OBJECT
QTemporaryDir m_dir;
static void writeCollectionSetting(const QString &file, const QString &collection)
{
QDir().mkpath(QFileInfo(file).path());
QFile f(file);
QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Text));
f.write("[elements-collections]\ncommon-collection-path=");
f.write(collection.toUtf8());
f.write("\n");
}
// Runs a script placing common://custom/my_siren.elmt and returns what it
// logged: "resolved", "not resolved", or empty if the run itself failed.
QString placeTheSymbol(bool with_settings_dir)
{
const QString root = m_dir.filePath(with_settings_dir ? QStringLiteral("with")
: QStringLiteral("without"));
const QString home = root + QStringLiteral("/home");
const QString settings = root + QStringLiteral("/settings");
const QString collection = root + QStringLiteral("/collection");
const QString empty_collection = root + QStringLiteral("/empty");
QDir().mkpath(collection + QStringLiteral("/custom"));
QDir().mkpath(empty_collection);
QDir().mkpath(root + QStringLiteral("/tmp"));
if (!QFile::copy(QStringLiteral(QET_ELEMENTS_DIR "/10_electric/10_allpole/380_signaling_operating/12_acoustic_signaling/sirene.elmt"),
collection + QStringLiteral("/custom/my_siren.elmt")))
return QString();
// The usual place (Linux): an empty collection. The folder: the right one.
writeCollectionSetting(home + QStringLiteral("/.config/QElectroTech/QElectroTech.conf"),
empty_collection);
writeCollectionSetting(settings + QStringLiteral("/QElectroTech/QElectroTech.ini"),
collection);
const QString project = root + QStringLiteral("/p.qet");
QFile::copy(QFINDTESTDATA("fixtures/qet_bug_repro_resaved.qet"), project);
const QString script = root + QStringLiteral("/add.js");
QFile js(script);
if (!js.open(QIODevice::WriteOnly))
return QString();
js.write("var u = qet.addElement(0, 'common://custom/my_siren.elmt', 100, 100);\n"
"qet.log('RESULT ' + (u ? 'resolved' : 'not resolved'));\n");
js.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("/.local/share"));
env.insert(QStringLiteral("TMPDIR"), root + QStringLiteral("/tmp"));
if (with_settings_dir)
env.insert(QStringLiteral("QET_SETTINGS_DIR"), settings);
else
env.remove(QStringLiteral("QET_SETTINGS_DIR"));
QProcess proc;
proc.setProcessEnvironment(env);
proc.setProcessChannelMode(QProcess::MergedChannels);
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--run"), script, project});
if (!proc.waitForFinished(60000))
return QString();
const QString out = QString::fromUtf8(proc.readAll());
static const QRegularExpression result(QStringLiteral("RESULT ([a-z ]+)"));
return result.match(out).captured(1).trimmed();
}
private slots:
void initTestCase()
{
QVERIFY(m_dir.isValid());
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
}
void theFolderIsRead()
{
QCOMPARE(placeTheSymbol(true), QStringLiteral("resolved"));
}
// Without the variable nothing changes: the usual file is read, and it
// points at the empty collection.
void withoutItTheUsualPlaceIsRead()
{
QCOMPARE(placeTheSymbol(false), QStringLiteral("not resolved"));
}
};
QTEST_APPLESS_MAIN(tst_settingsdir)
#include "tst_settingsdir.moc"