Warning, file /frameworks/kio/autotests/kfilewidgettest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     This file is part of the KIO framework tests
0003     SPDX-FileCopyrightText: 2016 Albert Astals Cid <aacid@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "kfilewidget.h"
0009 
0010 #include <QLabel>
0011 #include <QLoggingCategory>
0012 #include <QSignalSpy>
0013 #include <QStandardPaths>
0014 #include <QTemporaryDir>
0015 #include <QTest>
0016 #include <QUrl>
0017 
0018 #include "../utils_p.h"
0019 #include "kiotesthelper.h" // createTestFile
0020 #include <KDirLister>
0021 #include <KFileFilterCombo>
0022 #include <KUrlComboBox>
0023 #include <kdiroperator.h>
0024 #include <kurlnavigator.h>
0025 
0026 #include <KLocalizedString>
0027 
0028 #include <QAbstractItemView>
0029 #include <QDialog>
0030 #include <QDropEvent>
0031 #include <QLineEdit>
0032 #include <QList>
0033 #include <QMimeData>
0034 #include <QStringList>
0035 #include <QStringLiteral>
0036 #include <QUrl>
0037 
0038 Q_DECLARE_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW)
0039 Q_LOGGING_CATEGORY(KIO_KFILEWIDGETS_FW, "kf.kio.kfilewidgets.kfilewidget", QtInfoMsg)
0040 
0041 static QWidget *findLocationLabel(QWidget *parent)
0042 {
0043     const QList<QLabel *> labels = parent->findChildren<QLabel *>();
0044     for (QLabel *label : labels) {
0045         if (label->text() == i18n("&Name:") || label->text() == i18n("Name:")) {
0046             return label->buddy();
0047         }
0048     }
0049     Q_ASSERT(false);
0050     return nullptr;
0051 }
0052 
0053 /**
0054  * Unit test for KFileWidget
0055  */
0056 class KFileWidgetTest : public QObject
0057 {
0058     Q_OBJECT
0059 
0060 private Q_SLOTS:
0061     void initTestCase();
0062     void testFilterCombo();
0063     void testFocusOnLocationEdit();
0064     void testFocusOnLocationEditChangeDir();
0065     void testFocusOnLocationEditChangeDir2();
0066     void testFocusOnDirOps();
0067     void testGetStartUrl();
0068     void testSetSelection_data();
0069 
0070 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 33)
0071     void testSetSelection();
0072 #endif
0073 
0074     void testSetSelectedUrl_data();
0075     void testSetSelectedUrl();
0076     void testPreserveFilenameWhileNavigating();
0077     void testEnterUrl_data();
0078     void testEnterUrl();
0079     void testSetFilterForSave_data();
0080     void testSetFilterForSave();
0081     void testExtensionForSave_data();
0082     void testExtensionForSave();
0083     void testFilterChange();
0084     void testDropFile_data();
0085     void testDropFile();
0086     void testCreateNestedNewFolders();
0087     void testTokenize_data();
0088     void testTokenize();
0089 };
0090 
0091 void KFileWidgetTest::initTestCase()
0092 {
0093     QStandardPaths::setTestModeEnabled(true);
0094 
0095     QVERIFY(QDir::homePath() != QDir::tempPath());
0096 }
0097 
0098 void KFileWidgetTest::testFilterCombo()
0099 {
0100     KFileWidget fw(QUrl(QStringLiteral("kfiledialog:///SaveDialog")), nullptr);
0101     fw.setOperationMode(KFileWidget::Saving);
0102     fw.setMode(KFile::File);
0103 
0104     fw.setFilter(
0105         QStringLiteral("*.xml *.a|Word 2003 XML (.xml)\n"
0106                        "*.odt|ODF Text Document (.odt)\n"
0107                        "*.xml *.b|DocBook (.xml)\n"
0108                        "*|Raw (*)"));
0109 
0110     // default filter is selected
0111     QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.a"));
0112 
0113     // setUrl runs with blocked signals, so use setUrls.
0114     // auto-select ODT filter via filename
0115     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
0116     QCOMPARE(fw.currentFilter(), QStringLiteral("*.odt"));
0117     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
0118 
0119     // select 2nd duplicate XML filter (see bug 407642)
0120     fw.filterWidget()->setCurrentFilter("*.xml *.b|DocBook (.xml)");
0121     QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b"));
0122     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
0123 
0124     // keep filter after file change with same extension
0125     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test2.xml")));
0126     QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b"));
0127     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test2.xml"));
0128 
0129     // back to the non-xml / ODT filter
0130     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
0131     QCOMPARE(fw.currentFilter(), QStringLiteral("*.odt"));
0132     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
0133 
0134     // auto-select 1st XML filter
0135     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.xml")));
0136     QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.a"));
0137     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
0138 
0139     // select Raw '*' filter
0140     fw.filterWidget()->setCurrentFilter("*|Raw (*)");
0141     QCOMPARE(fw.currentFilter(), QStringLiteral("*"));
0142     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
0143 
0144     // keep Raw '*' filter with matching file extension
0145     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt")));
0146     QCOMPARE(fw.currentFilter(), QStringLiteral("*"));
0147     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt"));
0148 
0149     // keep Raw '*' filter with non-matching file extension
0150     fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.core")));
0151     QCOMPARE(fw.currentFilter(), QStringLiteral("*"));
0152     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.core"));
0153 
0154     // select 2nd XML filter
0155     fw.filterWidget()->setCurrentFilter("*.xml *.b|DocBook (.xml)");
0156     QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b"));
0157     QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml"));
0158 }
0159 
0160 void KFileWidgetTest::testFocusOnLocationEdit()
0161 {
0162     KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
0163     fw.show();
0164     fw.activateWindow();
0165     QVERIFY(QTest::qWaitForWindowActive(&fw));
0166 
0167     QWidget *label = findLocationLabel(&fw);
0168     QVERIFY(label);
0169     QVERIFY(label->hasFocus());
0170 }
0171 
0172 void KFileWidgetTest::testFocusOnLocationEditChangeDir()
0173 {
0174     KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
0175     fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
0176     fw.show();
0177     fw.activateWindow();
0178     QVERIFY(QTest::qWaitForWindowActive(&fw));
0179 
0180     QWidget *label = findLocationLabel(&fw);
0181     QVERIFY(label);
0182     QVERIFY(label->hasFocus());
0183 }
0184 
0185 void KFileWidgetTest::testFocusOnLocationEditChangeDir2()
0186 {
0187     KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
0188     fw.show();
0189     fw.activateWindow();
0190     QVERIFY(QTest::qWaitForWindowActive(&fw));
0191 
0192     fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
0193 
0194     QWidget *label = findLocationLabel(&fw);
0195     QVERIFY(label);
0196     QVERIFY(label->hasFocus());
0197 }
0198 
0199 void KFileWidgetTest::testFocusOnDirOps()
0200 {
0201     KFileWidget fw(QUrl::fromLocalFile(QDir::homePath()));
0202     fw.show();
0203     fw.activateWindow();
0204     QVERIFY(QTest::qWaitForWindowActive(&fw));
0205 
0206     const QList<KUrlNavigator *> nav = fw.findChildren<KUrlNavigator *>();
0207     QCOMPARE(nav.count(), 1);
0208     nav[0]->setFocus();
0209 
0210     fw.setUrl(QUrl::fromLocalFile(QDir::tempPath()));
0211 
0212     const QList<KDirOperator *> ops = fw.findChildren<KDirOperator *>();
0213     QCOMPARE(ops.count(), 1);
0214     QVERIFY(ops[0]->hasFocus());
0215 }
0216 
0217 void KFileWidgetTest::testGetStartUrl()
0218 {
0219     QString recentDirClass;
0220     QString outFileName;
0221     QUrl localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachmentDir")), recentDirClass, outFileName);
0222     QCOMPARE(recentDirClass, QStringLiteral(":attachmentDir"));
0223     QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
0224     QVERIFY(outFileName.isEmpty());
0225 
0226 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 96)
0227     localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachments/foo.txt?global")), recentDirClass, outFileName);
0228     QCOMPARE(recentDirClass, QStringLiteral("::attachments"));
0229 #else
0230     localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachments/foo.txt")), recentDirClass, outFileName);
0231     QCOMPARE(recentDirClass, QStringLiteral(":attachments"));
0232 #endif
0233     QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
0234     QCOMPARE(outFileName, QStringLiteral("foo.txt"));
0235 }
0236 
0237 void KFileWidgetTest::testSetSelection_data()
0238 {
0239     QTest::addColumn<QString>("baseDir");
0240     QTest::addColumn<QString>("selection");
0241     QTest::addColumn<QString>("expectedBaseDir");
0242     QTest::addColumn<QString>("expectedCurrentText");
0243 
0244     const QString baseDir = QDir::homePath();
0245     // A nice filename to detect URL encoding issues
0246     const QString fileName = QStringLiteral("some:fi#le");
0247 
0248     // Bug 369216, kdialog calls setSelection(path)
0249     QTest::newRow("path") << baseDir << baseDir + QLatin1Char('/') + fileName << baseDir << fileName;
0250     QTest::newRow("differentPath") << QDir::rootPath() << baseDir + QLatin1Char('/') + fileName << baseDir << fileName;
0251     // kdeplatformfiledialoghelper.cpp calls setSelection(URL as string)
0252     QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName).toString() << baseDir << fileName;
0253     // What if someone calls setSelection(fileName)? That breaks, hence e70f8134a2b in plasma-integration.git
0254     QTest::newRow("filename") << baseDir << fileName << baseDir << fileName;
0255 }
0256 
0257 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 33)
0258 void KFileWidgetTest::testSetSelection()
0259 {
0260     // GIVEN
0261     QFETCH(QString, baseDir);
0262     QFETCH(QString, selection);
0263     QFETCH(QString, expectedBaseDir);
0264     QFETCH(QString, expectedCurrentText);
0265     const QUrl baseUrl = QUrl::fromLocalFile(baseDir).adjusted(QUrl::StripTrailingSlash);
0266     const QUrl expectedBaseUrl = QUrl::fromLocalFile(expectedBaseDir);
0267 
0268     KFileWidget fw(baseUrl);
0269 
0270     // WHEN
0271     fw.setSelection(selection); // now deprecated, this test shows why ;)
0272 
0273     // THEN
0274     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl);
0275     // if (QByteArray(QTest::currentDataTag()) == "filename") {
0276     QEXPECT_FAIL("filename", "setSelection cannot work with filenames, bad API", Continue);
0277     //}
0278     QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
0279 }
0280 #endif
0281 
0282 void KFileWidgetTest::testSetSelectedUrl_data()
0283 {
0284     QTest::addColumn<QString>("baseDir");
0285     QTest::addColumn<QUrl>("selectionUrl");
0286     QTest::addColumn<QString>("expectedBaseDir");
0287     QTest::addColumn<QString>("expectedCurrentText");
0288 
0289     const QString baseDir = QDir::homePath();
0290     // A nice filename to detect URL encoding issues
0291     const QString fileName = QStringLiteral("some:fi#le");
0292     const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName);
0293 
0294     QTest::newRow("path") << baseDir << fileUrl << baseDir << fileName;
0295     QTest::newRow("differentPath") << QDir::rootPath() << fileUrl << baseDir << fileName;
0296     QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName) << baseDir << fileName;
0297 
0298     QUrl relativeUrl;
0299     relativeUrl.setPath(fileName);
0300     QTest::newRow("filename") << baseDir << relativeUrl << baseDir << fileName;
0301 }
0302 
0303 void KFileWidgetTest::testSetSelectedUrl()
0304 {
0305     // GIVEN
0306     QFETCH(QString, baseDir);
0307     QFETCH(QUrl, selectionUrl);
0308     QFETCH(QString, expectedBaseDir);
0309     QFETCH(QString, expectedCurrentText);
0310 
0311     const QUrl baseUrl = QUrl::fromLocalFile(baseDir).adjusted(QUrl::StripTrailingSlash);
0312     const QUrl expectedBaseUrl = QUrl::fromLocalFile(expectedBaseDir);
0313     KFileWidget fw(baseUrl);
0314 
0315     // WHEN
0316     fw.setSelectedUrl(selectionUrl);
0317 
0318     // THEN
0319     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl);
0320     QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
0321 }
0322 
0323 void KFileWidgetTest::testPreserveFilenameWhileNavigating() // bug 418711
0324 {
0325     // GIVEN
0326     const QUrl url = QUrl::fromLocalFile(QDir::homePath());
0327     KFileWidget fw(url);
0328     fw.setOperationMode(KFileWidget::Saving);
0329     fw.setMode(KFile::File);
0330     QString baseDir = QDir::homePath();
0331     if (baseDir.endsWith('/')) {
0332         baseDir.chop(1);
0333     }
0334     const QString fileName = QStringLiteral("somefi#le");
0335     const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName);
0336     fw.setSelectedUrl(fileUrl);
0337     const QUrl baseUrl = QUrl::fromLocalFile(baseDir);
0338     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl);
0339     QCOMPARE(fw.locationEdit()->currentText(), fileName);
0340 
0341     // WHEN
0342     fw.dirOperator()->cdUp();
0343 
0344     // THEN
0345     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
0346     QCOMPARE(fw.locationEdit()->currentText(), fileName); // unchanged
0347 }
0348 
0349 void KFileWidgetTest::testEnterUrl_data()
0350 {
0351     QTest::addColumn<QUrl>("expectedUrl");
0352 
0353     // Check if the root urls are well transformed into themself, otherwise
0354     // when going up from file:///home/ it will become file:///home/user
0355     QTest::newRow("file") << QUrl::fromLocalFile("/");
0356     QTest::newRow("trash") << QUrl("trash:/");
0357     QTest::newRow("sftp") << QUrl("sftp://127.0.0.1/");
0358 }
0359 
0360 void KFileWidgetTest::testEnterUrl()
0361 {
0362     // GIVEN
0363     QFETCH(QUrl, expectedUrl);
0364 
0365     // WHEN
0366     // These lines are copied from src/filewidgets/kfilewidget.cpp
0367     // void KFileWidgetPrivate::_k_enterUrl(const QUrl &url)
0368     QUrl u(expectedUrl);
0369     Utils::appendSlashToPath(u);
0370     // THEN
0371     QVERIFY(u.isValid());
0372     QCOMPARE(u, expectedUrl);
0373 }
0374 
0375 void KFileWidgetTest::testSetFilterForSave_data()
0376 {
0377     QTest::addColumn<QString>("fileName");
0378     QTest::addColumn<QString>("filter");
0379     QTest::addColumn<QString>("expectedCurrentText");
0380     QTest::addColumn<QString>("expectedSelectedFileName");
0381 
0382     const QString filter = QStringLiteral("*.txt|Text files\n*.HTML|HTML files");
0383 
0384     QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
0385 
0386     // If an application provides a name without extension, then the
0387     // displayed name will not receive an extension. It will however be
0388     // appended when the dialog is closed.
0389     QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt");
0390 
0391     // If the file literally exists, then no new extension will be appended.
0392     QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README");
0393 
0394     // XXX perhaps the "extension" should not be modified when it does not
0395     // match any of the existing types? Should "some.2019.txt" be expected?
0396     QTest::newRow("some.2019") << "some.2019" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
0397 
0398     // XXX be smarter and do not change the extension if one of the other
0399     // filters match. Should "some.html" be expected?
0400     QTest::newRow("some.html") << "some.html" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
0401 }
0402 
0403 void KFileWidgetTest::testSetFilterForSave()
0404 {
0405     QFETCH(QString, fileName);
0406     QFETCH(QString, filter);
0407     QFETCH(QString, expectedCurrentText);
0408     QFETCH(QString, expectedSelectedFileName);
0409 
0410     // Use a temporary directory since the presence of existing files
0411     // influences whether an extension is automatically appended.
0412     QTemporaryDir tempDir;
0413     const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
0414     const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
0415     const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName));
0416     createTestFile(tempDir.filePath("README"));
0417 
0418     KFileWidget fw(dirUrl);
0419     fw.setOperationMode(KFileWidget::Saving);
0420     fw.setSelectedUrl(fileUrl);
0421     // Calling setFilter has side-effects and changes the file name.
0422     fw.setFilter(filter);
0423 
0424     // Verify the expected populated name.
0425     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
0426     QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
0427 
0428     // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
0429     // which calls KFileWidget::selectedUrls().
0430     // Accept the filename to ensure that a filename is selected.
0431     connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
0432     QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
0433     QList<QUrl> urls = fw.selectedUrls();
0434     QCOMPARE(urls.size(), 1);
0435     QCOMPARE(urls[0], expectedFileUrl);
0436 }
0437 
0438 void KFileWidgetTest::testExtensionForSave_data()
0439 {
0440     QTest::addColumn<QString>("fileName");
0441     QTest::addColumn<QString>("filter");
0442     QTest::addColumn<QString>("expectedCurrentText");
0443     QTest::addColumn<QString>("expectedSelectedFileName");
0444 
0445     const QString filter = QStringLiteral("*.txt *.text|Text files\n*.HTML|HTML files");
0446 
0447     QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt");
0448 
0449     // If an application provides a name without extension, then the
0450     // displayed name will not receive an extension. It will however be
0451     // appended when the dialog is closed.
0452     QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt");
0453     QTest::newRow("extensionless name") << "some.with_dot" << filter << QStringLiteral("some.with_dot") << QStringLiteral("some.with_dot.txt");
0454     QTest::newRow("extensionless name") << "some.with.dots" << filter << QStringLiteral("some.with.dots") << QStringLiteral("some.with.dots.txt");
0455 
0456     // If the file literally exists, then no new extension will be appended.
0457     QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README");
0458 }
0459 
0460 void KFileWidgetTest::testExtensionForSave()
0461 {
0462     QFETCH(QString, fileName);
0463     QFETCH(QString, filter);
0464     QFETCH(QString, expectedCurrentText);
0465     QFETCH(QString, expectedSelectedFileName);
0466 
0467     // Use a temporary directory since the presence of existing files
0468     // influences whether an extension is automatically appended.
0469     QTemporaryDir tempDir;
0470     const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
0471     const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
0472     const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName));
0473     createTestFile(tempDir.filePath("README"));
0474 
0475     KFileWidget fw(dirUrl);
0476     fw.setOperationMode(KFileWidget::Saving);
0477     // Calling setFilter has side-effects and changes the file name.
0478     // The difference to testSetFilterForSave is that the filter is already set before the fileUrl
0479     // is set, and will not be changed after.
0480     fw.setFilter(filter);
0481     fw.setSelectedUrl(fileUrl);
0482 
0483     // Verify the expected populated name.
0484     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
0485     QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
0486 
0487     // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
0488     // which calls KFileWidget::selectedUrls().
0489     // Accept the filename to ensure that a filename is selected.
0490     connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
0491     QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
0492     QList<QUrl> urls = fw.selectedUrls();
0493     QCOMPARE(urls.size(), 1);
0494     QCOMPARE(urls[0], expectedFileUrl);
0495 }
0496 
0497 void KFileWidgetTest::testFilterChange()
0498 {
0499     QTemporaryDir tempDir;
0500     createTestFile(tempDir.filePath("some.c"));
0501     bool created = QDir(tempDir.path()).mkdir("directory");
0502     Q_ASSERT(created);
0503 
0504     KFileWidget fw(QUrl::fromLocalFile(tempDir.path()));
0505     fw.setOperationMode(KFileWidget::Saving);
0506     fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("some.txt")));
0507     fw.setFilter("*.txt|Txt\n*.c|C");
0508 
0509     // Initial filename.
0510     QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.txt"));
0511     QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt"));
0512 
0513     // Select type with an existing file.
0514     fw.filterWidget()->setCurrentFilter("*.c|C");
0515     QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.c"));
0516     QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.c"));
0517 
0518     // Do not update extension if the current selection is a directory.
0519     fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("directory")));
0520     fw.filterWidget()->setCurrentFilter("*.txt|Txt");
0521     QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("directory"));
0522     QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt"));
0523 }
0524 
0525 void KFileWidgetTest::testDropFile_data()
0526 {
0527     QTest::addColumn<QString>("dir");
0528     QTest::addColumn<QString>("fileName");
0529     QTest::addColumn<QString>("expectedCurrentText");
0530 
0531     QTest::newRow("some.txt") << ""
0532                               << "some.txt"
0533                               << "some.txt";
0534 
0535     QTest::newRow("subdir/some.txt") << "subdir"
0536                                      << "subdir/some.txt"
0537                                      << "some.txt";
0538 }
0539 
0540 void KFileWidgetTest::testDropFile()
0541 {
0542     QFETCH(QString, dir);
0543     QFETCH(QString, fileName);
0544     QFETCH(QString, expectedCurrentText);
0545 
0546     // Use a temporary directory since the presence of existing files
0547     // influences whether an extension is automatically appended.
0548     QTemporaryDir tempDir;
0549     QUrl dirUrl = QUrl::fromLocalFile(tempDir.path());
0550     const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName));
0551     if (!dir.isEmpty()) {
0552         createTestDirectory(tempDir.filePath(dir));
0553         dirUrl = QUrl::fromLocalFile(tempDir.filePath(dir));
0554     }
0555     createTestFile(tempDir.filePath(fileName));
0556 
0557     KFileWidget fileWidget(QUrl::fromLocalFile(tempDir.path()));
0558     fileWidget.setOperationMode(KFileWidget::Saving);
0559     fileWidget.setMode(KFile::File);
0560     fileWidget.show();
0561 
0562     QMimeData *mimeData = new QMimeData();
0563     mimeData->setUrls(QList<QUrl>() << fileUrl);
0564 
0565     KDirLister *dirLister = fileWidget.dirOperator()->dirLister();
0566     QSignalSpy spy(dirLister, qOverload<>(&KCoreDirLister::completed));
0567 
0568     QAbstractItemView *view = fileWidget.dirOperator()->view();
0569     QVERIFY(view);
0570 
0571     QDragEnterEvent event1(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
0572     QVERIFY(qApp->sendEvent(view->viewport(), &event1));
0573 
0574     // Fake drop
0575     QDropEvent event(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
0576     QVERIFY(qApp->sendEvent(view->viewport(), &event));
0577 
0578     if (!dir.isEmpty()) {
0579         // once we drop a file the dirlister scans the dir
0580         // wait for the completed signal from the dirlister
0581         QVERIFY(spy.wait());
0582     }
0583 
0584     // Verify the expected populated name.
0585     QCOMPARE(fileWidget.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl);
0586     QCOMPARE(fileWidget.locationEdit()->currentText(), expectedCurrentText);
0587 
0588     // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
0589     // which calls KFileWidget::selectedUrls().
0590     // Accept the filename to ensure that a filename is selected.
0591     connect(&fileWidget, &KFileWidget::accepted, &fileWidget, &KFileWidget::accept);
0592     QTest::keyClick(fileWidget.locationEdit(), Qt::Key_Return);
0593     const QList<QUrl> urls = fileWidget.selectedUrls();
0594     QCOMPARE(urls.size(), 1);
0595     QCOMPARE(urls[0], fileUrl);
0596 }
0597 
0598 void KFileWidgetTest::testCreateNestedNewFolders()
0599 {
0600     // when creating multiple nested new folders in the "save as" dialog, where folders are
0601     // created and entered, kdirlister would hit an assert (in reinsert()), bug 408801
0602     QTemporaryDir tempDir;
0603     QVERIFY(tempDir.isValid());
0604     const QString dir = tempDir.path();
0605     const QUrl url = QUrl::fromLocalFile(dir);
0606     KFileWidget fw(url);
0607     fw.setOperationMode(KFileWidget::Saving);
0608     fw.setMode(KFile::File);
0609 
0610     QString currentPath = dir;
0611     // create the nested folders
0612     for (int i = 1; i < 6; ++i) {
0613         fw.dirOperator()->mkdir();
0614         QDialog *dialog;
0615         // QTRY_ because a NameFinderJob could be running and the dialog will be shown when
0616         // it finishes.
0617         QTRY_VERIFY(dialog = fw.findChild<QDialog *>());
0618         QLineEdit *lineEdit = dialog->findChild<QLineEdit *>();
0619         QVERIFY(lineEdit);
0620         const QString name = QStringLiteral("folder%1").arg(i);
0621         lineEdit->setText(name);
0622         // simulate the time the user will take to type the new folder name
0623         QTest::qWait(1000);
0624 
0625         dialog->accept();
0626 
0627         currentPath += QLatin1Char('/') + name;
0628         // Wait till the filewidget changes to the new folder
0629         QTRY_COMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(), currentPath);
0630     }
0631 }
0632 
0633 void KFileWidgetTest::testTokenize_data()
0634 {
0635     // Real filename (as in how they are stored in the fs)
0636     QTest::addColumn<QStringList>("fileNames");
0637     // Escaped value of the text-box in the dialog
0638     QTest::addColumn<QString>("expectedCurrentText");
0639 
0640     QTest::newRow("simple") << QStringList{"test2"} << QString("test2");
0641 
0642     // When a single file with space is selected, it is _not_ quoted ...
0643     QTest::newRow("space-single-file") << QStringList{"test space"} << QString("test space");
0644 
0645     // However, when multiple files are selected, they are quoted
0646     QTest::newRow("space-multi-file") << QStringList{"test space", "test2"} << QString("\"test space\" \"test2\"");
0647 
0648     // All quotes in names should be escaped, however since this is a single
0649     // file, the whole name will not be escaped.
0650     QTest::newRow("quote-single-file") << QStringList{"test\"quote"} << QString("test\\\"quote");
0651 
0652     // Escape multiple files. Files should also be wrapped in ""
0653     // Note that we are also testing quote at the end of the name
0654     QTest::newRow("quote-multi-file") << QStringList{"test\"quote", "test2-quote\"", "test"} << QString("\"test\\\"quote\" \"test2-quote\\\"\" \"test\"");
0655 
0656     // Ok, enough with quotes... lets do some backslashes
0657     // Backslash literals in file names - Unix only case
0658     QTest::newRow("backslash-single-file") << QStringList{"test\\backslash"} << QString("test\\\\backslash");
0659 
0660     QTest::newRow("backslash-multi-file") << QStringList{"test\\back\\slash", "test"} << QString("\"test\\\\back\\\\slash\" \"test\"");
0661 
0662     QTest::newRow("double-backslash-multi-file") << QStringList{"test\\\\back\\slash", "test"} << QString("\"test\\\\\\\\back\\\\slash\" \"test\"");
0663 
0664     QTest::newRow("double-backslash-end") << QStringList{"test\\\\"} << QString("test\\\\\\\\");
0665 
0666     QTest::newRow("single-backslash-end") << QStringList{"some thing", "test\\"} << QString("\"some thing\" \"test\\\\\"");
0667 
0668     QTest::newRow("sharp") << QStringList{"some#thing"} << QString("some#thing");
0669 
0670     // Filenames beginning with ':'; QDir::isAbsolutePath() always returns true
0671     // in that case, #322837
0672     QTest::newRow("file-beginning-with-colon") << QStringList{":test2"} << QString{":test2"};
0673 
0674     QTest::newRow("multiple-files-beginning-with-colon") << QStringList{":test space", ":test2"} << QString{"\":test space\" \":test2\""};
0675 }
0676 
0677 void KFileWidgetTest::testTokenize()
0678 {
0679     // We will use setSelectedUrls([QUrl]) here in order to check correct
0680     // filename escaping. Afterwards we will accept() the dialog to confirm
0681     // correct result
0682     QFETCH(QStringList, fileNames);
0683     QFETCH(QString, expectedCurrentText);
0684 
0685     QTemporaryDir tempDir;
0686     const QString tempDirPath = tempDir.path();
0687     const QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath);
0688     QList<QUrl> fileUrls;
0689     for (const auto &fileName : fileNames) {
0690         const QString filePath = tempDirPath + QLatin1Char('/') + fileName;
0691         const QUrl localUrl = QUrl::fromLocalFile(filePath);
0692         fileUrls.append(localUrl);
0693         qCDebug(KIO_KFILEWIDGETS_FW) << fileName << " => " << localUrl;
0694     }
0695 
0696     KFileWidget fw(tempDirUrl);
0697     fw.setOperationMode(KFileWidget::Opening);
0698     fw.setMode(KFile::Files);
0699     fw.setSelectedUrls(fileUrls);
0700 
0701     // Verify the expected populated name.
0702     QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), tempDirUrl);
0703     QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText);
0704 
0705     // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles()
0706     // which calls KFileWidget::selectedUrls().
0707     // Accept the filename to ensure that a filename is selected.
0708     connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept);
0709     QTest::keyClick(fw.locationEdit(), Qt::Key_Return);
0710     const QList<QUrl> urls = fw.selectedUrls();
0711 
0712     // We must have the same size as requested files
0713     QCOMPARE(urls.size(), fileNames.size());
0714     QCOMPARE(urls, fileUrls);
0715 }
0716 
0717 QTEST_MAIN(KFileWidgetTest)
0718 
0719 #include "kfilewidgettest.moc"