Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,880 @@
/*
This file is part of the KIO framework tests
SPDX-FileCopyrightText: 2016 Albert Astals Cid <aacid@kde.org>
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kfilewidget.h"
#include <QLabel>
#include <QLoggingCategory>
#include <QSignalSpy>
#include <QStandardPaths>
#include <QTemporaryDir>
#include <QTest>
#include <QUrl>
#include "../utils_p.h"
#include "kiotesthelper.h" // createTestFile
#include <KDirLister>
#include <KFileFilterCombo>
#include <KUrlComboBox>
#include <kdiroperator.h>
#include <kurlnavigator.h>
#include <KLocalizedString>
#include <KWindowSystem>
#include <QAbstractItemView>
#include <QDialog>
#include <QDropEvent>
#include <QLineEdit>
#include <QList>
#include <QMimeData>
#include <QStringList>
#include <QStringLiteral>
#include <QUrl>
Q_DECLARE_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW)
Q_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW, "kf.kio.kfilewidgets.kfilewidget", QtInfoMsg)
static QWidget *findLocationLabel(QWidget *parent)
{
const QList<QLabel *> labels = parent->findChildren<QLabel *>();
for (QLabel *label : labels) {
if (label->text() == i18n("&Name:") || label->text() == i18n("Name:")) {
return label->buddy();
}
}
Q_ASSERT(false);
return nullptr;
}
/**
* Unit test for KFileWidget
*/
class KFileWidgetTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testFilterCombo();
void testFocusOnLocationEdit();
void testFocusOnLocationEditChangeDir();
void testFocusOnLocationEditChangeDir2();
void testFocusOnDirOps();
void testGetStartUrl();
void testSetSelection_data();
void testSetSelectedUrl_data();
void testSetSelectedUrl();
void testPreserveFilenameWhileNavigating();
void testEnterUrl_data();
void testEnterUrl();
void testSetFilterForSave_data();
void testSetFilterForSave();
void testExtensionForSave_data();
void testExtensionForSave();
void testFilterChange();
void testDropFile_data();
void testDropFile();
void testCreateNestedNewFolders();
void testTokenize_data();
void testTokenize();
void testTokenizeForSave_data();
void testTokenizeForSave();
};
void KFileWidgetTest::initTestCase()
{
QStandardPaths::setTestModeEnabled(true);
QVERIFY(QDir::homePath() != QDir::tempPath());
}
void KFileWidgetTest::testFilterCombo()
{
KFileWidget fw(QUrl(QStringLiteral("kfiledialog:///SaveDialog")), nullptr);
fw.setOperationMode(KFileWidget::Saving);
fw.setMode(KFile::File);
const KFileFilter wordFilter = KFileFilter::fromFilterString("*.xml *.a|Word 2003 XML (.xml)").first();
const KFileFilter odtFilter = KFileFilter::fromFilterString("*.odt|ODF Text Document (.odt)").first();
const KFileFilter docBookFilter = KFileFilter::fromFilterString("*.xml *.b|DocBook (.xml)").first();
const KFileFilter rawFilter = KFileFilter::fromFilterString("*|Raw (*)").first();
fw.setFilters({wordFilter, odtFilter, docBookFilter, rawFilter});
// default filter is selected
QCOMPARE(fw.currentFilter(), wordFilter);
// setUrl runs with blocked signals, so use setUrls.
// auto-select ODT filter via filename
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
QCOMPARE(fw.currentFilter(), odtFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
// select 2nd duplicate XML filter (see bug 407642)
fw.filterWidget()->setCurrentFilter(docBookFilter);
QCOMPARE(fw.currentFilter(), docBookFilter);
// when editing the filter, there is delay to avoid refreshing the KDirOperator after each keypress
QTest::qWait(350);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
// keep filter after file change with same extension
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test2.xml")));
QCOMPARE(fw.currentFilter(), docBookFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test2.xml"));
// back to the non-xml / ODT filter
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
QCOMPARE(fw.currentFilter(), odtFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
// auto-select 1st XML filter
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.xml")));
QCOMPARE(fw.currentFilter(), wordFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
// select Raw '*' filter
fw.filterWidget()->setCurrentFilter(rawFilter);
QCOMPARE(fw.currentFilter(), rawFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
// keep Raw '*' filter with matching file extension
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
QCOMPARE(fw.currentFilter(), rawFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
// keep Raw '*' filter with non-matching file extension
fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.core")));
QCOMPARE(fw.currentFilter(), rawFilter);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.core"));
// select 2nd XML filter
fw.filterWidget()->setCurrentFilter(docBookFilter);
QCOMPARE(fw.currentFilter(), docBookFilter);
// when editing the filter, there is delay to avoid refreshing the KDirOperator after each keypress
QTest::qWait(350);
QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
}
void KFileWidgetTest::testFocusOnLocationEdit()
{
if (KWindowSystem::isPlatformWayland()) {
QSKIP("X11 only, activation issue");
return;
}
KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
fw.show();
fw.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&fw));
QWidget *label = findLocationLabel(&fw);
QVERIFY(label);
QVERIFY(label->hasFocus());
}
void KFileWidgetTest::testFocusOnLocationEditChangeDir()
{
if (KWindowSystem::isPlatformWayland()) {
QSKIP("X11 only, activation issue");
return;
}
KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
fw.show();
fw.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&fw));
QWidget *label = findLocationLabel(&fw);
QVERIFY(label);
QVERIFY(label->hasFocus());
}
void KFileWidgetTest::testFocusOnLocationEditChangeDir2()
{
if (KWindowSystem::isPlatformWayland()) {
QSKIP("X11 only, activation issue");
return;
}
KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
fw.show();
fw.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&fw));
fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
QWidget *label = findLocationLabel(&fw);
QVERIFY(label);
QVERIFY(label->hasFocus());
}
void KFileWidgetTest::testFocusOnDirOps()
{
if (KWindowSystem::isPlatformWayland()) {
QSKIP("X11 only, activation issue");
return;
}
KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
fw.show();
fw.activateWindow();
QVERIFY(QTest::qWaitForWindowActive(&fw));
const QList<KUrlNavigator *> nav = fw.findChildren<KUrlNavigator *>();
QCOMPARE(nav.count(), 1);
nav[0]->setFocus();
fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
const QList<KDirOperator *> ops = fw.findChildren<KDirOperator *>();
QCOMPARE(ops.count(), 1);
QVERIFY(ops[0]->hasFocus());
}
void KFileWidgetTest::testGetStartUrl()
{
QString recentDirClass;
QString outFileName;
QUrl localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachmentDir")), recentDirClass, outFileName);
QCOMPARE(recentDirClass, QStringLiteral(":attachmentDir"));
QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
QVERIFY(outFileName.isEmpty());
localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachments/foo.txt")), recentDirClass, outFileName);
QCOMPARE(recentDirClass, QStringLiteral(":attachments"));
QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
QCOMPARE(outFileName, QStringLiteral("foo.txt"));
}
void KFileWidgetTest::testSetSelection_data()
{
QTest::addColumn<QString>("baseDir");
QTest::addColumn<QString>("selection");
QTest::addColumn<QString>("expectedBaseDir");
QTest::addColumn<QString>("expectedCurrentText");
const QString baseDir = QDir::homePath();
// A nice filename to detect URL encoding issues
const QString fileName = QStringLiteral("some:fi#le");
// Bug 369216, kdialog calls setSelection(path)
QTest::newRow("path") << baseDir << baseDir + QLatin1Char('/') + fileName << baseDir << fileName;
QTest::newRow("differentPath") << QDir::rootPath() << baseDir + QLatin1Char('/') + fileName << baseDir << fileName;
// kdeplatformfiledialoghelper.cpp calls setSelection(URL as string)
QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName).toString() << baseDir << fileName;
// What if someone calls setSelection(fileName)? That breaks, hence e70f8134a2b in plasma-integration.git
QTest::newRow("filename") << baseDir << fileName << baseDir << fileName;
}
void KFileWidgetTest::testSetSelectedUrl_data()
{
QTest::addColumn<QString>("baseDir");
QTest::addColumn<QUrl>("selectionUrl");
QTest::addColumn<QString>("expectedBaseDir");
QTest::addColumn<QString>("expectedCurrentText");
const QString baseDir = QDir::homePath();
// A nice filename to detect URL encoding issues
const QString fileName = QStringLiteral("some:fi#le");
const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName);
QTest::newRow("path") << baseDir << fileUrl << baseDir << fileName;
QTest::newRow("differentPath") << QDir::rootPath() << fileUrl << baseDir << fileName;
QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName) << baseDir << fileName;
QUrl relativeUrl;
relativeUrl.setPath(fileName);
QTest::newRow("filename") << baseDir << relativeUrl << baseDir << fileName;
}
void KFileWidgetTest::testSetSelectedUrl()
{
// GIVEN
QFETCH(QString, baseDir);
QFETCH(QUrl, selectionUrl);
QFETCH(QString, expectedBaseDir);
QFETCH(QString, expectedCurrentText);
const QUrl baseUrl = QUrl::fromLocalFile(baseDir).adjusted(QUrl::StripTrailingSlash);
const QUrl expectedBaseUrl = QUrl::fromLocalFile(expectedBaseDir);
KFileWidget fw(baseUrl);
// WHEN
fw.setSelectedUrl(selectionUrl);
// THEN
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl);
QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
}
void KFileWidgetTest::testPreserveFilenameWhileNavigating() // bug 418711
{
// GIVEN
const QUrl url = QUrl::fromLocalFile(QDir::homePath());
KFileWidget fw(url);
fw.setOperationMode(KFileWidget::Saving);
fw.setMode(KFile::File);
QString baseDir = QDir::homePath();
if (baseDir.endsWith('/')) {
baseDir.chop(1);
}
const QString fileName = QStringLiteral("somefi#le");
const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName);
fw.setSelectedUrl(fileUrl);
const QUrl baseUrl = QUrl::fromLocalFile(baseDir);
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl);
QCOMPARE(fw.locationEdit()->currentText(), fileName);
// WHEN
fw.dirOperator()->cdUp();
// THEN
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
QCOMPARE(fw.locationEdit()->currentText(), fileName); // unchanged
}
void KFileWidgetTest::testEnterUrl_data()
{
QTest::addColumn<QUrl>("expectedUrl");
// Check if the root urls are well transformed into themself, otherwise
// when going up from file:///home/ it will become file:///home/user
QTest::newRow("file") << QUrl::fromLocalFile("/");
QTest::newRow("trash") << QUrl("trash:/");
QTest::newRow("sftp") << QUrl("sftp://127.0.0.1/");
}
void KFileWidgetTest::testEnterUrl()
{
// GIVEN
QFETCH(QUrl, expectedUrl);
// WHEN
// These lines are copied from src/filewidgets/kfilewidget.cpp
// void KFileWidgetPrivate::_k_enterUrl(const QUrl &url)
QUrl u(expectedUrl);
Utils::appendSlashToPath(u);
// THEN
QVERIFY(u.isValid());
QCOMPARE(u, expectedUrl);
}
void KFileWidgetTest::testSetFilterForSave_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<QString>("filter");
QTest::addColumn<QString>("expectedCurrentText");
QTest::addColumn<QString>("expectedSelectedFileName");
const QString filter = QStringLiteral("*.txt|Text files\n*.HTML|HTML files");
QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
// If an application provides a name without extension, then the
// displayed name will not receive an extension. It will however be
// appended when the dialog is closed.
QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt");
// If the file literally exists, then no new extension will be appended.
QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README");
// XXX perhaps the "extension" should not be modified when it does not
// match any of the existing types? Should "some.2019.txt" be expected?
QTest::newRow("some.2019") << "some.2019" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
// XXX be smarter and do not change the extension if one of the other
// filters match. Should "some.html" be expected?
QTest::newRow("some.html") << "some.html" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
}
void KFileWidgetTest::testSetFilterForSave()
{
QFETCH(QString, fileName);
QFETCH(QString, filter);
QFETCH(QString, expectedCurrentText);
QFETCH(QString, expectedSelectedFileName);
// Use a temporary directory since the presence of existing files
// influences whether an extension is automatically appended.
QTemporaryDir tempDir;
const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName));
createTestFile(tempDir.filePath("README"));
KFileWidget fw(dirUrl);
fw.setOperationMode(KFileWidget::Saving);
fw.setSelectedUrl(fileUrl);
// Calling setFilter has side-effects and changes the file name.
fw.setFilters(KFileFilter::fromFilterString(filter));
// Verify the expected populated name.
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
// QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
// which calls KFileWidget::selectedUrls().
// Accept the filename to ensure that a filename is selected.
connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
QList<QUrl> urls = fw.selectedUrls();
QCOMPARE(urls.size(), 1);
QCOMPARE(urls[0], expectedFileUrl);
}
void KFileWidgetTest::testExtensionForSave_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<QString>("filter");
QTest::addColumn<QString>("expectedCurrentText");
QTest::addColumn<QString>("expectedSelectedFileName");
const QString filter = QStringLiteral("*.txt *.text|Text files\n*.HTML|HTML files");
QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
// If an application provides a name without extension, then the
// displayed name will not receive an extension. It will however be
// appended when the dialog is closed.
QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt");
QTest::newRow("extensionless name") << "some.with_dot" << filter << QStringLiteral("some.with_dot") << QStringLiteral("some.with_dot.txt");
QTest::newRow("extensionless name") << "some.with.dots" << filter << QStringLiteral("some.with.dots") << QStringLiteral("some.with.dots.txt");
// If the file literally exists, then no new extension will be appended.
QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README");
}
void KFileWidgetTest::testExtensionForSave()
{
QFETCH(QString, fileName);
QFETCH(QString, filter);
QFETCH(QString, expectedCurrentText);
QFETCH(QString, expectedSelectedFileName);
// Use a temporary directory since the presence of existing files
// influences whether an extension is automatically appended.
QTemporaryDir tempDir;
const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName));
createTestFile(tempDir.filePath("README"));
KFileWidget fw(dirUrl);
fw.setOperationMode(KFileWidget::Saving);
// Calling setFilter has side-effects and changes the file name.
// The difference to testSetFilterForSave is that the filter is already set before the fileUrl
// is set, and will not be changed after.
fw.setFilters(KFileFilter::fromFilterString(filter));
fw.setSelectedUrl(fileUrl);
// Verify the expected populated name.
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
// QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
// which calls KFileWidget::selectedUrls().
// Accept the filename to ensure that a filename is selected.
connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
QList<QUrl> urls = fw.selectedUrls();
QCOMPARE(urls.size(), 1);
QCOMPARE(urls[0], expectedFileUrl);
}
void KFileWidgetTest::testFilterChange()
{
QTemporaryDir tempDir;
createTestFile(tempDir.filePath("some.c"));
bool created = QDir(tempDir.path()).mkdir("directory");
Q_ASSERT(created);
KFileWidget fw(QUrl::fromLocalFile(tempDir.path()));
fw.setOperationMode(KFileWidget::Saving);
fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("some.txt")));
const QList<KFileFilter> filters = KFileFilter::fromFilterString("*.txt|Txt\n*.c|C");
fw.setFilters(filters);
// Initial filename.
QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.txt"));
QCOMPARE(fw.filterWidget()->currentFilter(), filters[0]);
// Select type with an existing file.
fw.filterWidget()->setCurrentFilter(filters[1]);
// when editing the filter, there is delay to avoid refreshing the KDirOperator after each keypress
QTest::qWait(350);
QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.c"));
QCOMPARE(fw.filterWidget()->currentFilter(), filters[1]);
// Do not update extension if the current selection is a directory.
fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("directory")));
fw.filterWidget()->setCurrentFilter(filters[0]);
QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("directory"));
QCOMPARE(fw.filterWidget()->currentFilter(), filters[0]);
// The user types something into the combobox.
fw.filterWidget()->setCurrentText("qml");
QSignalSpy filterChangedSpy(&fw, &KFileWidget::filterChanged);
filterChangedSpy.wait();
QVERIFY(filterChangedSpy.count());
// Plain text is automatically upgraded to wildcard syntax
QCOMPARE(fw.dirOperator()->nameFilter(), "*qml*");
// But existing wildcards are left intact
fw.filterWidget()->setCurrentText("*.md");
filterChangedSpy.wait();
QVERIFY(filterChangedSpy.count());
QCOMPARE(fw.dirOperator()->nameFilter(), "*.md");
fw.filterWidget()->setCurrentText("[ab]c");
filterChangedSpy.wait();
QVERIFY(filterChangedSpy.count());
QCOMPARE(fw.dirOperator()->nameFilter(), "[ab]c");
fw.filterWidget()->setCurrentText("b?c");
filterChangedSpy.wait();
QVERIFY(filterChangedSpy.count());
QCOMPARE(fw.dirOperator()->nameFilter(), "b?c");
}
void KFileWidgetTest::testDropFile_data()
{
QTest::addColumn<QString>("dir");
QTest::addColumn<QString>("fileName");
QTest::addColumn<QString>("expectedCurrentText");
QTest::newRow("some.txt") << ""
<< "some.txt"
<< "some.txt";
QTest::newRow("subdir/some.txt") << "subdir"
<< "subdir/some.txt"
<< "some.txt";
}
void KFileWidgetTest::testDropFile()
{
QFETCH(QString, dir);
QFETCH(QString, fileName);
QFETCH(QString, expectedCurrentText);
// Use a temporary directory since the presence of existing files
// influences whether an extension is automatically appended.
QTemporaryDir tempDir;
QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
if (!dir.isEmpty()) {
createTestDirectory(tempDir.filePath(dir));
dirUrl = QUrl::fromLocalFile(tempDir.filePath(dir));
}
createTestFile(tempDir.filePath(fileName));
KFileWidget fileWidget(QUrl::fromLocalFile(tempDir.path()));
fileWidget.setOperationMode(KFileWidget::Saving);
fileWidget.setMode(KFile::File);
fileWidget.show();
QMimeData *mimeData = new QMimeData();
mimeData->setUrls(QList<QUrl>() << fileUrl);
KDirLister *dirLister = fileWidget.dirOperator()->dirLister();
QSignalSpy spy(dirLister, qOverload<>(&KCoreDirLister::completed));
QAbstractItemView *view = fileWidget.dirOperator()->view();
QVERIFY(view);
QDragEnterEvent event1(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
QVERIFY(qApp->sendEvent(view->viewport(), &event1));
// Fake drop
QDropEvent event(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
QVERIFY(qApp->sendEvent(view->viewport(), &event));
if (!dir.isEmpty()) {
// once we drop a file the dirlister scans the dir
// wait for the completed signal from the dirlister
QVERIFY(spy.wait());
}
// Verify the expected populated name.
QCOMPARE(fileWidget.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
QCOMPARE(fileWidget.locationEdit()->currentText(), expectedCurrentText);
// QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
// which calls KFileWidget::selectedUrls().
// Accept the filename to ensure that a filename is selected.
connect(&fileWidget, &KFileWidget::accepted, &fileWidget, &KFileWidget::accept);
QTest::keyClick(fileWidget.locationEdit(), Qt::Key_Return);
const QList<QUrl> urls = fileWidget.selectedUrls();
QCOMPARE(urls.size(), 1);
QCOMPARE(urls[0], fileUrl);
}
void KFileWidgetTest::testCreateNestedNewFolders()
{
// when creating multiple nested new folders in the "save as" dialog, where folders are
// created and entered, kdirlister would hit an assert (in reinsert()), bug 408801
QTemporaryDir tempDir;
QVERIFY(tempDir.isValid());
const QString dir = tempDir.path();
const QUrl url = QUrl::fromLocalFile(dir);
KFileWidget fw(url);
fw.setOperationMode(KFileWidget::Saving);
fw.setMode(KFile::File);
QString currentPath = dir;
// create the nested folders
for (int i = 1; i < 6; ++i) {
fw.dirOperator()->mkdir();
QDialog *dialog;
// QTRY_ because a NameFinderJob could be running and the dialog will be shown when
// it finishes.
QTRY_VERIFY(dialog = fw.findChild<QDialog *>());
QLineEdit *lineEdit = dialog->findChild<QLineEdit *>();
QVERIFY(lineEdit);
const QString name = QStringLiteral("folder%1").arg(i);
lineEdit->setText(name);
// simulate the time the user will take to type the new folder name
QTest::qWait(1000);
dialog->accept();
currentPath += QLatin1Char('/') + name;
// Wait till the filewidget changes to the new folder
QTRY_COMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(), currentPath);
}
}
void KFileWidgetTest::testTokenize_data()
{
// Real filename (as in how they are stored in the fs)
QTest::addColumn<QStringList>("fileNames");
// Escaped value of the text-box in the dialog
QTest::addColumn<QString>("expectedCurrentText");
QTest::newRow("simple") << QStringList{"test2"} << QString("test2");
// When a single file with space is selected, it is _not_ quoted ...
QTest::newRow("space-single-file") << QStringList{"test space"} << QString("test space");
// However, when multiple files are selected, they are quoted
QTest::newRow("space-multi-file") << QStringList{"test space", "test2"} << QString("\"test space\" \"test2\"");
// All quotes in names should be escaped, however since this is a single
// file, the whole name will not be escaped.
QTest::newRow("quote-single-file") << QStringList{"test\"quote"} << QString("test\\\"quote");
// Escape multiple files. Files should also be wrapped in ""
// Note that we are also testing quote at the end of the name
QTest::newRow("quote-multi-file") << QStringList{"test\"quote", "test2-quote\"", "test"} << QString("\"test\\\"quote\" \"test2-quote\\\"\" \"test\"");
// Ok, enough with quotes... lets do some backslashes
// Backslash literals in file names - Unix only case
QTest::newRow("backslash-single-file") << QStringList{"test\\backslash"} << QString("test\\\\backslash");
QTest::newRow("backslash-multi-file") << QStringList{"test\\back\\slash", "test"} << QString("\"test\\\\back\\\\slash\" \"test\"");
QTest::newRow("double-backslash-multi-file") << QStringList{"test\\\\back\\slash", "test"} << QString("\"test\\\\\\\\back\\\\slash\" \"test\"");
QTest::newRow("double-backslash-end") << QStringList{"test\\\\"} << QString("test\\\\\\\\");
QTest::newRow("single-backslash-end") << QStringList{"some thing", "test\\"} << QString("\"some thing\" \"test\\\\\"");
QTest::newRow("sharp") << QStringList{"some#thing"} << QString("some#thing");
// Filenames beginning with ':'; QDir::isAbsolutePath() always returns true
// in that case, #322837
QTest::newRow("file-beginning-with-colon") << QStringList{":test2"} << QString{":test2"};
QTest::newRow("multiple-files-beginning-with-colon") << QStringList{":test space", ":test2"} << QString{"\":test space\" \":test2\""};
// # 473228
QTest::newRow("file-beginning-with-something-that-looks-like-a-url-scheme") << QStringList{"Hello: foo.txt"} << QString{"Hello: foo.txt"};
QTest::newRow("file-beginning-with-something-that-looks-like-a-file-url-scheme") << QStringList{"file: /foo.txt"} << QString{"file: /foo.txt"};
QTemporaryDir otherTempDir;
otherTempDir.setAutoRemove(false);
const auto testFile1Path = otherTempDir.filePath("test-1");
createTestFile(testFile1Path);
const auto testFile2Path = otherTempDir.filePath("test-2");
createTestFile(testFile2Path);
QTest::newRow("absolute-url-not-in-dir") << QStringList{"file://" + testFile1Path} << QString{"file://" + testFile1Path};
QTest::newRow("absolute-urls-not-in-dir") << QStringList{"file://" + testFile1Path, "file://" + testFile2Path}
<< QString{"\"file://" + testFile1Path + "\" \"file://" + testFile2Path + "\""};
auto expectedtestFile1Path = testFile1Path;
expectedtestFile1Path = expectedtestFile1Path.remove(0, 1);
auto expectedtestFile2Path = testFile2Path;
expectedtestFile2Path = expectedtestFile2Path.remove(0, 1);
QTest::newRow("absolute-url-not-in-dir-no-scheme") << QStringList{testFile1Path} << QString{testFile1Path};
QTest::newRow("absolute-urls-not-in-dir-no-scheme")
<< QStringList{testFile1Path, testFile2Path} << QString{"\"" + testFile1Path + "\" \"" + testFile2Path + "\""};
QTest::newRow("absolute-urls-not-in-dir-scheme-mixed")
<< QStringList{testFile1Path, "file://" + testFile2Path} << QString{"\"" + testFile1Path + "\" \"file://" + testFile2Path + "\""};
}
void KFileWidgetTest::testTokenize()
{
// We will use setSelectedUrls([QUrl]) here in order to check correct
// filename escaping. Afterwards we will accept() the dialog to confirm
// correct result
QFETCH(QStringList, fileNames);
QFETCH(QString, expectedCurrentText);
QTemporaryDir tempDir;
const QString tempDirPath = tempDir.path();
const QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath);
QList<QUrl> fileUrls;
for (const auto &fileName : fileNames) {
auto localUrl = QUrl(fileName);
if (!localUrl.path().startsWith(QLatin1Char('/'))) {
const QString filePath = tempDirPath + QLatin1Char('/') + fileName;
localUrl = QUrl::fromLocalFile(filePath);
}
fileUrls.append(localUrl);
qCDebug(KIO_KFILEWIDGETS_FW) << fileName << " => " << localUrl;
}
KFileWidget fw(tempDirUrl);
fw.setOperationMode(KFileWidget::Opening);
fw.setMode(KFile::Files);
fw.setSelectedUrls(fileUrls);
// Verify the expected populated name.
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), tempDirUrl);
QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
// QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
// which calls KFileWidget::selectedUrls().
// Accept the filename to ensure that a filename is selected.
connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
const QList<QUrl> urls = fw.selectedUrls();
// We must have the same size as requested files
QCOMPARE(urls.size(), fileNames.size());
for (auto &localUrl : fileUrls) {
if (localUrl.scheme().isEmpty()) {
localUrl.setScheme("file");
}
}
QCOMPARE(urls, fileUrls);
}
void KFileWidgetTest::testTokenizeForSave_data()
{
// Real filename (as in how they are stored in the fs)
QTest::addColumn<QString>("fileName");
// Escaped value of the text-box in the dialog
// Escaped cwd: Because setSelectedUrl is called in this
// test, it actually sets the CWD to the dirname and only
// keeps the filename displayed in the test
QTest::addColumn<QString>("expectedSubFolder");
QTest::addColumn<QString>("expectedCurrentText");
QTest::newRow("save-simple") << QString{"test2"} << QString() << QString("test2");
// When a single file with space is selected, it is _not_ quoted ...
QTest::newRow("save-space") << QString{"test space"} << QString() << QString("test space");
// All quotes in names should be escaped, however since this is a single
// file, the whole name will not be escaped.
QTest::newRow("save-quote") << QString{"test\"quote"} << QString() << QString("test\\\"quote");
// Ok, enough with quotes... lets do some backslashes
// Backslash literals in file names - Unix only case
QTest::newRow("save-backslash") << QString{"test\\backslash"} << QString() << QString("test\\\\backslash");
QTest::newRow("save-double-backslash") << QString{"test\\\\back\\slash"} << QString() << QString("test\\\\\\\\back\\\\slash");
QTest::newRow("save-double-backslash-end") << QString{"test\\\\"} << QString() << QString("test\\\\\\\\");
QTest::newRow("save-single-backslash-end") << QString{"test\\"} << QString() << QString("test\\\\");
QTest::newRow("save-sharp") << QString{"some#thing"} << QString() << QString("some#thing");
// Filenames beginning with ':'; QDir::isAbsolutePath() always returns true
// in that case, #322837
QTest::newRow("save-file-beginning-with-colon") << QString{":test2"} << QString() << QString{":test2"};
// # 473228
QTest::newRow("save-save-file-beginning-with-something-that-looks-like-a-url-scheme")
<< QString{"Hello: foo.txt"} << QString() << QString{"Hello: foo.txt"};
QTemporaryDir otherTempDir;
otherTempDir.setAutoRemove(false);
const auto testFile1Path = otherTempDir.filePath("test-1");
createTestFile(testFile1Path);
QTest::newRow("save-absolute-url-not-in-dir") << QString{"file://" + testFile1Path} << otherTempDir.path() << QString{"test-1"};
auto expectedtestFile1Path = testFile1Path;
expectedtestFile1Path = expectedtestFile1Path.remove(0, 1);
QTest::newRow("save-absolute-url-not-in-dir-no-scheme") << QString{testFile1Path} << otherTempDir.path() << QString{"test-1"};
}
void KFileWidgetTest::testTokenizeForSave()
{
// We will use setSelectedUrls([QUrl]) here in order to check correct
// filename escaping. Afterwards we will accept() the dialog to confirm
// correct result
// This test is similar to testTokenize but focuses on single-file
// "save" operation. This follows a different code-path internally
// and calls setSelectedUrl instead of setSelectedUrls.
QFETCH(QString, fileName);
QFETCH(QString, expectedSubFolder);
QFETCH(QString, expectedCurrentText);
QTemporaryDir tempDir;
const QString tempDirPath = tempDir.path();
const QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath);
auto fileUrl = QUrl(fileName);
if (!fileUrl.path().startsWith(QLatin1Char('/'))) {
const QString filePath = tempDirPath + QLatin1Char('/') + fileName;
fileUrl = QUrl::fromLocalFile(filePath);
}
if (fileUrl.scheme().isEmpty()) {
fileUrl.setScheme("file");
}
qCDebug(KIO_KFILEWIDGETS_FW) << fileName << " => " << fileUrl;
KFileWidget fw(tempDirUrl);
fw.setOperationMode(KFileWidget::Saving);
fw.setMode(KFile::File);
fw.setSelectedUrl(fileUrl);
// Verify the expected populated name.
if (expectedSubFolder != "") {
const QUrl expectedBaseUrl = tempDirUrl.resolved(QUrl::fromLocalFile(expectedSubFolder));
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl);
} else {
QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), tempDirUrl);
}
QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
// QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
// which calls KFileWidget::selectedUrls().
// Accept the filename to ensure that a filename is selected.
connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
const QList<QUrl> urls = fw.selectedUrls();
// We always only have one URL here
QCOMPARE(urls.size(), 1);
QCOMPARE(urls[0], fileUrl);
}
QTEST_MAIN(KFileWidgetTest)
#include "kfilewidgettest.moc"