From f82c992e6752a96c0d3f3b87a6636e426df4c686 Mon Sep 17 00:00:00 2001 From: Zhang Sheng Date: Thu, 16 Jul 2026 14:10:23 +0800 Subject: [PATCH] fix: handle DConfig list values in search utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added type checking for QVariant list values in tryLoadStringListFromDConfigInternal 2. Now properly handles both JSON array format (list type) and semicolon-delimited strings 3. The issue occurred when DConfig stored values as JSON arrays - toString() would return empty 4. Now uses toStringList() for list-type values to correctly parse the configuration Influence: 1. Test search functionality with DConfig settings in both JSON array format 2. Verify semicolon-delimited string parsing remains unchanged 3. Check configuration loading from different DConfig value formats fix: 修复搜索工具中DConfig列表值的处理 1. 在tryLoadStringListFromDConfigInternal中添加了对QVariant列表值的类型 检查 2. 现在能正确处理JSON数组格式(列表类型)和分号分隔的字符串 3. 当DConfig以JSON数组格式存储值时会出现问题 - toString()会返回空值 4. 现在对列表类型值使用toStringList()以正确解析配置 Influence: 1. 测试使用JSON数组格式的DConfig设置的搜索功能 2. 验证分号分隔字符串的解析保持不变 3. 检查从不同DConfig值格式加载配置的情况 --- autotests/dfm-search-tests/main.cpp | 5 + .../dfm-search-tests/tst_dconfig_parsing.cpp | 160 ++++++++++++++++++ .../dfm-search-lib/utils/searchutility.cpp | 30 ++-- .../dfm-search-lib/utils/searchutility.h | 14 ++ 4 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 autotests/dfm-search-tests/tst_dconfig_parsing.cpp diff --git a/autotests/dfm-search-tests/main.cpp b/autotests/dfm-search-tests/main.cpp index 7894bd2f..f0fb78b1 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 00000000..56106af8 --- /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 303e6bc6..59318a00 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 0908062d..9c76c862 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