Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions autotests/dfm-search-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ target_link_libraries(dfm-search-test
# Add size_parser source for testing (it's part of dfm-searcher client but we test it here)
target_sources(dfm-search-test PRIVATE
${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/size_parser.cpp
${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/time_parser.cpp
${CMAKE_SOURCE_DIR}/src/dfm-search/dfm-search-client/cli_options.cpp
${CMAKE_SOURCE_DIR}/3rdparty/testutils/stub-ext/stub-shadow.cpp
)

Expand Down
5 changes: 5 additions & 0 deletions autotests/dfm-search-tests/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern QObject *create_tst_ContentRetriever();
extern QObject *create_tst_ContentSearchEngine();
extern QObject *create_tst_FileNameSearchEngine();
extern QObject *create_tst_RecentSearchEngine();
extern QObject *create_tst_CliOptions();

int main(int argc, char *argv[])
{
Expand Down Expand Up @@ -106,5 +107,9 @@ int main(int argc, char *argv[])
result |= QTest::qExec(testObj18, argc, argv);
delete testObj18;

QObject *testObj19 = create_tst_CliOptions();
result |= QTest::qExec(testObj19, argc, argv);
delete testObj19;

return result;
}
108 changes: 108 additions & 0 deletions autotests/dfm-search-tests/tst_cli_options.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include <QTest>

Check warning on line 5 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTest> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 5 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QTest> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonDocument>

Check warning on line 6 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonDocument> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 6 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QJsonDocument> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonObject>

Check warning on line 7 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 7 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QJsonObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonArray>

Check warning on line 8 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonArray> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 8 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QJsonArray> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QStringList>

Check warning on line 9 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QStringList> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 9 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QStringList> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include "cli_options.h"

Check warning on line 11 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "cli_options.h" not found.

Check warning on line 11 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: "cli_options.h" not found.

using namespace dfmsearch;

class tst_CliOptions : public QObject
{
Q_OBJECT

private Q_SLOTS:

Check warning on line 19 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

There is an unknown macro here somewhere. Configuration is required. If Q_SLOTS is a macro then please configure it.

Check warning on line 19 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

There is an unknown macro here somewhere. Configuration is required. If Q_SLOTS is a macro then please configure it.
void testFeaturesCount();
void testFeaturesContent();
void testFeaturesOrder();
void testFeaturesJsonOutput();
void testFeaturesTextOutput();
};

// --- supportedFeatures() integrity tests ---

void tst_CliOptions::testFeaturesCount()
{
QStringList features = CliOptions::supportedFeatures();
QCOMPARE(features.size(), 26);
}

void tst_CliOptions::testFeaturesContent()
{
QStringList features = CliOptions::supportedFeatures();
QStringList expected = {
"filename", "content", "ocr", "recent", "semantic",
"indexed", "realtime",
"simple", "boolean", "wildcard",
"preview", "pinyin", "pinyin-acronym", "case-sensitive",
"include-hidden", "file-types", "file-extensions", "exclude",
"time-filter", "size-filter", "max-results", "max-preview",
"offset", "filename-in-content", "json-output", "verbose"
};
QCOMPARE(features, expected);
}

void tst_CliOptions::testFeaturesOrder()
{
QStringList features = CliOptions::supportedFeatures();
// Verify first and last items to ensure order is preserved
QCOMPARE(features.first(), QStringLiteral("filename"));
QCOMPARE(features.last(), QStringLiteral("verbose"));

// Verify category boundaries
QCOMPARE(features.at(4), QStringLiteral("semantic")); // last search type
QCOMPARE(features.at(6), QStringLiteral("realtime")); // last search method
QCOMPARE(features.at(9), QStringLiteral("wildcard")); // last query type
QCOMPARE(features.at(10), QStringLiteral("preview")); // first functional feature
}

void tst_CliOptions::testFeaturesJsonOutput()
{
// Replicate the JSON formatting logic from parse() to verify structure
QStringList features = CliOptions::supportedFeatures();

QJsonObject root;
root["type"] = QStringLiteral("features");
QJsonArray featuresArray;
for (const QString &feature : features) {
featuresArray.append(feature);
}
root["features"] = featuresArray;
root["count"] = features.size();
QJsonDocument doc(root);

QVERIFY(doc.isObject());
QJsonObject obj = doc.object();
QCOMPARE(obj.value("type").toString(), QStringLiteral("features"));
QCOMPARE(obj.value("count").toInt(), 26);

QJsonArray arr = obj.value("features").toArray();
QCOMPARE(arr.size(), 26);
QCOMPARE(arr.at(0).toString(), QStringLiteral("filename"));
QCOMPARE(arr.at(25).toString(), QStringLiteral("verbose"));
}

void tst_CliOptions::testFeaturesTextOutput()
{
// Replicate the text output formatting (space-separated) from parse()
QStringList features = CliOptions::supportedFeatures();
QString textOutput = features.join(' ');

// Should be a single line with 25 spaces separating 26 items
QCOMPARE(textOutput.count(' '), 25);
QVERIFY(!textOutput.contains('\n'));
QVERIFY(textOutput.startsWith(QStringLiteral("filename")));
QVERIFY(textOutput.endsWith(QStringLiteral("verbose")));
}

QObject *create_tst_CliOptions()
{
return new tst_CliOptions();
}

#include "tst_cli_options.moc"

Check warning on line 108 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "tst_cli_options.moc" not found.

Check warning on line 108 in autotests/dfm-search-tests/tst_cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: "tst_cli_options.moc" not found.
49 changes: 48 additions & 1 deletion src/dfm-search/dfm-search-client/cli_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
#include "time_parser.h"
#include "size_parser.h"

#include <QCoreApplication>

Check warning on line 9 in src/dfm-search/dfm-search-client/cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QCoreApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 9 in src/dfm-search/dfm-search-client/cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QCoreApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFileInfo>

Check warning on line 10 in src/dfm-search/dfm-search-client/cli_options.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 10 in src/dfm-search/dfm-search-client/cli_options.cpp

View workflow job for this annotation

GitHub Actions / static-check / static-check

Include file: <QFileInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <iostream>
#include <cstdlib>

Expand Down Expand Up @@ -53,16 +56,36 @@
m_timeRangeOption(QStringList() << "time-range", "Custom time range (start,end)", "range"),
m_sizeMinOption(QStringList() << "size-min", "Minimum file size (e.g., 1K, 10M, 1G, 512)", "size"),
m_sizeMaxOption(QStringList() << "size-max", "Maximum file size (e.g., 1K, 10M, 1G, 512)", "size"),
m_versionOption(QStringList() << "version", "Show version information and exit")
m_versionOption(QStringList() << "version", "Show version information and exit"),
m_featuresOption(QStringList() << "features", "List supported features and exit")
{
setupOptions();
}

QStringList CliOptions::supportedFeatures()
{
// 静态特性列表(编译时确定,26 项)
// Search types
return {
"filename", "content", "ocr", "recent", "semantic",
// Search methods
"indexed", "realtime",
// Query types
"simple", "boolean", "wildcard",
// Functional features
"preview", "pinyin", "pinyin-acronym", "case-sensitive",
"include-hidden", "file-types", "file-extensions", "exclude",
"time-filter", "size-filter", "max-results", "max-preview",
"offset", "filename-in-content", "json-output", "verbose"
};
}

void CliOptions::setupOptions()
{
m_parser.setApplicationDescription("DFM Search Client");
m_parser.addHelpOption();
m_parser.addOption(m_versionOption);
m_parser.addOption(m_featuresOption);

// Basic options
m_parser.addOption(m_typeOption);
Expand Down Expand Up @@ -163,6 +186,7 @@
std::cout << "Output Options:" << std::endl;
std::cout << " --json, -j Output results in JSON format" << std::endl;
std::cout << " --verbose, -v Enable verbose output with detailed result information" << std::endl;
std::cout << " --features List supported features and exit" << std::endl;
std::cout << " --help Display this help" << std::endl;
std::cout << " --version Show version information and exit" << std::endl;
std::cout << std::endl;
Expand Down Expand Up @@ -238,6 +262,29 @@
std::exit(0);
}

if (m_parser.isSet(m_featuresOption)) {
// 静态特性列表(编译时确定)
static const QStringList kFeatures = supportedFeatures();

if (m_parser.isSet(m_jsonOption)) {
// JSON output
QJsonObject root;
root["type"] = QStringLiteral("features");
QJsonArray featuresArray;
for (const QString &feature : kFeatures) {
featuresArray.append(feature);
}
root["features"] = featuresArray;
root["count"] = kFeatures.size();
QJsonDocument doc(root);
std::cout << doc.toJson(QJsonDocument::Indented).toStdString() << std::endl;
} else {
// Space-separated text output
std::cout << kFeatures.join(' ').toStdString() << std::endl;
}
std::exit(0);
}

QStringList positionalArgs = m_parser.positionalArguments();

// For preview subcommand, the positional args are: [<keyword>] <path1> [path2 ...]
Expand Down
8 changes: 8 additions & 0 deletions src/dfm-search/dfm-search-client/cli_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ class CliOptions
*/
void printHelp() const;

/**
* @brief 返回 --features 输出的静态特性列表(编译时确定,26 项)
*
* 提取为静态方法以便单元测试验证列表完整性,无需启动 CLI 进程。
*/
static QStringList supportedFeatures();

private:
void setupOptions();
bool validateConfig(SearchCliConfig &config);
Expand Down Expand Up @@ -135,6 +142,7 @@ class CliOptions
QCommandLineOption m_sizeMinOption;
QCommandLineOption m_sizeMaxOption;
QCommandLineOption m_versionOption;
QCommandLineOption m_featuresOption;
};

} // namespace dfmsearch
Expand Down
Loading