Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QtCore/QScopeGuard>
#include <QtCore/QTemporaryDir>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtHelp/QHelpEngineCore>
#include <QtHelp/QHelpSearchEngineCore>
#include <QtHelp/QHelpSearchResult>
class tst_QHelpSearchEngineCore : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void search_data();
void search();
private:
QTemporaryDir m_dir;
std::unique_ptr<QHelpEngineCore> m_helpEngine;
std::unique_ptr<QHelpSearchEngineCore> m_searchEngine;
};
static bool addFile(QSqlQuery &query, int fileId, const QString &name, const QByteArray &data)
{
query.prepare("INSERT INTO FileDataTable (Id, Data) VALUES (?, ?)");
query.addBindValue(fileId);
query.addBindValue(qCompress(data));
if (!query.exec())
return false;
query.prepare("INSERT INTO FileNameTable (FolderId, Name, FileId, Title) VALUES (1, ?, ?, ?)");
query.addBindValue(name);
query.addBindValue(fileId);
query.addBindValue(name);
if (!query.exec())
return false;
// Make the file part of every filter attribute set of test.qch.
query.prepare("INSERT INTO FileFilterTable (FilterAttributeId, FileId) "
"SELECT Id, ? FROM FilterAttributeTable");
query.addBindValue(fileId);
return query.exec();
}
static QByteArray html(const QByteArray &word)
{
return "<html><head><title>" + word + "</title></head><body><p>" + word
+ "</p></body></html>";
}
void tst_QHelpSearchEngineCore::initTestCase()
{
QVERIFY(m_dir.isValid());
const QString qchFile = m_dir.filePath("test.qch");
QVERIFY(QFile::copy(QLatin1String(SRCDIR) + "/data/test.qch", qchFile));
QVERIFY(QFile::setPermissions(qchFile, QFile::WriteUser | QFile::ReadUser));
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "searchdb");
const auto cleanup = qScopeGuard([&db] {
db.close();
db = {};
QSqlDatabase::removeDatabase("searchdb");
});
db.setDatabaseName(qchFile);
QVERIFY(db.open());
QSqlQuery query(db);
QVERIFY(addFile(query, 100, "extra.html", html("quokkahtml")));
QVERIFY(addFile(query, 101, "page.htm", html("quokkahtm")));
QVERIFY(addFile(query, 102, "notes.txt", "quokkatxt"));
QVERIFY(addFile(query, 103, "style.css", "quokkacss { color: red; }"));
}
m_helpEngine.reset(new QHelpEngineCore(m_dir.filePath("collection.qhc")));
m_helpEngine->setReadOnly(false);
QVERIFY(m_helpEngine->setupData());
QVERIFY(m_helpEngine->registerDocumentation(qchFile));
m_searchEngine.reset(new QHelpSearchEngineCore(m_helpEngine.get()));
QSignalSpy indexingSpy(m_searchEngine.get(), &QHelpSearchEngineCore::indexingFinished);
m_searchEngine->reindexDocumentation();
QVERIFY(indexingSpy.wait(10000));
}
void tst_QHelpSearchEngineCore::search_data()
{
QTest::addColumn<QString>("word");
QTest::addColumn<QString>("expectedFile");
QTest::addColumn<bool>("needsFullQtHelp");
QTest::newRow("html") << "quokkahtml" << "extra.html" << true;
QTest::newRow("htm") << "quokkahtm" << "page.htm" << true;
QTest::newRow("txt") << "quokkatxt" << "notes.txt" << false;
QTest::newRow("css") << "quokkacss" << QString() << false;
}
void tst_QHelpSearchEngineCore::search()
{
QFETCH(QString, word);
QFETCH(QString, expectedFile);
QFETCH(bool, needsFullQtHelp);
#ifndef TEST_FULLQTHELP
if (needsFullQtHelp)
QSKIP("The contents of HTML files are indexed only with fullqthelp.");
#else
Q_UNUSED(needsFullQtHelp);
#endif
QSignalSpy searchingSpy(m_searchEngine.get(), &QHelpSearchEngineCore::searchingFinished);
m_searchEngine->search(word);
QVERIFY(searchingSpy.wait(10000));
const QList<QHelpSearchResult> results
= m_searchEngine->searchResults(0, m_searchEngine->searchResultCount());
QSet<QString> files;
for (const QHelpSearchResult &result : results)
files.insert(result.url().path().section(u'/', -1));
if (expectedFile.isEmpty())
QVERIFY2(files.isEmpty(), qPrintable(QStringList(files.cbegin(), files.cend()).join(u',')));
else
QCOMPARE(files, QSet<QString>{expectedFile});
}
QTEST_MAIN(tst_QHelpSearchEngineCore)
#include "tst_qhelpsearchenginecore.moc"