diff options
Diffstat (limited to 'tests/auto/corelib/io/qsettings/tst_qsettings.cpp')
-rw-r--r-- | tests/auto/corelib/io/qsettings/tst_qsettings.cpp | 571 |
1 files changed, 329 insertions, 242 deletions
diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 732d81d84a..f4d7f076ef 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -1,52 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtCore/QSettings> #include <private/qsettings_p.h> + +#include "tst_qmetatype_common.h" + #include <QtCore/QCoreApplication> -#include <QtCore/QDateTime> -#include <QtCore/QtGlobal> -#include <QtCore/QMetaType> -#include <QtCore/QString> #include <QtCore/QDir> +#include <QtCore/QEventLoop> +#include <QtCore/QtGlobal> #include <QtCore/QThread> #include <QtCore/QSysInfo> #if QT_CONFIG(shortcut) # include <QtGui/QKeySequence> #endif -#include <QtCore> -#include <QtGui> -#include "tst_qmetatype_common.h" - #include <cctype> #include <stdlib.h> #if defined(Q_OS_WIN) && defined(Q_CC_GNU) @@ -57,14 +28,27 @@ #if defined(Q_OS_WIN) #include <QtCore/qt_windows.h> #include <private/qwinregistry_p.h> +#define QT_UNLINK _unlink #else #include <unistd.h> +#define QT_UNLINK unlink #endif #if defined(Q_OS_DARWIN) #include <CoreFoundation/CoreFoundation.h> #endif +#ifdef Q_OS_INTEGRITY +#include "qplatformdefs.h" +#endif + +#if defined(Q_OS_WASM) +#include <QtCore/private/qstdweb_p.h> + +#include "emscripten/threading.h" +#include "emscripten/val.h" +#endif + Q_DECLARE_METATYPE(QSettings::Format) #ifndef QSETTINGS_P_H_VERSION @@ -102,6 +86,20 @@ static inline bool canWriteNativeSystemSettings() static const char insufficientPermissionSkipMessage[] = "Insufficient permissions for this test."; +static void populateWithFormats() +{ + QTest::addColumn<QSettings::Format>("format"); + + QTest::newRow("native") << QSettings::NativeFormat; +#if defined(Q_OS_WASM) + if (qstdweb::haveJspi()) + QTest::newRow("idb") << QSettings::WebIndexedDBFormat; +#endif // defined(Q_OS_WASM) + QTest::newRow("ini") << QSettings::IniFormat; + QTest::newRow("custom1") << QSettings::CustomFormat1; + QTest::newRow("custom2") << QSettings::CustomFormat2; +} + class tst_QSettings : public QObject { Q_OBJECT @@ -114,8 +112,11 @@ public slots: void cleanup() { cleanupTestFiles(); } private slots: void getSetCheck(); - void ctor_data(); + void ctor_data() { populateWithFormats(); } void ctor(); +#ifdef Q_OS_WASM + void idb(); +#endif void beginGroup(); void setValue(); void remove(); @@ -126,16 +127,16 @@ private slots: void syncAlternateDataStream(); #endif void setFallbacksEnabled(); - void setFallbacksEnabled_data(); - void fromFile_data(); + void setFallbacksEnabled_data() { populateWithFormats(); } + void fromFile_data() { populateWithFormats(); } void fromFile(); - void testArrays_data(); + void testArrays_data() { populateWithFormats(); } void testArrays(); - void testCaseSensitivity_data(); + void testCaseSensitivity_data() { populateWithFormats(); } void testCaseSensitivity(); void testErrorHandling_data(); void testErrorHandling(); - void testChildKeysAndGroups_data(); + void testChildKeysAndGroups_data() { populateWithFormats(); } void testChildKeysAndGroups(); void testUpdateRequestEvent(); void testThreadSafety(); @@ -145,10 +146,10 @@ private slots: void testRegistryShortRootNames(); void testRegistry32And64Bit(); void trailingWhitespace(); -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN void fileName(); #endif - void isWritable_data(); + void isWritable_data() { populateWithFormats(); } void isWritable(); void registerFormat(); void setPath(); @@ -162,23 +163,23 @@ private slots: #endif #ifdef QT_BUILD_INTERNAL - void allKeys_data(); + void allKeys_data() { populateWithFormats(); } void allKeys(); - void childGroups_data(); + void childGroups_data() { populateWithFormats(); } void childGroups(); - void childKeys_data(); + void childKeys_data() { populateWithFormats(); } void childKeys(); void testIniParsing_data(); void testIniParsing(); void testEscapes(); void testNormalizedKey_data(); void testNormalizedKey(); - void testVariantTypes_data(); + void testVariantTypes_data() { populateWithFormats(); } void testVariantTypes(); void testMetaTypes_data(); void testMetaTypes(); #endif - void rainersSyncBugOnMac_data(); + void rainersSyncBugOnMac_data() { populateWithFormats(); } void rainersSyncBugOnMac(); void recursionBug(); @@ -192,6 +193,10 @@ private slots: void floatAsQVariant(); void testXdg(); + + void testReadKeys_data(); + void testReadKeys(); + private: void cleanupTestFiles(); @@ -274,16 +279,6 @@ static bool writeCustom3File(QIODevice &device, const QSettings::SettingsMap &ma return true; } -static void populateWithFormats() -{ - QTest::addColumn<QSettings::Format>("format"); - - QTest::newRow("native") << QSettings::NativeFormat; - QTest::newRow("ini") << QSettings::IniFormat; - QTest::newRow("custom1") << QSettings::CustomFormat1; - QTest::newRow("custom2") << QSettings::CustomFormat2; -} - tst_QSettings::tst_QSettings() : m_canWriteNativeSystemSettings(canWriteNativeSystemSettings()) { @@ -343,6 +338,32 @@ void tst_QSettings::cleanupTestFiles() QSettings(QSettings::UserScope, "other.software.org").clear(); QSettings(QSettings::SystemScope, "other.software.org").clear(); #endif +#if defined(Q_OS_WASM) + emscripten::val::global("window")["localStorage"].call<void>("clear"); + if (qstdweb::haveJspi()) { + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::UserScope, "software.org", + "KillerAPP") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::SystemScope, "software.org", + "KillerAPP") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::UserScope, "other.software.org", + "KillerAPP") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::SystemScope, + "other.software.org", "KillerAPP") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::UserScope, "software.org") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::SystemScope, "software.org") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::UserScope, "other.software.org") + .clear(); + QSettings(QSettings::Format::WebIndexedDBFormat, QSettings::SystemScope, + "other.software.org") + .clear(); + } +#endif const QString foo(QLatin1String("foo")); @@ -355,11 +376,6 @@ void tst_QSettings::cleanupTestFiles() Test the constructors and the assignment operator. */ -void tst_QSettings::ctor_data() -{ - populateWithFormats(); -} - void tst_QSettings::ctor() { QFETCH(QSettings::Format, format); @@ -522,10 +538,10 @@ void tst_QSettings::ctor() QSettings settings5(format, QSettings::UserScope, "SoftWare.ORG", "killerApp"); bool caseSensitive = true; -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) if (format == QSettings::NativeFormat) { // more details in QMacSettingsPrivate::QMacSettingsPrivate(), organization was comify()-ed - caseSensitive = settings5.fileName().contains("SoftWare.ORG");; + caseSensitive = settings5.fileName().contains("SoftWare.ORG"); } else { caseSensitive = pathconf(settings5.fileName().toLatin1().constData(), _PC_CASE_SENSITIVE); } @@ -575,7 +591,7 @@ void tst_QSettings::ctor() QCOMPARE(settings1.value("alpha/beta/geometry/width").toInt(), 3); QCOMPARE(settings1.value("alpha/beta/geometry/height").toInt(), 4); QCOMPARE(settings1.value("alpha/gamma/splitter").toInt(), 5); - QCOMPARE(settings1.allKeys().count(), 6); + QCOMPARE(settings1.allKeys().size(), 6); QCOMPARE(settings2.value("alpha/beta/geometry").toInt(), -7); QCOMPARE(settings2.value("alpha/beta/geometry/x").toInt(), 1); @@ -583,7 +599,7 @@ void tst_QSettings::ctor() QCOMPARE(settings2.value("alpha/beta/geometry/width").toInt(), 3); QCOMPARE(settings2.value("alpha/beta/geometry/height").toInt(), 4); QCOMPARE(settings2.value("alpha/gamma/splitter").toInt(), 5); - QCOMPARE(settings2.allKeys().count(), 6); + QCOMPARE(settings2.allKeys().size(), 6); } { @@ -594,7 +610,7 @@ void tst_QSettings::ctor() QCOMPARE(settings1.value("alpha/beta/geometry/width").toInt(), 3); QCOMPARE(settings1.value("alpha/beta/geometry/height").toInt(), 4); QCOMPARE(settings1.value("alpha/gamma/splitter").toInt(), 5); - QCOMPARE(settings1.allKeys().count(), 6); + QCOMPARE(settings1.allKeys().size(), 6); } { @@ -603,7 +619,7 @@ void tst_QSettings::ctor() QCoreApplication::instance()->setOrganizationName(""); QCoreApplication::instance()->setApplicationName(""); QSettings settings; -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) QEXPECT_FAIL("native", "Default settings on Mac are valid, despite organization domain, name, and app name being null", Continue); #endif QCOMPARE(settings.status(), QSettings::AccessError); @@ -618,7 +634,7 @@ void tst_QSettings::ctor() } QSettings settings(format, QSettings::UserScope, "", ""); -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) QEXPECT_FAIL("native", "Default settings on Mac are valid, despite organization domain, name, and app name being null", Continue); #endif QCOMPARE(settings.status(), QSettings::AccessError); @@ -643,6 +659,50 @@ void tst_QSettings::ctor() } } +#if defined(Q_OS_WASM) +void tst_QSettings::idb() +{ + if (!qstdweb::haveJspi()) + QSKIP("JSPI needed for IndexedDB format"); + + QString systemScopeOrganizationWideFile; + { + QSettings settingsUserScopeAppSpecific(QSettings::Format::WebIndexedDBFormat, + QSettings::UserScope, "software.org", "KillerAPP"); + QSettings settingsUserScopeOrganizationWide(QSettings::Format::WebIndexedDBFormat, + QSettings::UserScope, "software.org"); + QSettings settingsSystemScopeAppSpecific(QSettings::Format::WebIndexedDBFormat, + QSettings::SystemScope, "software.org", + "KillerAPP"); + QSettings settingsSystemScopeOrganizationWide(QSettings::Format::WebIndexedDBFormat, + QSettings::SystemScope, "software.org"); + + settingsSystemScopeOrganizationWide.setValue("testKey", 1); + systemScopeOrganizationWideFile = settingsSystemScopeOrganizationWide.fileName(); + } + + // Emscripten's memfs has a bug that makes a file appear twice in the hashmap. + while (QFile::exists(systemScopeOrganizationWideFile)) { + Q_ASSERT(QFile::remove(systemScopeOrganizationWideFile)); + } + + QEventLoop loop; + QTimer timer; + timer.setInterval(1); + + connect(&timer, &QTimer::timeout, [&loop]() { loop.quit(); }); + timer.start(); + + loop.exec(); + { + QSettings settingsUserScopeAppSpecific(QSettings::Format::WebIndexedDBFormat, + QSettings::UserScope, "software.org", "KillerAPP"); + + QCOMPARE(settingsUserScopeAppSpecific.value("testKey").toInt(), 1); + } +} +#endif // Q_OS_WASM + void tst_QSettings::testByteArray_data() { QTest::addColumn<QByteArray>("data"); @@ -709,6 +769,10 @@ void tst_QSettings::embeddedZeroByte_data() QTest::newRow("@bytearray\\0") << QVariant(bytes); QTest::newRow("@string\\0") << QVariant(QString::fromLatin1(bytes.data(), bytes.size())); + + bytes = QByteArray("@\xdd\x7d", 3); + QTest::newRow("@-prefixed data") << QVariant(bytes); + QTest::newRow("@-prefixed data as string") << QVariant(QString::fromLatin1(bytes.data(), bytes.size())); } void tst_QSettings::embeddedZeroByte() @@ -945,7 +1009,7 @@ void tst_QSettings::testIniParsing() if ( settings.status() == QSettings::NoError ) { // else no point proceeding QVariant v = settings.value(key); if (expect.isValid()) - QVERIFY(v.canConvert(expect.type())); + QVERIFY(v.canConvert(expect.metaType())); // check some types so as to give prettier error messages if ( v.typeId() == QMetaType::QString ) { QCOMPARE(v.toString(), expect.toString()); @@ -1173,7 +1237,7 @@ template<int MetaTypeId> static void testMetaTypesHelper(QSettings::Format format) { typedef typename MetaEnumToType<MetaTypeId>::Type Type; - const char *key = QMetaType::typeName(MetaTypeId); + const char *key = QMetaType(MetaTypeId).name(); Type *value = TestValueFactory<MetaTypeId>::create(); QVariant inputVariant = QVariant::fromValue(*value); @@ -1192,8 +1256,8 @@ static void testMetaTypesHelper(QSettings::Format format) QSettings settings(format, scope, organization, applicationName); QVariant outputVariant = settings.value(key); if (MetaTypeId != QMetaType::QVariant) - QVERIFY(outputVariant.canConvert(MetaTypeId)); - if (outputVariant.type() != inputVariant.type()) + QVERIFY(outputVariant.canConvert(QMetaType(MetaTypeId))); + if (outputVariant.typeId() != inputVariant.typeId()) qWarning() << "type mismatch between" << inputVariant << "and" << outputVariant; QCOMPARE(qvariant_cast<Type >(outputVariant), *value); } @@ -1232,7 +1296,7 @@ void tst_QSettings::testMetaTypes_data() #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ { \ const char *formatName = QMetaEnum::fromType<QSettings::Format>().valueToKey(formats[i]); \ - const char *typeName = QMetaType::typeName(QMetaType::MetaTypeName); \ + const char *typeName = QMetaType(QMetaType::MetaTypeName).name(); \ QTest::newRow(QString("%1:%2").arg(formatName).arg(typeName).toLatin1().constData()) \ << QSettings::Format(formats[i]) << int(QMetaType::MetaTypeName); \ } @@ -1267,106 +1331,102 @@ FOR_EACH_CORE_METATYPE(RETURN_CREATE_FUNCTION) TypeTestFunctionGetter::get(type)(format); } - -void tst_QSettings::testVariantTypes_data() -{ - populateWithFormats(); -} #endif #ifdef QT_BUILD_INTERNAL void tst_QSettings::testVariantTypes() { -#define testVal(key, val, tp, rtype) \ - { \ - QSettings settings1(format, QSettings::UserScope, "software.org", "KillerAPP"); \ - settings1.setValue(key, QVariant::fromValue(val)); \ - } \ - QConfFile::clearCache(); \ - { \ - QSettings settings2(format, QSettings::UserScope, "software.org", "KillerAPP"); \ - QVariant v = settings2.value(key); \ - QVERIFY(qvariant_cast<tp >(v) == val); \ - QVERIFY(v.type() == QVariant::rtype); \ + QFETCH(QSettings::Format, format); + + { + QSettings settings(format, QSettings::UserScope, "software.org", "KillerAPP"); + QVERIFY(!settings.contains("empty")); + QCOMPARE(settings.value("empty"), QVariant()); + + settings.setValue("empty", QVariant()); + QVERIFY(settings.contains("empty")); + QCOMPARE(settings.value("empty"), QVariant()); + + settings.setValue("empty", QVariant(1)); + QVERIFY(settings.contains("empty")); + QCOMPARE(settings.value("empty"), QVariant(1)); + + settings.setValue("empty", QVariant()); + QVERIFY(settings.contains("empty")); + QCOMPARE(settings.value("empty"), QVariant()); + + settings.remove("empty"); + QVERIFY(!settings.contains("empty")); + QCOMPARE(settings.value("empty"), QVariant()); } - typedef QMap<QString, QVariant> TestVariantMap; + auto checker = [format](const char *key, auto value, QMetaType::Type expected) { + { + QSettings settings(format, QSettings::UserScope, "software.org", "KillerAPP"); + settings.setValue(key, QVariant::fromValue(value)); + } + QConfFile::clearCache(); + { + QSettings settings(format, QSettings::UserScope, "software.org", "KillerAPP"); + QVariant actual = settings.value(key); + QCOMPARE(actual.metaType().id(), expected); + QCOMPARE(qvariant_cast<decltype(value)>(actual), value); + } + }; +#define testValue(key, supplied, expected) do { \ + checker(key, supplied, QMetaType::expected); \ + if (QTest::currentTestFailed()) \ + return; \ + } while (0) - QFETCH(QSettings::Format, format); + typedef QMap<QString, QVariant> TestVariantMap; TestVariantMap m2; m2.insert("ene", "due"); m2.insert("rike", "fake"); m2.insert("borba", "dorba"); - testVal("key2", m2, TestVariantMap, Map); - - QStringList l2; + testValue("customMap", m2, QVariantMap); - l2 << "ene" << "due" << "@Point(1 2)" << "@fake"; - testVal("key3", l2, QStringList, StringList); + QStringList l2 { "ene", "due", "@Point(1 2)", "@fake" }; + testValue("stringsAt", l2, QStringList); - l2.clear(); - l2 << "ene" << "due" << "rike" << "fake"; - testVal("key3", l2, QStringList, StringList); + l2 = { "ene", "due", "rike", "fake" }; + testValue("strings", l2, QStringList); - QList<QVariant> l3; QDate date = QDate::currentDate(); QTime time = QTime::currentTime(); - l3 << QString("ene") << 10 << QVariant::fromValue(QColor(1, 2, 3)) << QVariant(QRect(1, 2, 3, 4)) - << QVariant(QSize(4, 56)) << QVariant(QPoint(4, 2)) << true << false << date << time; - testVal("key3", l3, QVariantList, List); - - testVal("key4", QString("hello"), QString, String); - testVal("key5", QColor(1, 2, 3), QColor, Color); - testVal("key6", QRect(1, 2, 3, 4), QRect, Rect); - testVal("key7", QSize(4, 56), QSize, Size); - testVal("key8", QPoint(4, 2), QPoint, Point); - testVal("key10", date, QDate, Date); - testVal("key11", time, QTime, Time); - testVal("key12", QByteArray("foo bar"), QByteArray, ByteArray); - - { - QSettings settings(format, QSettings::UserScope, "software.org", "KillerAPP"); - QVERIFY(!settings.contains("key99")); - QCOMPARE(settings.value("key99"), QVariant()); + QList<QVariant> l3 { QString("ene"), 10, QVariant::fromValue(QColor(1, 2, 3)), + QVariant(QRect(1, 2, 3, 4)), QVariant(QSize(4, 56)), QVariant(QPoint(4, 2)), + true, false, date, time }; + testValue("mixedList", l3, QVariantList); + + testValue("string", QString("hello"), QString); + testValue("color", QColor(1, 2, 3), QColor); + testValue("rect", QRect(1, 2, 3, 4), QRect); + testValue("size", QSize(4, 56), QSize); + testValue("point", QPoint(4, 2), QPoint); + testValue("date", date, QDate); + testValue("time", time, QTime); + testValue("byteArray", QByteArray("foo bar"), QByteArray); + + QList<QVariant> l4 { QVariant(m2), QVariant(l2), QVariant(l3) }; + testValue("collectList", l4, QVariantList); - settings.setValue("key99", QVariant()); - QVERIFY(settings.contains("key99")); - QCOMPARE(settings.value("key99"), QVariant()); - - settings.setValue("key99", QVariant(1)); - QVERIFY(settings.contains("key99")); - QCOMPARE(settings.value("key99"), QVariant(1)); - - settings.setValue("key99", QVariant()); - QVERIFY(settings.contains("key99")); - QCOMPARE(settings.value("key99"), QVariant()); - - settings.remove("key99"); - QVERIFY(!settings.contains("key99")); - QCOMPARE(settings.value("key99"), QVariant()); - } - - QList<QVariant> l4; - l4 << QVariant(m2) << QVariant(l2) << QVariant(l3); - testVal("key13", l4, QVariantList, List); QDateTime dt = QDateTime::currentDateTime(); - dt.setOffsetFromUtc(3600); - testVal("key14", dt, QDateTime, DateTime); + dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(3600)); + testValue("dateTime", dt, QDateTime); #if QT_CONFIG(shortcut) // We store key sequences as strings instead of binary variant blob, for improved // readability in the resulting format. - if (format >= QSettings::InvalidFormat) { - testVal("keysequence", QKeySequence(Qt::ControlModifier + Qt::Key_F1), QKeySequence, KeySequence); - } else { - testVal("keysequence", - QKeySequence(Qt::ControlModifier + Qt::Key_F1).toString(QKeySequence::NativeText), - QString, String); - } + QKeySequence seq(Qt::ControlModifier | Qt::Key_F1); + if (format >= QSettings::InvalidFormat) + testValue("keySequence", seq, QKeySequence); + else + testValue("keySequence", seq.toString(QKeySequence::NativeText), QString); #endif // QT_CONFIG(shortcut) -#undef testVal +#undef testValue } #endif @@ -1736,7 +1796,7 @@ void tst_QSettings::sync() // Now "some other app" will change other.software.org.ini QString userConfDir = settingsPath("__user__") + QDir::separator(); - unlink((userConfDir + "other.software.org.ini").toLatin1()); + QT_UNLINK((userConfDir + "other.software.org.ini").toLatin1()); rename((userConfDir + "software.org.ini").toLatin1(), (userConfDir + "other.software.org.ini").toLatin1()); @@ -1756,12 +1816,12 @@ void tst_QSettings::sync() QCOMPARE(settings2.value("moo/beta/geometry/width").toInt(), 3); QCOMPARE(settings2.value("moo/beta/geometry/height").toInt(), 4); QCOMPARE(settings2.value("moo/gamma/splitter").toInt(), 5); - QCOMPARE(settings2.allKeys().count(), 11); + QCOMPARE(settings2.allKeys().size(), 11); // Now, software.org.ini no longer exists, this is same as another app // clearing all settings. settings1.sync(); - QCOMPARE(settings1.allKeys().count(), 0); + QCOMPARE(settings1.allKeys().size(), 0); // Now "some other app" will change software.org.ini QVERIFY(QFile::rename((userConfDir + "other.software.org.ini").toLatin1(), @@ -1779,7 +1839,7 @@ void tst_QSettings::sync() QCOMPARE(settings1.value("moo/beta/geometry/width").toInt(), 3); QCOMPARE(settings1.value("moo/beta/geometry/height").toInt(), 4); QCOMPARE(settings1.value("moo/gamma/splitter").toInt(), 5); - QCOMPARE(settings1.allKeys().count(), 11); + QCOMPARE(settings1.allKeys().size(), 11); } void tst_QSettings::syncNonWriteableDir() @@ -1854,11 +1914,6 @@ void tst_QSettings::syncAlternateDataStream() } #endif -void tst_QSettings::setFallbacksEnabled_data() -{ - populateWithFormats(); -} - void tst_QSettings::setFallbacksEnabled() { QFETCH(QSettings::Format, format); @@ -1943,11 +1998,6 @@ void tst_QSettings::setFallbacksEnabled() QVERIFY(!settings1.contains("key 5")); } -void tst_QSettings::testChildKeysAndGroups_data() -{ - populateWithFormats(); -} - void tst_QSettings::testChildKeysAndGroups() { QFETCH(QSettings::Format, format); @@ -2008,6 +2058,24 @@ void tst_QSettings::testChildKeysAndGroups() l.sort(); QCOMPARE(l, QStringList() << "bar" << "foo"); } + +#if defined(Q_OS_WASM) + // WebIndexedDBFormat does not use the cached settings file on creation, but instead always uses + // the file from the indexed DB anew. + if (format == QSettings::Format::WebIndexedDBFormat) + settings1.sync(); +#endif + + { + QSettings settings3(format, QSettings::UserScope, "software.org", "application"); + settings3.setFallbacksEnabled(false); + settings3.beginGroup("alpha"); + QCOMPARE(settings3.childGroups(), QStringList()); + settings3.setFallbacksEnabled(true); + QStringList children = settings3.childGroups(); + children.sort(); + QCOMPARE(children, QStringList({"beta", "gamma"})); + } } void tst_QSettings::testUpdateRequestEvent() @@ -2067,7 +2135,7 @@ void SettingsThread::run() settings.setValue(QString::number((param * NumIterations) + i), param); settings.sync(); if (settings.status() != QSettings::NoError) { - QWARN(qPrintable(QString("Unexpected QSettings status %1").arg((int)settings.status()))); + qWarning() << qPrintable(QString("Unexpected QSettings status %1").arg((int)settings.status())); ++numThreadSafetyFailures; } } @@ -2075,6 +2143,16 @@ void SettingsThread::run() void tst_QSettings::testThreadSafety() { +#if !QT_CONFIG(thread) + QSKIP("This test requires threads to be enabled."); +#endif // !QT_CONFIG(thread) +#if defined(Q_OS_WASM) + if (!qstdweb::haveJspi()) + QSKIP("Test needs jspi on WASM. Calls are proxied to the main thread from SettingsThreads, " + "which necessitates the use of an event loop to yield to the main loop. Event loops " + "require jspi."); +#endif + SettingsThread threads[NumThreads]; int i, j; @@ -2082,6 +2160,19 @@ void tst_QSettings::testThreadSafety() for (i = 0; i < NumThreads; ++i) threads[i].start(i + 1); + +#if defined(Q_OS_WASM) && QT_CONFIG(thread) + QEventLoop loop; + int remaining = NumThreads; + for (int i = 0; i < NumThreads; ++i) { + QObject::connect(&threads[i], &QThread::finished, this, [&remaining, &loop]() { + if (!--remaining) + loop.quit(); + }); + } + loop.exec(); +#endif // defined(Q_OS_WASM) && QT_CONFIG(thread) + for (i = 0; i < NumThreads; ++i) threads[i].wait(); @@ -2136,20 +2227,17 @@ void tst_QSettings::testNormalizedKey() inKey.detach(); - QString result = QSettingsPrivate::normalizedKey(inKey); - QCOMPARE(result, outKey); - - /* - If the key is already normalized, we verify that outKey is - just a shallow copy of the input string. This is an important - optimization that shouldn't be removed accidentally. - */ - if (inKey == outKey) { - QVERIFY(!result.isDetached()); - } else { - if (!result.isEmpty()) { - QVERIFY(result.isDetached()); - } + { + auto result = QSettingsPrivate::normalizedKey(inKey); + QCOMPARE(result, outKey); + } + { + auto result = QSettingsPrivate::normalizedKey(QUtf8StringView{inKey.toUtf8()}); + QCOMPARE(result, outKey); + } + { + auto result = QSettingsPrivate::normalizedKey(QLatin1String{inKey.toLatin1()}); + QCOMPARE(result, outKey); } } #endif @@ -2175,6 +2263,8 @@ void tst_QSettings::testEmptyData() vList2 << emptyString << nullString; vList3 << QString("foo"); + const auto rm = QScopeGuard([=] { QFile::remove(filename); }); + { QSettings settings(filename, QSettings::IniFormat); settings.setValue("nullString", nullString); @@ -2228,7 +2318,6 @@ void tst_QSettings::testEmptyData() QCOMPARE(settings.value("vList3").toList(), vList3); QCOMPARE(settings.status(), QSettings::NoError); } - QFile::remove(filename); } void tst_QSettings::testEmptyKey() @@ -2334,11 +2423,6 @@ void tst_QSettings::trailingWhitespace() } } -void tst_QSettings::fromFile_data() -{ - populateWithFormats(); -} - void tst_QSettings::fromFile() { QFETCH(QSettings::Format, format); @@ -2360,6 +2444,12 @@ void tst_QSettings::fromFile() QStringList strList = QStringList() << "hope" << "destiny" << "chastity"; +#if !defined(Q_OS_WIN) + auto deleteFile = QScopeGuard([path, oldCur]() { + QFile::remove(path); + QDir::setCurrent(oldCur); + }); +#endif // !defined(Q_OS_WIN) { QSettings settings1(path, format); QVERIFY(settings1.allKeys().isEmpty()); @@ -2395,8 +2485,6 @@ void tst_QSettings::fromFile() QCOMPARE(settings1.value("gamma/foo.bar").toInt(), 4); QCOMPARE(settings1.allKeys().size(), 3); } - - QDir::setCurrent(oldCur); } static bool containsSubList(QStringList mom, QStringList son) @@ -2408,11 +2496,6 @@ static bool containsSubList(QStringList mom, QStringList son) return true; } -void tst_QSettings::testArrays_data() -{ - populateWithFormats(); -} - /* Tests beginReadArray(), beginWriteArray(), endArray(), and setArrayIndex(). @@ -2478,17 +2561,17 @@ void tst_QSettings::testArrays() QCOMPARE(settings1.value("ene").toInt(), 2); QCOMPARE(settings1.value("due").toInt(), 3); QCOMPARE(settings1.value("rike").toInt(), 4); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.setArrayIndex(1); QCOMPARE(settings1.value("ene").toInt(), 5); QCOMPARE(settings1.value("due").toInt(), 6); QCOMPARE(settings1.value("rike").toInt(), 7); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.setArrayIndex(2); QCOMPARE(settings1.value("ene").toInt(), 8); QCOMPARE(settings1.value("due").toInt(), 9); QCOMPARE(settings1.value("rike").toInt(), 10); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.endArray(); settings1.endGroup(); @@ -2538,17 +2621,17 @@ void tst_QSettings::testArrays() QCOMPARE(settings1.value("ene").toInt(), 2); QCOMPARE(settings1.value("due").toInt(), 3); QCOMPARE(settings1.value("rike").toInt(), 4); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.setArrayIndex(1); QCOMPARE(settings1.value("ene").toInt(), 5); QCOMPARE(settings1.value("due").toInt(), 6); QCOMPARE(settings1.value("rike").toInt(), 7); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.setArrayIndex(2); QCOMPARE(settings1.value("ene").toInt(), 8); QCOMPARE(settings1.value("due").toInt(), 9); QCOMPARE(settings1.value("rike").toInt(), 10); - QCOMPARE(settings1.allKeys().count(), 3); + QCOMPARE(settings1.allKeys().size(), 3); settings1.endArray(); settings1.endGroup(); @@ -2693,7 +2776,7 @@ static QByteArray iniEscapedKey(const QString &str) static QString iniUnescapedKey(const QByteArray &ba) { QString result; - QSettingsPrivate::iniUnescapedKey(ba, 0, ba.size(), result); + QSettingsPrivate::iniUnescapedKey(ba, result); return result; } @@ -2708,7 +2791,7 @@ static QStringList iniUnescapedStringList(const QByteArray &ba) { QStringList result; QString str; - bool isStringList = QSettingsPrivate::iniUnescapedStringList(ba, 0, ba.size(), str, result); + bool isStringList = QSettingsPrivate::iniUnescapedStringList(ba, str, result); if (!isStringList) result = QStringList(str); return result; @@ -2720,7 +2803,7 @@ QString escapeWeirdChars(const QString &s) QString result; bool escapeNextDigit = false; - for (int i = 0; i < s.length(); ++i) { + for (int i = 0; i < s.size(); ++i) { QChar c = s.at(i); if (c.unicode() < ' ' || c.unicode() > '~' || (escapeNextDigit && c.unicode() >= '0' && c.unicode() <= 'f')) { @@ -2860,11 +2943,6 @@ void tst_QSettings::testEscapes() } #endif -void tst_QSettings::testCaseSensitivity_data() -{ - populateWithFormats(); -} - void tst_QSettings::testCaseSensitivity() { QFETCH(QSettings::Format, format); @@ -2944,7 +3022,7 @@ void tst_QSettings::testCaseSensitivity() } } -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN // Please write a fileName() test for the other platforms void tst_QSettings::fileName() { @@ -3014,11 +3092,6 @@ void tst_QSettings::fileName() } #endif -void tst_QSettings::isWritable_data() -{ - populateWithFormats(); -} - void tst_QSettings::isWritable() { QFETCH(QSettings::Format, format); @@ -3071,13 +3144,6 @@ void tst_QSettings::isWritable() } #ifdef QT_BUILD_INTERNAL -void tst_QSettings::childGroups_data() -{ - populateWithFormats(); -} -#endif - -#ifdef QT_BUILD_INTERNAL void tst_QSettings::childGroups() { QFETCH(QSettings::Format, format); @@ -3147,13 +3213,6 @@ void tst_QSettings::childGroups() #endif #ifdef QT_BUILD_INTERNAL -void tst_QSettings::childKeys_data() -{ - populateWithFormats(); -} -#endif - -#ifdef QT_BUILD_INTERNAL void tst_QSettings::childKeys() { QFETCH(QSettings::Format, format); @@ -3221,13 +3280,6 @@ void tst_QSettings::childKeys() #endif #ifdef QT_BUILD_INTERNAL -void tst_QSettings::allKeys_data() -{ - populateWithFormats(); -} -#endif - -#ifdef QT_BUILD_INTERNAL void tst_QSettings::allKeys() { QFETCH(QSettings::Format, format); @@ -3378,7 +3430,7 @@ void tst_QSettings::setPath() path checks that it has no bad side effects. */ for (int i = 0; i < 2; ++i) { -#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) +#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) && !defined(Q_OS_WASM) TEST_PATH(i == 0, "conf", NativeFormat, UserScope, "alpha") TEST_PATH(i == 0, "conf", NativeFormat, SystemScope, "beta") #endif @@ -3487,11 +3539,6 @@ void tst_QSettings::dontReorderIniKeysNeedlessly() } #endif -void tst_QSettings::rainersSyncBugOnMac_data() -{ - ctor_data(); -} - void tst_QSettings::rainersSyncBugOnMac() { QFETCH(QSettings::Format, format); @@ -3500,6 +3547,12 @@ void tst_QSettings::rainersSyncBugOnMac() if (format == QSettings::NativeFormat) QSKIP("Apple OSes do not support direct reads from and writes to .plist files, due to caching and background syncing. See QTBUG-34899."); #endif +#if defined(Q_OS_WASM) + if (format == QSettings::NativeFormat) + QSKIP("WASM's localStorage backend recognizes no concept of file"); + if (format == QSettings::WebIndexedDBFormat) + QSKIP("WASM's indexedDB backend uses the virtual FS file only as a backing store"); +#endif // Q_OS_WASM QString fileName; @@ -3523,7 +3576,7 @@ void tst_QSettings::rainersSyncBugOnMac() void tst_QSettings::recursionBug() { QPixmap pix(10,10); - pix.fill("blue"); + pix.fill(Qt::blue); { QSettings settings(settingsPath("starrunner.ini"), QSettings::IniFormat); @@ -3570,14 +3623,14 @@ void tst_QSettings::consistentRegistryStorage() } #endif -#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(QT_NO_STANDARDPATHS) +#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM) && !defined(QT_NO_STANDARDPATHS) QT_BEGIN_NAMESPACE extern void clearDefaultPaths(); QT_END_NAMESPACE #endif void tst_QSettings::testXdg() { -#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(QT_NO_STANDARDPATHS) +#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM) && !defined(QT_NO_STANDARDPATHS) // Note: The XDG_CONFIG_DIRS test must be done before overriding the system path // by QSettings::setPath/setSystemIniPath (used in cleanupTestFiles()). clearDefaultPaths(); @@ -3642,5 +3695,39 @@ void tst_QSettings::testXdg() #endif } +void tst_QSettings::testReadKeys_data() +{ + QTest::addColumn<QString>("filepath"); + + QTest::newRow("escaped") << ":/qt5settings.ini"; + QTest::newRow("utf-8") << ":/utf8settings.ini"; +} + +void tst_QSettings::testReadKeys() +{ + QFETCH(QString, filepath); + + QSettings settings(filepath, QSettings::IniFormat); + QCOMPARE(settings.status(), QSettings::NoError); + + QVariantMap expectedValues; + expectedValues.insert("Test/BAR", "BAR"); + expectedValues.insert("Test/OST", "OST"); + expectedValues.insert("Test/B\xC3\x84R", "B\xC3\x84R"); // BÄR + expectedValues.insert("Test/\xC3\x96SE", "\xC3\x96SE"); // ÖSE + expectedValues.insert( + "Test/\xD0\xAD\xD1\x82\xD0\xBE/\xD1\x82\xD0\xB5\xD1\x81\xD1\x82", // Это/тест + "\xD0\xAD\xD1\x82\xD0\xBE \xD1\x82\xD0\xB5\xD1\x81\xD1\x82"); // Это тест + expectedValues.insert(".,'%U\xD0\xB0\xD0\xB1\xD0\xB2\xD0\xB3\"\t", ".,'%!@#$"); + expectedValues.insert("\xE2\x99\x9F", "\xE2\x99\x98\xE2\x99\x9A"); // ♟︎ ♘♚ + expectedValues.insert("\xF0\x9F\x8C\x8D", "\xF0\x9F\x8C\x90"); // 🌍 🌐 + + QVariantMap readValues; + for (const auto &key : settings.allKeys()) + readValues.insert(key, settings.value(key)); + + QCOMPARE(readValues, expectedValues); +} + QTEST_MAIN(tst_QSettings) #include "tst_qsettings.moc" |