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 void testTokenizeForSave_data(); 0090 void testTokenizeForSave(); 0091 }; 0092 0093 void KFileWidgetTest::initTestCase() 0094 { 0095 QStandardPaths::setTestModeEnabled(true); 0096 0097 QVERIFY(QDir::homePath() != QDir::tempPath()); 0098 } 0099 0100 void KFileWidgetTest::testFilterCombo() 0101 { 0102 KFileWidget fw(QUrl(QStringLiteral("kfiledialog:///SaveDialog")), nullptr); 0103 fw.setOperationMode(KFileWidget::Saving); 0104 fw.setMode(KFile::File); 0105 0106 fw.setFilter( 0107 QStringLiteral("*.xml *.a|Word 2003 XML (.xml)\n" 0108 "*.odt|ODF Text Document (.odt)\n" 0109 "*.xml *.b|DocBook (.xml)\n" 0110 "*|Raw (*)")); 0111 0112 // default filter is selected 0113 QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.a")); 0114 0115 // setUrl runs with blocked signals, so use setUrls. 0116 // auto-select ODT filter via filename 0117 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt"))); 0118 QCOMPARE(fw.currentFilter(), QStringLiteral("*.odt")); 0119 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt")); 0120 0121 // select 2nd duplicate XML filter (see bug 407642) 0122 fw.filterWidget()->setCurrentFilter("*.xml *.b|DocBook (.xml)"); 0123 QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b")); 0124 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml")); 0125 0126 // keep filter after file change with same extension 0127 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test2.xml"))); 0128 QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b")); 0129 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test2.xml")); 0130 0131 // back to the non-xml / ODT filter 0132 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt"))); 0133 QCOMPARE(fw.currentFilter(), QStringLiteral("*.odt")); 0134 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt")); 0135 0136 // auto-select 1st XML filter 0137 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.xml"))); 0138 QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.a")); 0139 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml")); 0140 0141 // select Raw '*' filter 0142 fw.filterWidget()->setCurrentFilter("*|Raw (*)"); 0143 QCOMPARE(fw.currentFilter(), QStringLiteral("*")); 0144 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml")); 0145 0146 // keep Raw '*' filter with matching file extension 0147 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.odt"))); 0148 QCOMPARE(fw.currentFilter(), QStringLiteral("*")); 0149 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.odt")); 0150 0151 // keep Raw '*' filter with non-matching file extension 0152 fw.locationEdit()->setUrls(QStringList(QStringLiteral("test.core"))); 0153 QCOMPARE(fw.currentFilter(), QStringLiteral("*")); 0154 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.core")); 0155 0156 // select 2nd XML filter 0157 fw.filterWidget()->setCurrentFilter("*.xml *.b|DocBook (.xml)"); 0158 QCOMPARE(fw.currentFilter(), QStringLiteral("*.xml *.b")); 0159 QCOMPARE(fw.locationEdit()->urls()[0], QStringLiteral("test.xml")); 0160 } 0161 0162 void KFileWidgetTest::testFocusOnLocationEdit() 0163 { 0164 KFileWidget fw(QUrl::fromLocalFile(QDir::homePath())); 0165 fw.show(); 0166 fw.activateWindow(); 0167 QVERIFY(QTest::qWaitForWindowActive(&fw)); 0168 0169 QWidget *label = findLocationLabel(&fw); 0170 QVERIFY(label); 0171 QVERIFY(label->hasFocus()); 0172 } 0173 0174 void KFileWidgetTest::testFocusOnLocationEditChangeDir() 0175 { 0176 KFileWidget fw(QUrl::fromLocalFile(QDir::homePath())); 0177 fw.setUrl(QUrl::fromLocalFile(QDir::tempPath())); 0178 fw.show(); 0179 fw.activateWindow(); 0180 QVERIFY(QTest::qWaitForWindowActive(&fw)); 0181 0182 QWidget *label = findLocationLabel(&fw); 0183 QVERIFY(label); 0184 QVERIFY(label->hasFocus()); 0185 } 0186 0187 void KFileWidgetTest::testFocusOnLocationEditChangeDir2() 0188 { 0189 KFileWidget fw(QUrl::fromLocalFile(QDir::homePath())); 0190 fw.show(); 0191 fw.activateWindow(); 0192 QVERIFY(QTest::qWaitForWindowActive(&fw)); 0193 0194 fw.setUrl(QUrl::fromLocalFile(QDir::tempPath())); 0195 0196 QWidget *label = findLocationLabel(&fw); 0197 QVERIFY(label); 0198 QVERIFY(label->hasFocus()); 0199 } 0200 0201 void KFileWidgetTest::testFocusOnDirOps() 0202 { 0203 KFileWidget fw(QUrl::fromLocalFile(QDir::homePath())); 0204 fw.show(); 0205 fw.activateWindow(); 0206 QVERIFY(QTest::qWaitForWindowActive(&fw)); 0207 0208 const QList<KUrlNavigator *> nav = fw.findChildren<KUrlNavigator *>(); 0209 QCOMPARE(nav.count(), 1); 0210 nav[0]->setFocus(); 0211 0212 fw.setUrl(QUrl::fromLocalFile(QDir::tempPath())); 0213 0214 const QList<KDirOperator *> ops = fw.findChildren<KDirOperator *>(); 0215 QCOMPARE(ops.count(), 1); 0216 QVERIFY(ops[0]->hasFocus()); 0217 } 0218 0219 void KFileWidgetTest::testGetStartUrl() 0220 { 0221 QString recentDirClass; 0222 QString outFileName; 0223 QUrl localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachmentDir")), recentDirClass, outFileName); 0224 QCOMPARE(recentDirClass, QStringLiteral(":attachmentDir")); 0225 QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); 0226 QVERIFY(outFileName.isEmpty()); 0227 0228 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 96) 0229 localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachments/foo.txt?global")), recentDirClass, outFileName); 0230 QCOMPARE(recentDirClass, QStringLiteral("::attachments")); 0231 #else 0232 localUrl = KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///attachments/foo.txt")), recentDirClass, outFileName); 0233 QCOMPARE(recentDirClass, QStringLiteral(":attachments")); 0234 #endif 0235 QCOMPARE(localUrl.toLocalFile(), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); 0236 QCOMPARE(outFileName, QStringLiteral("foo.txt")); 0237 } 0238 0239 void KFileWidgetTest::testSetSelection_data() 0240 { 0241 QTest::addColumn<QString>("baseDir"); 0242 QTest::addColumn<QString>("selection"); 0243 QTest::addColumn<QString>("expectedBaseDir"); 0244 QTest::addColumn<QString>("expectedCurrentText"); 0245 0246 const QString baseDir = QDir::homePath(); 0247 // A nice filename to detect URL encoding issues 0248 const QString fileName = QStringLiteral("some:fi#le"); 0249 0250 // Bug 369216, kdialog calls setSelection(path) 0251 QTest::newRow("path") << baseDir << baseDir + QLatin1Char('/') + fileName << baseDir << fileName; 0252 QTest::newRow("differentPath") << QDir::rootPath() << baseDir + QLatin1Char('/') + fileName << baseDir << fileName; 0253 // kdeplatformfiledialoghelper.cpp calls setSelection(URL as string) 0254 QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName).toString() << baseDir << fileName; 0255 // What if someone calls setSelection(fileName)? That breaks, hence e70f8134a2b in plasma-integration.git 0256 QTest::newRow("filename") << baseDir << fileName << baseDir << fileName; 0257 } 0258 0259 #if KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(5, 33) 0260 void KFileWidgetTest::testSetSelection() 0261 { 0262 // GIVEN 0263 QFETCH(QString, baseDir); 0264 QFETCH(QString, selection); 0265 QFETCH(QString, expectedBaseDir); 0266 QFETCH(QString, expectedCurrentText); 0267 const QUrl baseUrl = QUrl::fromLocalFile(baseDir).adjusted(QUrl::StripTrailingSlash); 0268 const QUrl expectedBaseUrl = QUrl::fromLocalFile(expectedBaseDir); 0269 0270 KFileWidget fw(baseUrl); 0271 0272 // WHEN 0273 fw.setSelection(selection); // now deprecated, this test shows why ;) 0274 0275 // THEN 0276 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl); 0277 // if (QByteArray(QTest::currentDataTag()) == "filename") { 0278 QEXPECT_FAIL("filename", "setSelection cannot work with filenames, bad API", Continue); 0279 //} 0280 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0281 } 0282 #endif 0283 0284 void KFileWidgetTest::testSetSelectedUrl_data() 0285 { 0286 QTest::addColumn<QString>("baseDir"); 0287 QTest::addColumn<QUrl>("selectionUrl"); 0288 QTest::addColumn<QString>("expectedBaseDir"); 0289 QTest::addColumn<QString>("expectedCurrentText"); 0290 0291 const QString baseDir = QDir::homePath(); 0292 // A nice filename to detect URL encoding issues 0293 const QString fileName = QStringLiteral("some:fi#le"); 0294 const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName); 0295 0296 QTest::newRow("path") << baseDir << fileUrl << baseDir << fileName; 0297 QTest::newRow("differentPath") << QDir::rootPath() << fileUrl << baseDir << fileName; 0298 QTest::newRow("url") << baseDir << QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName) << baseDir << fileName; 0299 0300 QUrl relativeUrl; 0301 relativeUrl.setPath(fileName); 0302 QTest::newRow("filename") << baseDir << relativeUrl << baseDir << fileName; 0303 } 0304 0305 void KFileWidgetTest::testSetSelectedUrl() 0306 { 0307 // GIVEN 0308 QFETCH(QString, baseDir); 0309 QFETCH(QUrl, selectionUrl); 0310 QFETCH(QString, expectedBaseDir); 0311 QFETCH(QString, expectedCurrentText); 0312 0313 const QUrl baseUrl = QUrl::fromLocalFile(baseDir).adjusted(QUrl::StripTrailingSlash); 0314 const QUrl expectedBaseUrl = QUrl::fromLocalFile(expectedBaseDir); 0315 KFileWidget fw(baseUrl); 0316 0317 // WHEN 0318 fw.setSelectedUrl(selectionUrl); 0319 0320 // THEN 0321 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl); 0322 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0323 } 0324 0325 void KFileWidgetTest::testPreserveFilenameWhileNavigating() // bug 418711 0326 { 0327 // GIVEN 0328 const QUrl url = QUrl::fromLocalFile(QDir::homePath()); 0329 KFileWidget fw(url); 0330 fw.setOperationMode(KFileWidget::Saving); 0331 fw.setMode(KFile::File); 0332 QString baseDir = QDir::homePath(); 0333 if (baseDir.endsWith('/')) { 0334 baseDir.chop(1); 0335 } 0336 const QString fileName = QStringLiteral("somefi#le"); 0337 const QUrl fileUrl = QUrl::fromLocalFile(baseDir + QLatin1Char('/') + fileName); 0338 fw.setSelectedUrl(fileUrl); 0339 const QUrl baseUrl = QUrl::fromLocalFile(baseDir); 0340 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl); 0341 QCOMPARE(fw.locationEdit()->currentText(), fileName); 0342 0343 // WHEN 0344 fw.dirOperator()->cdUp(); 0345 0346 // THEN 0347 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), baseUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); 0348 QCOMPARE(fw.locationEdit()->currentText(), fileName); // unchanged 0349 } 0350 0351 void KFileWidgetTest::testEnterUrl_data() 0352 { 0353 QTest::addColumn<QUrl>("expectedUrl"); 0354 0355 // Check if the root urls are well transformed into themself, otherwise 0356 // when going up from file:///home/ it will become file:///home/user 0357 QTest::newRow("file") << QUrl::fromLocalFile("/"); 0358 QTest::newRow("trash") << QUrl("trash:/"); 0359 QTest::newRow("sftp") << QUrl("sftp://127.0.0.1/"); 0360 } 0361 0362 void KFileWidgetTest::testEnterUrl() 0363 { 0364 // GIVEN 0365 QFETCH(QUrl, expectedUrl); 0366 0367 // WHEN 0368 // These lines are copied from src/filewidgets/kfilewidget.cpp 0369 // void KFileWidgetPrivate::_k_enterUrl(const QUrl &url) 0370 QUrl u(expectedUrl); 0371 Utils::appendSlashToPath(u); 0372 // THEN 0373 QVERIFY(u.isValid()); 0374 QCOMPARE(u, expectedUrl); 0375 } 0376 0377 void KFileWidgetTest::testSetFilterForSave_data() 0378 { 0379 QTest::addColumn<QString>("fileName"); 0380 QTest::addColumn<QString>("filter"); 0381 QTest::addColumn<QString>("expectedCurrentText"); 0382 QTest::addColumn<QString>("expectedSelectedFileName"); 0383 0384 const QString filter = QStringLiteral("*.txt|Text files\n*.HTML|HTML files"); 0385 0386 QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt"); 0387 0388 // If an application provides a name without extension, then the 0389 // displayed name will not receive an extension. It will however be 0390 // appended when the dialog is closed. 0391 QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt"); 0392 0393 // If the file literally exists, then no new extension will be appended. 0394 QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README"); 0395 0396 // XXX perhaps the "extension" should not be modified when it does not 0397 // match any of the existing types? Should "some.2019.txt" be expected? 0398 QTest::newRow("some.2019") << "some.2019" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt"); 0399 0400 // XXX be smarter and do not change the extension if one of the other 0401 // filters match. Should "some.html" be expected? 0402 QTest::newRow("some.html") << "some.html" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt"); 0403 } 0404 0405 void KFileWidgetTest::testSetFilterForSave() 0406 { 0407 QFETCH(QString, fileName); 0408 QFETCH(QString, filter); 0409 QFETCH(QString, expectedCurrentText); 0410 QFETCH(QString, expectedSelectedFileName); 0411 0412 // Use a temporary directory since the presence of existing files 0413 // influences whether an extension is automatically appended. 0414 QTemporaryDir tempDir; 0415 const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path()); 0416 const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName)); 0417 const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName)); 0418 createTestFile(tempDir.filePath("README")); 0419 0420 KFileWidget fw(dirUrl); 0421 fw.setOperationMode(KFileWidget::Saving); 0422 fw.setSelectedUrl(fileUrl); 0423 // Calling setFilter has side-effects and changes the file name. 0424 fw.setFilter(filter); 0425 0426 // Verify the expected populated name. 0427 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl); 0428 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0429 0430 // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() 0431 // which calls KFileWidget::selectedUrls(). 0432 // Accept the filename to ensure that a filename is selected. 0433 connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept); 0434 QTest::keyClick(fw.locationEdit(), Qt::Key_Return); 0435 QList<QUrl> urls = fw.selectedUrls(); 0436 QCOMPARE(urls.size(), 1); 0437 QCOMPARE(urls[0], expectedFileUrl); 0438 } 0439 0440 void KFileWidgetTest::testExtensionForSave_data() 0441 { 0442 QTest::addColumn<QString>("fileName"); 0443 QTest::addColumn<QString>("filter"); 0444 QTest::addColumn<QString>("expectedCurrentText"); 0445 QTest::addColumn<QString>("expectedSelectedFileName"); 0446 0447 const QString filter = QStringLiteral("*.txt *.text|Text files\n*.HTML|HTML files"); 0448 0449 QTest::newRow("some.txt") << "some.txt" << filter << QStringLiteral("some.txt") << QStringLiteral("some.txt"); 0450 0451 // If an application provides a name without extension, then the 0452 // displayed name will not receive an extension. It will however be 0453 // appended when the dialog is closed. 0454 QTest::newRow("extensionless name") << "some" << filter << QStringLiteral("some") << QStringLiteral("some.txt"); 0455 QTest::newRow("extensionless name") << "some.with_dot" << filter << QStringLiteral("some.with_dot") << QStringLiteral("some.with_dot.txt"); 0456 QTest::newRow("extensionless name") << "some.with.dots" << filter << QStringLiteral("some.with.dots") << QStringLiteral("some.with.dots.txt"); 0457 0458 // If the file literally exists, then no new extension will be appended. 0459 QTest::newRow("existing file") << "README" << filter << QStringLiteral("README") << QStringLiteral("README"); 0460 } 0461 0462 void KFileWidgetTest::testExtensionForSave() 0463 { 0464 QFETCH(QString, fileName); 0465 QFETCH(QString, filter); 0466 QFETCH(QString, expectedCurrentText); 0467 QFETCH(QString, expectedSelectedFileName); 0468 0469 // Use a temporary directory since the presence of existing files 0470 // influences whether an extension is automatically appended. 0471 QTemporaryDir tempDir; 0472 const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path()); 0473 const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName)); 0474 const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName)); 0475 createTestFile(tempDir.filePath("README")); 0476 0477 KFileWidget fw(dirUrl); 0478 fw.setOperationMode(KFileWidget::Saving); 0479 // Calling setFilter has side-effects and changes the file name. 0480 // The difference to testSetFilterForSave is that the filter is already set before the fileUrl 0481 // is set, and will not be changed after. 0482 fw.setFilter(filter); 0483 fw.setSelectedUrl(fileUrl); 0484 0485 // Verify the expected populated name. 0486 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl); 0487 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0488 0489 // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() 0490 // which calls KFileWidget::selectedUrls(). 0491 // Accept the filename to ensure that a filename is selected. 0492 connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept); 0493 QTest::keyClick(fw.locationEdit(), Qt::Key_Return); 0494 QList<QUrl> urls = fw.selectedUrls(); 0495 QCOMPARE(urls.size(), 1); 0496 QCOMPARE(urls[0], expectedFileUrl); 0497 } 0498 0499 void KFileWidgetTest::testFilterChange() 0500 { 0501 QTemporaryDir tempDir; 0502 createTestFile(tempDir.filePath("some.c")); 0503 bool created = QDir(tempDir.path()).mkdir("directory"); 0504 Q_ASSERT(created); 0505 0506 KFileWidget fw(QUrl::fromLocalFile(tempDir.path())); 0507 fw.setOperationMode(KFileWidget::Saving); 0508 fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("some.txt"))); 0509 fw.setFilter("*.txt|Txt\n*.c|C"); 0510 0511 // Initial filename. 0512 QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.txt")); 0513 QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt")); 0514 0515 // Select type with an existing file. 0516 fw.filterWidget()->setCurrentFilter("*.c|C"); 0517 QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.c")); 0518 QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.c")); 0519 0520 // Do not update extension if the current selection is a directory. 0521 fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("directory"))); 0522 fw.filterWidget()->setCurrentFilter("*.txt|Txt"); 0523 QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("directory")); 0524 QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt")); 0525 } 0526 0527 void KFileWidgetTest::testDropFile_data() 0528 { 0529 QTest::addColumn<QString>("dir"); 0530 QTest::addColumn<QString>("fileName"); 0531 QTest::addColumn<QString>("expectedCurrentText"); 0532 0533 QTest::newRow("some.txt") << "" 0534 << "some.txt" 0535 << "some.txt"; 0536 0537 QTest::newRow("subdir/some.txt") << "subdir" 0538 << "subdir/some.txt" 0539 << "some.txt"; 0540 } 0541 0542 void KFileWidgetTest::testDropFile() 0543 { 0544 QFETCH(QString, dir); 0545 QFETCH(QString, fileName); 0546 QFETCH(QString, expectedCurrentText); 0547 0548 // Use a temporary directory since the presence of existing files 0549 // influences whether an extension is automatically appended. 0550 QTemporaryDir tempDir; 0551 QUrl dirUrl = QUrl::fromLocalFile(tempDir.path()); 0552 const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName)); 0553 if (!dir.isEmpty()) { 0554 createTestDirectory(tempDir.filePath(dir)); 0555 dirUrl = QUrl::fromLocalFile(tempDir.filePath(dir)); 0556 } 0557 createTestFile(tempDir.filePath(fileName)); 0558 0559 KFileWidget fileWidget(QUrl::fromLocalFile(tempDir.path())); 0560 fileWidget.setOperationMode(KFileWidget::Saving); 0561 fileWidget.setMode(KFile::File); 0562 fileWidget.show(); 0563 0564 QMimeData *mimeData = new QMimeData(); 0565 mimeData->setUrls(QList<QUrl>() << fileUrl); 0566 0567 KDirLister *dirLister = fileWidget.dirOperator()->dirLister(); 0568 QSignalSpy spy(dirLister, qOverload<>(&KCoreDirLister::completed)); 0569 0570 QAbstractItemView *view = fileWidget.dirOperator()->view(); 0571 QVERIFY(view); 0572 0573 QDragEnterEvent event1(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier); 0574 QVERIFY(qApp->sendEvent(view->viewport(), &event1)); 0575 0576 // Fake drop 0577 QDropEvent event(QPoint(), Qt::DropAction::MoveAction, mimeData, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier); 0578 QVERIFY(qApp->sendEvent(view->viewport(), &event)); 0579 0580 if (!dir.isEmpty()) { 0581 // once we drop a file the dirlister scans the dir 0582 // wait for the completed signal from the dirlister 0583 QVERIFY(spy.wait()); 0584 } 0585 0586 // Verify the expected populated name. 0587 QCOMPARE(fileWidget.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl); 0588 QCOMPARE(fileWidget.locationEdit()->currentText(), expectedCurrentText); 0589 0590 // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() 0591 // which calls KFileWidget::selectedUrls(). 0592 // Accept the filename to ensure that a filename is selected. 0593 connect(&fileWidget, &KFileWidget::accepted, &fileWidget, &KFileWidget::accept); 0594 QTest::keyClick(fileWidget.locationEdit(), Qt::Key_Return); 0595 const QList<QUrl> urls = fileWidget.selectedUrls(); 0596 QCOMPARE(urls.size(), 1); 0597 QCOMPARE(urls[0], fileUrl); 0598 } 0599 0600 void KFileWidgetTest::testCreateNestedNewFolders() 0601 { 0602 // when creating multiple nested new folders in the "save as" dialog, where folders are 0603 // created and entered, kdirlister would hit an assert (in reinsert()), bug 408801 0604 QTemporaryDir tempDir; 0605 QVERIFY(tempDir.isValid()); 0606 const QString dir = tempDir.path(); 0607 const QUrl url = QUrl::fromLocalFile(dir); 0608 KFileWidget fw(url); 0609 fw.setOperationMode(KFileWidget::Saving); 0610 fw.setMode(KFile::File); 0611 0612 QString currentPath = dir; 0613 // create the nested folders 0614 for (int i = 1; i < 6; ++i) { 0615 fw.dirOperator()->mkdir(); 0616 QDialog *dialog; 0617 // QTRY_ because a NameFinderJob could be running and the dialog will be shown when 0618 // it finishes. 0619 QTRY_VERIFY(dialog = fw.findChild<QDialog *>()); 0620 QLineEdit *lineEdit = dialog->findChild<QLineEdit *>(); 0621 QVERIFY(lineEdit); 0622 const QString name = QStringLiteral("folder%1").arg(i); 0623 lineEdit->setText(name); 0624 // simulate the time the user will take to type the new folder name 0625 QTest::qWait(1000); 0626 0627 dialog->accept(); 0628 0629 currentPath += QLatin1Char('/') + name; 0630 // Wait till the filewidget changes to the new folder 0631 QTRY_COMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(), currentPath); 0632 } 0633 } 0634 0635 void KFileWidgetTest::testTokenize_data() 0636 { 0637 // Real filename (as in how they are stored in the fs) 0638 QTest::addColumn<QStringList>("fileNames"); 0639 // Escaped value of the text-box in the dialog 0640 QTest::addColumn<QString>("expectedCurrentText"); 0641 0642 QTest::newRow("simple") << QStringList{"test2"} << QString("test2"); 0643 0644 // When a single file with space is selected, it is _not_ quoted ... 0645 QTest::newRow("space-single-file") << QStringList{"test space"} << QString("test space"); 0646 0647 // However, when multiple files are selected, they are quoted 0648 QTest::newRow("space-multi-file") << QStringList{"test space", "test2"} << QString("\"test space\" \"test2\""); 0649 0650 // All quotes in names should be escaped, however since this is a single 0651 // file, the whole name will not be escaped. 0652 QTest::newRow("quote-single-file") << QStringList{"test\"quote"} << QString("test\\\"quote"); 0653 0654 // Escape multiple files. Files should also be wrapped in "" 0655 // Note that we are also testing quote at the end of the name 0656 QTest::newRow("quote-multi-file") << QStringList{"test\"quote", "test2-quote\"", "test"} << QString("\"test\\\"quote\" \"test2-quote\\\"\" \"test\""); 0657 0658 // Ok, enough with quotes... lets do some backslashes 0659 // Backslash literals in file names - Unix only case 0660 QTest::newRow("backslash-single-file") << QStringList{"test\\backslash"} << QString("test\\\\backslash"); 0661 0662 QTest::newRow("backslash-multi-file") << QStringList{"test\\back\\slash", "test"} << QString("\"test\\\\back\\\\slash\" \"test\""); 0663 0664 QTest::newRow("double-backslash-multi-file") << QStringList{"test\\\\back\\slash", "test"} << QString("\"test\\\\\\\\back\\\\slash\" \"test\""); 0665 0666 QTest::newRow("double-backslash-end") << QStringList{"test\\\\"} << QString("test\\\\\\\\"); 0667 0668 QTest::newRow("single-backslash-end") << QStringList{"some thing", "test\\"} << QString("\"some thing\" \"test\\\\\""); 0669 0670 QTest::newRow("sharp") << QStringList{"some#thing"} << QString("some#thing"); 0671 0672 // Filenames beginning with ':'; QDir::isAbsolutePath() always returns true 0673 // in that case, #322837 0674 QTest::newRow("file-beginning-with-colon") << QStringList{":test2"} << QString{":test2"}; 0675 0676 QTest::newRow("multiple-files-beginning-with-colon") << QStringList{":test space", ":test2"} << QString{"\":test space\" \":test2\""}; 0677 0678 // # 473228 0679 QTest::newRow("file-beginning-with-something-that-looks-like-a-url-scheme") << QStringList{"Hello: foo.txt"} << QString{"Hello: foo.txt"}; 0680 QTest::newRow("file-beginning-with-something-that-looks-like-a-file-url-scheme") << QStringList{"file: /foo.txt"} << QString{"file: /foo.txt"}; 0681 0682 QTemporaryDir otherTempDir; 0683 otherTempDir.setAutoRemove(false); 0684 const auto testFile1Path = otherTempDir.filePath("test-1"); 0685 createTestFile(testFile1Path); 0686 const auto testFile2Path = otherTempDir.filePath("test-2"); 0687 createTestFile(testFile2Path); 0688 0689 QTest::newRow("absolute-url-not-in-dir") << QStringList{"file://" + testFile1Path} << QString{"file://" + testFile1Path}; 0690 QTest::newRow("absolute-urls-not-in-dir") << QStringList{"file://" + testFile1Path, "file://" + testFile2Path} 0691 << QString{"\"file://" + testFile1Path + "\" \"file://" + testFile2Path + "\""}; 0692 0693 auto expectedtestFile1Path = testFile1Path; 0694 expectedtestFile1Path = expectedtestFile1Path.remove(0, 1); 0695 auto expectedtestFile2Path = testFile2Path; 0696 expectedtestFile2Path = expectedtestFile2Path.remove(0, 1); 0697 0698 QTest::newRow("absolute-url-not-in-dir-no-scheme") << QStringList{testFile1Path} << QString{testFile1Path}; 0699 QTest::newRow("absolute-urls-not-in-dir-no-scheme") 0700 << QStringList{testFile1Path, testFile2Path} << QString{"\"" + testFile1Path + "\" \"" + testFile2Path + "\""}; 0701 0702 QTest::newRow("absolute-urls-not-in-dir-scheme-mixed") 0703 << QStringList{testFile1Path, "file://" + testFile2Path} << QString{"\"" + testFile1Path + "\" \"file://" + testFile2Path + "\""}; 0704 } 0705 0706 void KFileWidgetTest::testTokenize() 0707 { 0708 // We will use setSelectedUrls([QUrl]) here in order to check correct 0709 // filename escaping. Afterwards we will accept() the dialog to confirm 0710 // correct result 0711 QFETCH(QStringList, fileNames); 0712 QFETCH(QString, expectedCurrentText); 0713 0714 QTemporaryDir tempDir; 0715 const QString tempDirPath = tempDir.path(); 0716 const QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath); 0717 QList<QUrl> fileUrls; 0718 for (const auto &fileName : fileNames) { 0719 auto localUrl = QUrl(fileName); 0720 if (!localUrl.path().startsWith(QLatin1Char('/'))) { 0721 const QString filePath = tempDirPath + QLatin1Char('/') + fileName; 0722 localUrl = QUrl::fromLocalFile(filePath); 0723 } 0724 fileUrls.append(localUrl); 0725 qCDebug(KIO_KFILEWIDGETS_FW) << fileName << " => " << localUrl; 0726 } 0727 0728 KFileWidget fw(tempDirUrl); 0729 fw.setOperationMode(KFileWidget::Opening); 0730 fw.setMode(KFile::Files); 0731 fw.setSelectedUrls(fileUrls); 0732 0733 // Verify the expected populated name. 0734 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), tempDirUrl); 0735 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0736 0737 // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() 0738 // which calls KFileWidget::selectedUrls(). 0739 // Accept the filename to ensure that a filename is selected. 0740 connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept); 0741 QTest::keyClick(fw.locationEdit(), Qt::Key_Return); 0742 const QList<QUrl> urls = fw.selectedUrls(); 0743 0744 // We must have the same size as requested files 0745 QCOMPARE(urls.size(), fileNames.size()); 0746 0747 for (auto &localUrl : fileUrls) { 0748 if (localUrl.scheme().isEmpty()) { 0749 localUrl.setScheme("file"); 0750 } 0751 } 0752 QCOMPARE(urls, fileUrls); 0753 } 0754 0755 void KFileWidgetTest::testTokenizeForSave_data() 0756 { 0757 // Real filename (as in how they are stored in the fs) 0758 QTest::addColumn<QString>("fileName"); 0759 // Escaped value of the text-box in the dialog 0760 // Escaped cwd: Because setSelectedUrl is called in this 0761 // test, it actually sets the CWD to the dirname and only 0762 // keeps the filename displayed in the test 0763 QTest::addColumn<QString>("expectedSubFolder"); 0764 QTest::addColumn<QString>("expectedCurrentText"); 0765 0766 QTest::newRow("save-simple") << QString{"test2"} << QString() << QString("test2"); 0767 0768 // When a single file with space is selected, it is _not_ quoted ... 0769 QTest::newRow("save-space") << QString{"test space"} << QString() << QString("test space"); 0770 0771 // All quotes in names should be escaped, however since this is a single 0772 // file, the whole name will not be escaped. 0773 QTest::newRow("save-quote") << QString{"test\"quote"} << QString() << QString("test\\\"quote"); 0774 0775 // Ok, enough with quotes... lets do some backslashes 0776 // Backslash literals in file names - Unix only case 0777 QTest::newRow("save-backslash") << QString{"test\\backslash"} << QString() << QString("test\\\\backslash"); 0778 0779 QTest::newRow("save-double-backslash") << QString{"test\\\\back\\slash"} << QString() << QString("test\\\\\\\\back\\\\slash"); 0780 0781 QTest::newRow("save-double-backslash-end") << QString{"test\\\\"} << QString() << QString("test\\\\\\\\"); 0782 0783 QTest::newRow("save-single-backslash-end") << QString{"test\\"} << QString() << QString("test\\\\"); 0784 0785 QTest::newRow("save-sharp") << QString{"some#thing"} << QString() << QString("some#thing"); 0786 0787 // Filenames beginning with ':'; QDir::isAbsolutePath() always returns true 0788 // in that case, #322837 0789 QTest::newRow("save-file-beginning-with-colon") << QString{":test2"} << QString() << QString{":test2"}; 0790 0791 // # 473228 0792 QTest::newRow("save-save-file-beginning-with-something-that-looks-like-a-url-scheme") 0793 << QString{"Hello: foo.txt"} << QString() << QString{"Hello: foo.txt"}; 0794 0795 QTemporaryDir otherTempDir; 0796 otherTempDir.setAutoRemove(false); 0797 const auto testFile1Path = otherTempDir.filePath("test-1"); 0798 createTestFile(testFile1Path); 0799 0800 QTest::newRow("save-absolute-url-not-in-dir") << QString{"file://" + testFile1Path} << otherTempDir.path() << QString{"test-1"}; 0801 0802 auto expectedtestFile1Path = testFile1Path; 0803 expectedtestFile1Path = expectedtestFile1Path.remove(0, 1); 0804 0805 QTest::newRow("save-absolute-url-not-in-dir-no-scheme") << QString{testFile1Path} << otherTempDir.path() << QString{"test-1"}; 0806 } 0807 0808 void KFileWidgetTest::testTokenizeForSave() 0809 { 0810 // We will use setSelectedUrls([QUrl]) here in order to check correct 0811 // filename escaping. Afterwards we will accept() the dialog to confirm 0812 // correct result 0813 0814 // This test is similar to testTokenize but focuses on single-file 0815 // "save" operation. This follows a different code-path internally 0816 // and calls setSelectedUrl instead of setSelectedUrls. 0817 0818 QFETCH(QString, fileName); 0819 QFETCH(QString, expectedSubFolder); 0820 QFETCH(QString, expectedCurrentText); 0821 0822 QTemporaryDir tempDir; 0823 const QString tempDirPath = tempDir.path(); 0824 const QUrl tempDirUrl = QUrl::fromLocalFile(tempDirPath); 0825 auto fileUrl = QUrl(fileName); 0826 if (!fileUrl.path().startsWith(QLatin1Char('/'))) { 0827 const QString filePath = tempDirPath + QLatin1Char('/') + fileName; 0828 fileUrl = QUrl::fromLocalFile(filePath); 0829 } 0830 if (fileUrl.scheme().isEmpty()) { 0831 fileUrl.setScheme("file"); 0832 } 0833 qCDebug(KIO_KFILEWIDGETS_FW) << fileName << " => " << fileUrl; 0834 0835 KFileWidget fw(tempDirUrl); 0836 fw.setOperationMode(KFileWidget::Saving); 0837 fw.setMode(KFile::File); 0838 fw.setSelectedUrl(fileUrl); 0839 0840 // Verify the expected populated name. 0841 if (expectedSubFolder != "") { 0842 const QUrl expectedBaseUrl = tempDirUrl.resolved(QUrl::fromLocalFile(expectedSubFolder)); 0843 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), expectedBaseUrl); 0844 } else { 0845 QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), tempDirUrl); 0846 } 0847 QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); 0848 0849 // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() 0850 // which calls KFileWidget::selectedUrls(). 0851 // Accept the filename to ensure that a filename is selected. 0852 connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept); 0853 QTest::keyClick(fw.locationEdit(), Qt::Key_Return); 0854 const QList<QUrl> urls = fw.selectedUrls(); 0855 0856 // We always only have one URL here 0857 QCOMPARE(urls.size(), 1); 0858 QCOMPARE(urls[0], fileUrl); 0859 } 0860 0861 QTEST_MAIN(KFileWidgetTest) 0862 0863 #include "kfilewidgettest.moc"