cf12defd28
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
438 lines
16 KiB
C++
438 lines
16 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2004 David Faure <faure@kde.org>
|
|
|
|
SPDX-License-Identifier: LGPL-2.0-only
|
|
*/
|
|
|
|
#include <KUser>
|
|
#include <QApplication>
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QTemporaryDir>
|
|
#include <QTest>
|
|
#include <QThread>
|
|
#include <kurlcompletion.h>
|
|
#include <qplatformdefs.h>
|
|
|
|
class KUrlCompletionTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
private Q_SLOTS:
|
|
void test();
|
|
|
|
public:
|
|
KUrlCompletionTest()
|
|
{
|
|
#ifdef NO_WAIT // kurlcompletiontest-nowait sets this, to test what happens on slower systems (or systems with many dirs or users)
|
|
qputenv("KURLCOMPLETION_WAIT", "1"); // 1ms, too short for a full listing of /usr/bin, but at least give a chance for a few items in the result
|
|
#endif
|
|
}
|
|
~KUrlCompletionTest() override
|
|
{
|
|
teardown();
|
|
}
|
|
void runAllTests();
|
|
void setup();
|
|
void teardown();
|
|
void testLocalRelativePath();
|
|
void testLocalAbsolutePath();
|
|
void testLocalURL();
|
|
void testEmptyCwd();
|
|
void testBug346920();
|
|
void testInvalidProtocol();
|
|
void testUser();
|
|
void testCancel();
|
|
|
|
// remember to register new test methods in runAllTests
|
|
|
|
private:
|
|
void waitForCompletion(KUrlCompletion *completion);
|
|
KUrlCompletion *m_completion;
|
|
KUrlCompletion *m_completionWithMimeFilter;
|
|
QTemporaryDir *m_tempDir;
|
|
QUrl m_dirURL;
|
|
QString m_dir;
|
|
KUrlCompletion *m_completionEmptyCwd;
|
|
};
|
|
|
|
void KUrlCompletionTest::setup()
|
|
{
|
|
qDebug();
|
|
m_completion = new KUrlCompletion;
|
|
m_completionWithMimeFilter = new KUrlCompletion;
|
|
m_completionWithMimeFilter->setMimeTypeFilters({QStringLiteral("text/x-c++src")});
|
|
m_tempDir = new QTemporaryDir;
|
|
m_dir = m_tempDir->path();
|
|
m_dir += QLatin1String("/Dir With#Spaces/");
|
|
QDir().mkdir(m_dir);
|
|
qDebug() << "m_dir=" << m_dir;
|
|
m_completion->setDir(QUrl::fromLocalFile(m_dir));
|
|
m_completionWithMimeFilter->setDir(m_completion->dir());
|
|
m_dirURL = QUrl::fromLocalFile(m_dir);
|
|
|
|
QFile f1(m_dir + QStringLiteral("/file1"));
|
|
bool ok = f1.open(QIODevice::WriteOnly);
|
|
QVERIFY(ok);
|
|
f1.close();
|
|
|
|
QFile f2(m_dir + QStringLiteral("/file#a"));
|
|
ok = f2.open(QIODevice::WriteOnly);
|
|
QVERIFY(ok);
|
|
f2.close();
|
|
|
|
QFile f3(m_dir + QStringLiteral("/file."));
|
|
ok = f3.open(QIODevice::WriteOnly);
|
|
QVERIFY(ok);
|
|
f3.close();
|
|
|
|
QFile f4(m_dir + QStringLiteral("/source.cpp"));
|
|
ok = f4.open(QIODevice::WriteOnly);
|
|
QVERIFY(ok);
|
|
f4.close();
|
|
|
|
QFile f5(m_dir + QStringLiteral("/source.php"));
|
|
ok = f5.open(QIODevice::WriteOnly);
|
|
QVERIFY(ok);
|
|
f5.close();
|
|
|
|
QDir().mkdir(m_dir + QStringLiteral("/file_subdir"));
|
|
QDir().mkdir(m_dir + QStringLiteral("/.1_hidden_file_subdir"));
|
|
QDir().mkdir(m_dir + QStringLiteral("/.2_hidden_file_subdir"));
|
|
|
|
m_completionEmptyCwd = new KUrlCompletion;
|
|
m_completionEmptyCwd->setDir(QUrl());
|
|
}
|
|
|
|
void KUrlCompletionTest::teardown()
|
|
{
|
|
delete m_completion;
|
|
m_completion = nullptr;
|
|
delete m_completionWithMimeFilter;
|
|
m_completionWithMimeFilter = nullptr;
|
|
delete m_tempDir;
|
|
m_tempDir = nullptr;
|
|
delete m_completionEmptyCwd;
|
|
m_completionEmptyCwd = nullptr;
|
|
}
|
|
|
|
void KUrlCompletionTest::waitForCompletion(KUrlCompletion *completion)
|
|
{
|
|
while (completion->isRunning()) {
|
|
qDebug() << "waiting for thread...";
|
|
QTest::qWait(5);
|
|
}
|
|
// The thread emitted a signal, process it.
|
|
qApp->sendPostedEvents(nullptr, QEvent::MetaCall);
|
|
}
|
|
|
|
void KUrlCompletionTest::testLocalRelativePath()
|
|
{
|
|
qDebug();
|
|
// Completion from relative path, with two matches
|
|
m_completion->makeCompletion(QStringLiteral("f"));
|
|
waitForCompletion(m_completion);
|
|
QStringList comp1all = m_completion->allMatches();
|
|
qDebug() << comp1all;
|
|
QCOMPARE(comp1all.count(), 4);
|
|
QVERIFY(comp1all.contains(QLatin1String("file1")));
|
|
QVERIFY(comp1all.contains(QLatin1String("file#a")));
|
|
QVERIFY(comp1all.contains(QLatin1String("file.")));
|
|
QVERIFY(comp1all.contains(QLatin1String("file_subdir/")));
|
|
QString comp1 = m_completion->replacedPath(QStringLiteral("file1")); // like KUrlRequester does
|
|
QCOMPARE(comp1, QStringLiteral("file1"));
|
|
|
|
// Completion from relative path
|
|
qDebug() << "\nnow completing on 'file#'";
|
|
m_completion->makeCompletion(QStringLiteral("file#"));
|
|
QVERIFY(!m_completion->isRunning()); // last listing reused
|
|
QStringList compall = m_completion->allMatches();
|
|
qDebug() << compall;
|
|
QCOMPARE(compall.count(), 1);
|
|
QCOMPARE(compall.first(), QStringLiteral("file#a"));
|
|
QString comp2 = m_completion->replacedPath(compall.first()); // like KUrlRequester does
|
|
QCOMPARE(comp2, QStringLiteral("file#a"));
|
|
|
|
// Completion with empty string
|
|
qDebug() << "\nnow completing on ''";
|
|
m_completion->makeCompletion(QLatin1String(""));
|
|
waitForCompletion(m_completion);
|
|
QStringList compEmpty = m_completion->allMatches();
|
|
QCOMPARE(compEmpty.count(), 0);
|
|
|
|
m_completion->makeCompletion(QStringLiteral("."));
|
|
waitForCompletion(m_completion);
|
|
const auto compAllHidden = m_completion->allMatches();
|
|
QCOMPARE(compAllHidden.count(), 2);
|
|
QVERIFY(compAllHidden.contains(QLatin1String(".1_hidden_file_subdir/")));
|
|
QVERIFY(compAllHidden.contains(QLatin1String(".2_hidden_file_subdir/")));
|
|
|
|
// Completion with '.2', should find only hidden folders starting with '2'
|
|
m_completion->makeCompletion(QStringLiteral(".2"));
|
|
waitForCompletion(m_completion);
|
|
const auto compHiddenStartingWith2 = m_completion->allMatches();
|
|
QCOMPARE(compHiddenStartingWith2.count(), 1);
|
|
QVERIFY(compHiddenStartingWith2.contains(QLatin1String(".2_hidden_file_subdir/")));
|
|
|
|
// Completion with 'file.', should only find one file
|
|
m_completion->makeCompletion(QStringLiteral("file."));
|
|
waitForCompletion(m_completion);
|
|
const auto compFileEndingWithDot = m_completion->allMatches();
|
|
QCOMPARE(compFileEndingWithDot.count(), 1);
|
|
QVERIFY(compFileEndingWithDot.contains(QLatin1String("file.")));
|
|
|
|
// Completion with 'source' should only find the C++ file
|
|
m_completionWithMimeFilter->makeCompletion(QStringLiteral("source"));
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compSourceFile = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compSourceFile.count(), 1);
|
|
QVERIFY(compSourceFile.contains(QLatin1String("source.cpp")));
|
|
|
|
// But it should also be able to find folders
|
|
m_completionWithMimeFilter->makeCompletion(QStringLiteral("file_subdir"));
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compMimeFolder = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compMimeFolder.count(), 1);
|
|
QVERIFY(compMimeFolder.contains(QLatin1String("file_subdir/")));
|
|
}
|
|
|
|
void KUrlCompletionTest::testLocalAbsolutePath()
|
|
{
|
|
// Completion from absolute path
|
|
qDebug() << m_dir + "file#";
|
|
m_completion->makeCompletion(m_dir + "file#");
|
|
waitForCompletion(m_completion);
|
|
QStringList compall = m_completion->allMatches();
|
|
qDebug() << compall;
|
|
QCOMPARE(compall.count(), 1);
|
|
QString comp = compall.first();
|
|
QCOMPARE(comp, QString(m_dir + "file#a"));
|
|
comp = m_completion->replacedPath(comp); // like KUrlRequester does
|
|
QCOMPARE(comp, QString(m_dir + "file#a"));
|
|
|
|
// Completion with '.', should find all hidden folders
|
|
m_completion->makeCompletion(m_dir + QLatin1Char('.'));
|
|
waitForCompletion(m_completion);
|
|
const auto compAllHidden = m_completion->allMatches();
|
|
QCOMPARE(compAllHidden.count(), 2);
|
|
QVERIFY(compAllHidden.contains(m_dir + ".1_hidden_file_subdir/"));
|
|
QVERIFY(compAllHidden.contains(m_dir + ".2_hidden_file_subdir/"));
|
|
|
|
// Completion with '.2', should find only hidden folders starting with '2'
|
|
m_completion->makeCompletion(m_dir + ".2");
|
|
waitForCompletion(m_completion);
|
|
const auto compHiddenStartingWith2 = m_completion->allMatches();
|
|
QCOMPARE(compHiddenStartingWith2.count(), 1);
|
|
QVERIFY(compHiddenStartingWith2.contains(m_dir + ".2_hidden_file_subdir/"));
|
|
|
|
// Completion with 'file.', should only find one file
|
|
m_completion->makeCompletion(m_dir + "file.");
|
|
waitForCompletion(m_completion);
|
|
const auto compFileEndingWithDot = m_completion->allMatches();
|
|
QCOMPARE(compFileEndingWithDot.count(), 1);
|
|
QVERIFY(compFileEndingWithDot.contains(m_dir + "file."));
|
|
|
|
// Completion with 'source' should only find the C++ file
|
|
m_completionWithMimeFilter->makeCompletion(m_dir + "source");
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compSourceFile = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compSourceFile.count(), 1);
|
|
QVERIFY(compSourceFile.contains(m_dir + "source.cpp"));
|
|
|
|
// But it should also be able to find folders
|
|
m_completionWithMimeFilter->makeCompletion(m_dir + "file_subdir");
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compMimeFolder = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compMimeFolder.count(), 1);
|
|
QVERIFY(compMimeFolder.contains(m_dir + "file_subdir/"));
|
|
}
|
|
|
|
void KUrlCompletionTest::testLocalURL()
|
|
{
|
|
// Completion from URL
|
|
qDebug();
|
|
QUrl url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + "file");
|
|
m_completion->makeCompletion(url.toString());
|
|
waitForCompletion(m_completion);
|
|
QStringList comp1all = m_completion->allMatches();
|
|
qDebug() << comp1all;
|
|
QCOMPARE(comp1all.count(), 4);
|
|
qDebug() << "Looking for" << m_dirURL.toString() + "file1";
|
|
QVERIFY(comp1all.contains(m_dirURL.toString() + "file1"));
|
|
qDebug() << "Looking for" << m_dirURL.toString() + "file.";
|
|
QVERIFY(comp1all.contains(m_dirURL.toString() + "file."));
|
|
QVERIFY(comp1all.contains(m_dirURL.toString() + "file_subdir/"));
|
|
QString filehash = m_dirURL.toString() + "file%23a";
|
|
qDebug() << "Looking for" << filehash;
|
|
QVERIFY(comp1all.contains(filehash));
|
|
QString filehashPath = m_completion->replacedPath(filehash); // note that it returns a path!!
|
|
qDebug() << filehashPath;
|
|
QCOMPARE(filehashPath, QString(m_dirURL.toLocalFile() + "file#a"));
|
|
|
|
// Completion from URL with no match
|
|
url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + "foobar");
|
|
qDebug() << "makeCompletion(" << url << ")";
|
|
QString comp2 = m_completion->makeCompletion(url.toString());
|
|
QVERIFY(comp2.isEmpty());
|
|
waitForCompletion(m_completion);
|
|
QVERIFY(m_completion->allMatches().isEmpty());
|
|
|
|
// Completion from URL with a ref -> no match
|
|
url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + 'f');
|
|
url.setFragment(QStringLiteral("ref"));
|
|
qDebug() << "makeCompletion(" << url << ")";
|
|
m_completion->makeCompletion(url.toString());
|
|
waitForCompletion(m_completion);
|
|
QVERIFY(m_completion->allMatches().isEmpty());
|
|
|
|
// Completion with '.', should find all hidden folders
|
|
qDebug() << "makeCompletion(" << (m_dirURL.toString() + QLatin1Char('.')) << ")";
|
|
m_completion->makeCompletion(m_dirURL.toString() + QLatin1Char('.'));
|
|
waitForCompletion(m_completion);
|
|
const auto compAllHidden = m_completion->allMatches();
|
|
QCOMPARE(compAllHidden.count(), 2);
|
|
QVERIFY(compAllHidden.contains(m_dirURL.toString() + ".1_hidden_file_subdir/"));
|
|
QVERIFY(compAllHidden.contains(m_dirURL.toString() + ".2_hidden_file_subdir/"));
|
|
|
|
// Completion with '.2', should find only hidden folders starting with '2'
|
|
url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + ".2");
|
|
qDebug() << "makeCompletion(" << url << ")";
|
|
m_completion->makeCompletion(url.toString());
|
|
waitForCompletion(m_completion);
|
|
const auto compHiddenStartingWith2 = m_completion->allMatches();
|
|
QCOMPARE(compHiddenStartingWith2.count(), 1);
|
|
QVERIFY(compHiddenStartingWith2.contains(m_dirURL.toString() + QStringLiteral(".2_hidden_file_subdir/")));
|
|
|
|
// Completion with 'file.', should only find one file
|
|
url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + QStringLiteral("file."));
|
|
qDebug() << "makeCompletion(" << url << ")";
|
|
m_completion->makeCompletion(url.toString());
|
|
waitForCompletion(m_completion);
|
|
const auto compFileEndingWithDot = m_completion->allMatches();
|
|
QCOMPARE(compFileEndingWithDot.count(), 1);
|
|
QVERIFY(compFileEndingWithDot.contains(m_dirURL.toString() + QStringLiteral("file.")));
|
|
|
|
// Completion with 'source' should only find the C++ file
|
|
m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + QStringLiteral("source"));
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compSourceFile = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compSourceFile.count(), 1);
|
|
QVERIFY(compSourceFile.contains(m_dirURL.toString() + QStringLiteral("source.cpp")));
|
|
|
|
// But it should also be able to find folders
|
|
m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + QStringLiteral("file_subdir"));
|
|
waitForCompletion(m_completionWithMimeFilter);
|
|
const auto compMimeFolder = m_completionWithMimeFilter->allMatches();
|
|
QCOMPARE(compMimeFolder.count(), 1);
|
|
QVERIFY(compMimeFolder.contains(m_dirURL.toString() + QStringLiteral("file_subdir/")));
|
|
}
|
|
|
|
void KUrlCompletionTest::testEmptyCwd()
|
|
{
|
|
// Completion with empty string (with a KUrlCompletion whose cwd is "")
|
|
qDebug() << "\nnow completing on '' with empty cwd";
|
|
m_completionEmptyCwd->makeCompletion(QLatin1String(""));
|
|
waitForCompletion(m_completionEmptyCwd);
|
|
QStringList compEmpty = m_completionEmptyCwd->allMatches();
|
|
QCOMPARE(compEmpty.count(), 0);
|
|
}
|
|
|
|
void KUrlCompletionTest::testBug346920()
|
|
{
|
|
m_completionEmptyCwd->makeCompletion(QStringLiteral("~/."));
|
|
waitForCompletion(m_completionEmptyCwd);
|
|
m_completionEmptyCwd->allMatches();
|
|
// just don't crash
|
|
}
|
|
|
|
void KUrlCompletionTest::testInvalidProtocol()
|
|
{
|
|
m_completion->makeCompletion(QStringLiteral(":/"));
|
|
waitForCompletion(m_completion);
|
|
m_completion->allMatches();
|
|
// just don't crash
|
|
}
|
|
|
|
void KUrlCompletionTest::testUser()
|
|
{
|
|
m_completionEmptyCwd->makeCompletion(QStringLiteral("~"));
|
|
waitForCompletion(m_completionEmptyCwd);
|
|
const auto matches = m_completionEmptyCwd->allMatches();
|
|
const QStringList allUsers = KUser::allUserNames();
|
|
if (!allUsers.isEmpty()) {
|
|
Q_ASSERT(!matches.isEmpty());
|
|
}
|
|
for (const auto &user : allUsers) {
|
|
QVERIFY2(matches.contains(QLatin1Char('~') + user), qPrintable(matches.join(QLatin1Char(' '))));
|
|
}
|
|
|
|
// Check that the same query doesn't re-list
|
|
m_completionEmptyCwd->makeCompletion(QStringLiteral("~"));
|
|
QVERIFY(!m_completionEmptyCwd->isRunning());
|
|
QCOMPARE(m_completionEmptyCwd->allMatches(), matches);
|
|
}
|
|
|
|
// Test cancelling a running thread
|
|
// In a normal run (./kurlcompletiontest) and a reasonable amount of files, we have few chances of making this happen
|
|
// But in a "nowait" run (./kurlcompletiontest-nowait), this will cancel the thread before it even starts listing the dir.
|
|
void KUrlCompletionTest::testCancel()
|
|
{
|
|
KUrlCompletion comp;
|
|
comp.setDir(QUrl::fromLocalFile("/usr/bin"));
|
|
comp.makeCompletion(QStringLiteral("g"));
|
|
const QStringList matchesG = comp.allMatches();
|
|
// We get many matches in a normal run, and usually 0 matches when testing "no wait" (thread is sleeping) -> this is where this method can test cancelling
|
|
// qDebug() << "got" << matchesG.count() << "matches";
|
|
bool done = !comp.isRunning();
|
|
|
|
// Doing the same search again, should hopefully not restart everything from scratch
|
|
comp.makeCompletion(QStringLiteral("g"));
|
|
const QStringList matchesG2 = comp.allMatches();
|
|
QVERIFY(matchesG2.count() >= matchesG.count());
|
|
if (done) {
|
|
QVERIFY(!comp.isRunning()); // it had no reason to restart
|
|
}
|
|
done = !comp.isRunning();
|
|
|
|
// Search for something else, should reuse dir listing but not mix up results
|
|
comp.makeCompletion(QStringLiteral("a"));
|
|
if (done) {
|
|
QVERIFY(!comp.isRunning()); // it had no reason to restart
|
|
}
|
|
const QStringList matchesA = comp.allMatches();
|
|
// qDebug() << "got" << matchesA.count() << "matches";
|
|
for (const QString &match : matchesA) {
|
|
QVERIFY2(!match.startsWith(QLatin1Char('g')), qPrintable(match));
|
|
}
|
|
waitForCompletion(&comp);
|
|
const QStringList matchesB = comp.allMatches();
|
|
for (const QString &match : matchesB) {
|
|
QVERIFY2(!match.startsWith(QLatin1Char('g')), qPrintable(match));
|
|
}
|
|
}
|
|
|
|
void KUrlCompletionTest::test()
|
|
{
|
|
runAllTests();
|
|
// Try again, with another QTemporaryDir (to check that the caching doesn't give us wrong results)
|
|
runAllTests();
|
|
}
|
|
|
|
void KUrlCompletionTest::runAllTests()
|
|
{
|
|
setup();
|
|
testLocalRelativePath();
|
|
testLocalAbsolutePath();
|
|
testLocalURL();
|
|
testEmptyCwd();
|
|
testBug346920();
|
|
testInvalidProtocol();
|
|
testUser();
|
|
testCancel();
|
|
teardown();
|
|
}
|
|
|
|
QTEST_MAIN(KUrlCompletionTest)
|
|
|
|
#include "kurlcompletiontest.moc"
|