diff --git a/autotests/dfm-search-tests/main.cpp b/autotests/dfm-search-tests/main.cpp index 7894bd2..f0fb78b 100644 --- a/autotests/dfm-search-tests/main.cpp +++ b/autotests/dfm-search-tests/main.cpp @@ -25,6 +25,7 @@ extern QObject *create_tst_ContentSearchEngine(); extern QObject *create_tst_FileNameSearchEngine(); extern QObject *create_tst_RecentSearchEngine(); extern QObject *create_tst_CliOptions(); +extern QObject *create_tst_DConfigParsing(); int main(int argc, char *argv[]) { @@ -111,5 +112,9 @@ int main(int argc, char *argv[]) result |= QTest::qExec(testObj19, argc, argv); delete testObj19; + QObject *testObj20 = create_tst_DConfigParsing(); + result |= QTest::qExec(testObj20, argc, argv); + delete testObj20; + return result; } diff --git a/autotests/dfm-search-tests/tst_dconfig_parsing.cpp b/autotests/dfm-search-tests/tst_dconfig_parsing.cpp new file mode 100644 index 0000000..56106af --- /dev/null +++ b/autotests/dfm-search-tests/tst_dconfig_parsing.cpp @@ -0,0 +1,160 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include +#include +#include + +#include +#include + +using namespace DFMSEARCH; + +class tst_DConfigParsing : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void testSemicolonSeparatedString(); + void testSingleValueString(); + void testStringListVariant(); + void testVariantListVariant(); + void testEmptyString(); + void testEmptyStringList(); + void testInvalidVariant(); + void testNonConvertibleType(); + void testSemicolonWithEmptyParts(); + void testTrailingSemicolon(); +}; + +void tst_DConfigParsing::initTestCase() +{ +} + +void tst_DConfigParsing::cleanupTestCase() +{ +} + +void tst_DConfigParsing::testSemicolonSeparatedString() +{ + QVariant value(QString("txt;pdf;docx;xlsx")); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for semicolon-separated string"); + QCOMPARE(result->size(), 4); + QCOMPARE(result->at(0), QString("txt")); + QCOMPARE(result->at(1), QString("pdf")); + QCOMPARE(result->at(2), QString("docx")); + QCOMPARE(result->at(3), QString("xlsx")); +} + +void tst_DConfigParsing::testSingleValueString() +{ + QVariant value(QString("txt")); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for single string"); + QCOMPARE(result->size(), 1); + QCOMPARE(result->at(0), QString("txt")); +} + +void tst_DConfigParsing::testStringListVariant() +{ + QStringList list = { "txt", "pdf", "docx" }; + QVariant value(list); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for QStringList variant"); + QCOMPARE(result->size(), 3); + QCOMPARE(result->at(0), QString("txt")); + QCOMPARE(result->at(1), QString("pdf")); + QCOMPARE(result->at(2), QString("docx")); +} + +void tst_DConfigParsing::testVariantListVariant() +{ + QVariantList list; + list << QVariant("home") << QVariant("usr") << QVariant("opt"); + QVariant value(list); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for QVariantList variant"); + QCOMPARE(result->size(), 3); + QCOMPARE(result->at(0), QString("home")); + QCOMPARE(result->at(1), QString("usr")); + QCOMPARE(result->at(2), QString("opt")); +} + +void tst_DConfigParsing::testEmptyString() +{ + QVariant value(QString("")); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for empty string"); + QCOMPARE(result->size(), 1); + QVERIFY2(result->at(0).isEmpty(), "Single element should be empty string"); +} + +void tst_DConfigParsing::testEmptyStringList() +{ + QStringList list; + QVariant value(list); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value for empty QStringList"); + QCOMPARE(result->size(), 0); +} + +void tst_DConfigParsing::testInvalidVariant() +{ + QVariant value; + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(!result.has_value(), "Should return nullopt for invalid QVariant"); +} + +void tst_DConfigParsing::testNonConvertibleType() +{ + QVariant value(42); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(!result.has_value(), "Should return nullopt for non-convertible type (int)"); +} + +void tst_DConfigParsing::testSemicolonWithEmptyParts() +{ + QVariant value(QString("txt;;pdf;")); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value"); + QCOMPARE(result->size(), 4); + QCOMPARE(result->at(0), QString("txt")); + QCOMPARE(result->at(1), QString("")); + QCOMPARE(result->at(2), QString("pdf")); + QCOMPARE(result->at(3), QString("")); +} + +void tst_DConfigParsing::testTrailingSemicolon() +{ + QVariant value(QString("/home;/usr;/opt;")); + auto result = SearchUtility::parseVariantToStringList(value); + + QVERIFY2(result.has_value(), "Should return a value"); + QCOMPARE(result->size(), 4); + QCOMPARE(result->at(0), QString("/home")); + QCOMPARE(result->at(1), QString("/usr")); + QCOMPARE(result->at(2), QString("/opt")); + QCOMPARE(result->at(3), QString("")); +} + +QObject *create_tst_DConfigParsing() +{ + return new tst_DConfigParsing(); +} + +#include "tst_dconfig_parsing.moc" diff --git a/src/dfm-search/dfm-search-lib/utils/searchutility.cpp b/src/dfm-search/dfm-search-lib/utils/searchutility.cpp index 303e6bc..59318a0 100644 --- a/src/dfm-search/dfm-search-lib/utils/searchutility.cpp +++ b/src/dfm-search/dfm-search-lib/utils/searchutility.cpp @@ -118,18 +118,8 @@ static std::optional tryLoadStringListFromDConfigInternal( QVariant value = dconfigPtr->value(keyName); - if (!value.isValid()) { - qDebug() << "DConfig: Key '" << keyName << "' not found in appId:" << appId << "schemaId:" << schemaId; - return std::nullopt; // Key not found - } - - if (!value.canConvert()) { - qWarning() << "DConfig: Value for key '" << keyName << "' in appId:" << appId << "schemaId:" << schemaId - << "cannot be converted to QStringList. Actual type:" << value.typeName(); - return std::nullopt; // Type mismatch - } // No need to delete dconfigPtr, dconfigParent will manage it when it goes out of scope. - return value.toString().split(';'); + return SearchUtility::parseVariantToStringList(value); } // --- Specific Loader for "supportedFileExtensions" --- @@ -887,5 +877,23 @@ QSet semanticPicExtensions() return kExts; } +std::optional parseVariantToStringList(const QVariant &value) +{ + if (!value.isValid()) { + return std::nullopt; + } + + if (!value.canConvert()) { + return std::nullopt; + } + + // DConfig value 可能是 JSON 数组(list 类型)或分号分隔字符串; + // 当为 JSON 数组时 toString() 返回空,必须用 toStringList()。 + if (value.type() == QVariant::List || value.type() == QVariant::StringList) { + return value.toStringList(); + } + return value.toString().split(';'); +} + } // namespace SearchUtility DFM_SEARCH_END_NS diff --git a/src/dfm-search/dfm-search-lib/utils/searchutility.h b/src/dfm-search/dfm-search-lib/utils/searchutility.h index 0908062..9c76c86 100644 --- a/src/dfm-search/dfm-search-lib/utils/searchutility.h +++ b/src/dfm-search/dfm-search-lib/utils/searchutility.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include // SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // @@ -28,6 +30,18 @@ namespace SearchUtility { */ QStringList extractBooleanKeywords(const SearchQuery &query); +/** + * @brief Parse a DConfig QVariant value into a QStringList + * + * DConfig values may arrive as a semicolon-separated string or as a JSON array + * (QVariant::List / QVariant::StringList). When the value is a list type, + * toString() returns an empty string, so toStringList() must be used instead. + * + * @param value The QVariant from DConfig::value() + * @return The parsed string list, or std::nullopt if the value is invalid or not convertible + */ +std::optional parseVariantToStringList(const QVariant &value); + /** * @brief Get the list of supported file types for Deepin Anything * @return List of supported file types