diff options
author | Ivan Komissarov <abbapoh@gmail.com> | 2023-05-31 22:14:03 +0300 |
---|---|---|
committer | Ivan Komissarov <ABBAPOH@gmail.com> | 2023-06-16 12:44:24 +0000 |
commit | 6acac88c0b6fecd58b5eb74669ba9a98d7ab020f (patch) | |
tree | 7392b83b0cada7f40619e9377c59999f6b42d6f5 | |
parent | 8bbc32480244e3dbb5ef2aef338003a1af7e5200 (diff) |
Allow exporting/importing settings to/from JSON
The format is determined by the file extension.
Fixes: QBS-1685
Change-Id: I1544fc7c440a4be0d2d0b8013bd7d5994c6d1288
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r-- | src/app/config/configcommandexecutor.cpp | 85 | ||||
-rw-r--r-- | src/lib/corelib/tools/settings.cpp | 21 | ||||
-rw-r--r-- | src/lib/corelib/tools/settings.h | 2 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/qbs-config-import-export/config.json | 11 | ||||
-rw-r--r-- | tests/auto/blackbox/testdata/qbs-config-import-export/config.txt | 4 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.cpp | 90 | ||||
-rw-r--r-- | tests/auto/blackbox/tst_blackbox.h | 4 |
7 files changed, 195 insertions, 22 deletions
diff --git a/src/app/config/configcommandexecutor.cpp b/src/app/config/configcommandexecutor.cpp index 023514980..cbcb5ee73 100644 --- a/src/app/config/configcommandexecutor.cpp +++ b/src/app/config/configcommandexecutor.cpp @@ -49,11 +49,52 @@ #include <QtCore/qdir.h> #include <QtCore/qfile.h> #include <QtCore/qtextstream.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> #include <cstdio> using namespace qbs; +static QJsonObject settingsToJSONObject( + Settings &settings, qbs::Settings::Scopes scopes, const QString &parentGroup = {}) +{ + QJsonObject result; + + const auto allKeys = settings.directChildren(parentGroup, scopes); + for (const auto& key : allKeys) { + const auto fullKey = parentGroup.isEmpty() + ? key + : QStringLiteral("%1.%2").arg(parentGroup, key); + const auto value = settings.value(fullKey, scopes); + if (value.isValid()) { // looks like a real value + result[key] = QJsonValue::fromVariant(value); + } else { // looks like a group + result[key] = settingsToJSONObject(settings, scopes, fullKey); + } + } + + return result; +} + +static void settingsFromJSONObject( + Settings &settings, const QJsonObject &object, const QString &parentGroup = {}) +{ + for (auto it = object.begin(), end = object.end(); it != end; ++it) { + const auto key = it.key(); + const auto value = it.value(); + const auto fullKey = parentGroup.isEmpty() + ? key + : QStringLiteral("%1.%2").arg(parentGroup, key); + if (value.isObject()) { + settingsFromJSONObject(settings, it.value().toObject(), fullKey); + } else { + settings.setValue(fullKey, value.toVariant()); + } + } +} + + ConfigCommandExecutor::ConfigCommandExecutor(Settings *settings, Settings::Scopes scope) : m_settings(settings), m_scope(scope) { @@ -139,12 +180,20 @@ void ConfigCommandExecutor::exportSettings(const QString &filename) throw ErrorInfo(tr("Could not open file '%1' for writing: %2") .arg(QDir::toNativeSeparators(filename), file.errorString())); } - QTextStream stream(&file); - setupDefaultCodec(stream); - const auto keys = m_settings->allKeys(m_scope); - for (const QString &key : keys) - stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope)) - << Qt::endl; + + if (QFileInfo(filename).suffix() == u"json") { + QJsonDocument doc; + doc.setObject(settingsToJSONObject(*m_settings, m_scope)); + file.write(doc.toJson()); + } else { + QTextStream stream(&file); + setupDefaultCodec(stream); + const auto keys = m_settings->allKeys(m_scope); + for (const QString &key : keys) + stream << key << ": " + << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope)) + << Qt::endl; + } } void ConfigCommandExecutor::importSettings(const QString &filename) @@ -159,15 +208,21 @@ void ConfigCommandExecutor::importSettings(const QString &filename) for (const QString &key : keys) m_settings->remove(key); - QTextStream stream(&file); - setupDefaultCodec(stream); - while (!stream.atEnd()) { - QString line = stream.readLine(); - int colon = line.indexOf(QLatin1Char(':')); - if (colon >= 0 && !line.startsWith(QLatin1Char('#'))) { - const QString key = line.left(colon).trimmed(); - const QString value = line.mid(colon + 1).trimmed(); - m_settings->setValue(key, representationToSettingsValue(value)); + if (QFileInfo(filename).suffix() == u"json") { + const auto doc = QJsonDocument::fromJson(file.readAll()); + const auto object = doc.object(); + settingsFromJSONObject(*m_settings, doc.object()); + } else { + QTextStream stream(&file); + setupDefaultCodec(stream); + while (!stream.atEnd()) { + QString line = stream.readLine(); + int colon = line.indexOf(QLatin1Char(':')); + if (colon >= 0 && !line.startsWith(QLatin1Char('#'))) { + const QString key = line.left(colon).trimmed(); + const QString value = line.mid(colon + 1).trimmed(); + m_settings->setValue(key, representationToSettingsValue(value)); + } } } } diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp index 8b22c45e5..4a3d9d727 100644 --- a/src/lib/corelib/tools/settings.cpp +++ b/src/lib/corelib/tools/settings.cpp @@ -135,13 +135,22 @@ QStringList Settings::allKeys(Scopes scopes) const return keys; } -QStringList Settings::directChildren(const QString &parentGroup, Scope scope) const +QStringList Settings::directChildren(const QString &parentGroup, Scopes scopes) const { - QSettings * const settings = settingsForScope(scope); - settings->beginGroup(internalRepresentation(parentGroup)); - QStringList children = settings->childGroups(); - children << settings->childKeys(); - settings->endGroup(); + auto helper = [this, &parentGroup](const Scope scope) { + QSettings * const settings = settingsForScope(scope); + settings->beginGroup(internalRepresentation(parentGroup)); + QStringList children = settings->childGroups(); + children << settings->childKeys(); + settings->endGroup(); + return children; + }; + + QStringList children; + if (scopes & UserScope) + children += helper(UserScope); + if (scopes & SystemScope) + children += helper(SystemScope); fixupKeys(children); return children; } diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h index 4ea148af0..7c9c52c9e 100644 --- a/src/lib/corelib/tools/settings.h +++ b/src/lib/corelib/tools/settings.h @@ -70,7 +70,7 @@ public: QVariant value(const QString &key, Scopes scopes, const QVariant &defaultValue = QVariant()) const; QStringList allKeys(Scopes scopes) const; - QStringList directChildren(const QString &parentGroup, Scope scope) const; // Keys and groups. + QStringList directChildren(const QString &parentGroup, Scopes scopes) const; // Keys and groups. QStringList allKeysWithPrefix(const QString &group, Scopes scopes) const; void setValue(const QString &key, const QVariant &value); void remove(const QString &key); diff --git a/tests/auto/blackbox/testdata/qbs-config-import-export/config.json b/tests/auto/blackbox/testdata/qbs-config-import-export/config.json new file mode 100644 index 000000000..edcaf238a --- /dev/null +++ b/tests/auto/blackbox/testdata/qbs-config-import-export/config.json @@ -0,0 +1,11 @@ +{ + "group": { + "key1": "value1", + "key2": "value2" + }, + "key": "value", + "listKey": [ + "valueOne", + "valueTwo" + ] +} diff --git a/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt b/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt new file mode 100644 index 000000000..2bd19c422 --- /dev/null +++ b/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt @@ -0,0 +1,4 @@ +group.key1: "value1" +group.key2: "value2" +key: "value" +listKey: ["valueOne", "valueTwo"] diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 424d08642..4177143be 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -6105,6 +6105,10 @@ void TestBlackbox::qbsConfig() if (!canWriteToSystemSettings) { QVERIFY2(m_qbsStderr.contains("You do not have permission to write to that location."), m_qbsStderr.constData()); + } else { + // cleanup system settings + params.arguments = QStringList{"--system", "--unset", "key.subkey.scalar"}; + QCOMPARE(runQbs(params) == 0, canWriteToSystemSettings); } } @@ -6169,6 +6173,92 @@ void TestBlackbox::qbsConfigAddProfile_data() << QString("Profile properties must be key/value pairs"); } +void TestBlackbox::qbsConfigImport() +{ + QFETCH(QString, format); + + QDir::setCurrent(testDataDir + "/qbs-config-import-export"); + + QbsRunParameters params("config"); + QTemporaryDir settingsDir; + QVERIFY(settingsDir.isValid()); + const QStringList settingsDirArgs = QStringList{"--settings-dir", settingsDir.path()}; + params.arguments = settingsDirArgs; + params.arguments << "--import" << "config." + format; + + QCOMPARE(runQbs(params), 0); + + params.arguments = settingsDirArgs; + params.arguments << "--list"; + QCOMPARE(runQbs(params), 0); + const QByteArray output = m_qbsStdout; + const auto lines = output.split('\n'); + QCOMPARE(lines.count(), 5); + QCOMPARE(lines[0], "group.key1: \"value1\""); + QCOMPARE(lines[1], "group.key2: \"value2\""); + QCOMPARE(lines[2], "key: \"value\""); + QCOMPARE(lines[3], "listKey: [\"valueOne\", \"valueTwo\"]"); + QCOMPARE(lines[4], ""); +} + +void TestBlackbox::qbsConfigImport_data() +{ + QTest::addColumn<QString>("format"); + + QTest::newRow("text") << QStringLiteral("txt"); + QTest::newRow("json") << QStringLiteral("json"); +} + +void TestBlackbox::qbsConfigExport() +{ + QFETCH(QString, format); + + QDir::setCurrent(testDataDir + "/qbs-config-import-export"); + + QbsRunParameters params("config"); + QTemporaryDir settingsDir; + const QString fileName = "config." + format; + const QString filePath = settingsDir.path() + "/" + fileName; + QVERIFY(settingsDir.isValid()); + const QStringList commonArgs = QStringList{"--settings-dir", settingsDir.path(), "--user"}; + + std::pair<QString, QString> values[] = { + {"key", "value"}, + {"listKey", "[\"valueOne\",\"valueTwo\"]"}, + {"group.key1", "value1"}, + {"group.key2", "value2"} + }; + + for (const auto &value: values) { + params.arguments = commonArgs; + params.arguments << value.first << value.second; + QCOMPARE(runQbs(params), 0); + } + + params.arguments = commonArgs; + params.arguments << "--export" << filePath; + + QCOMPARE(runQbs(params), 0); + + QVERIFY(QFileInfo(filePath).canonicalPath() != QFileInfo(fileName).canonicalPath()); + + QFile exported(filePath); + QFile expected(fileName); + + QVERIFY(exported.open(QIODevice::ReadOnly | QIODevice::Text)); + QVERIFY(expected.open(QIODevice::ReadOnly | QIODevice::Text)); + + QCOMPARE(exported.readAll(), expected.readAll()); +} + +void TestBlackbox::qbsConfigExport_data() +{ + QTest::addColumn<QString>("format"); + + QTest::newRow("text") << QStringLiteral("txt"); + QTest::newRow("json") << QStringLiteral("json"); +} + static QJsonObject getNextSessionPacket(QProcess &session, QByteArray &data) { int totalSize = -1; diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index 4f8e898aa..e405c3c0e 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -261,6 +261,10 @@ private slots: void qbsConfig(); void qbsConfigAddProfile(); void qbsConfigAddProfile_data(); + void qbsConfigImport(); + void qbsConfigImport_data(); + void qbsConfigExport(); + void qbsConfigExport_data(); void qbsSession(); void qbsVersion(); void qtBug51237(); |