/* This file is part of the KDE project SPDX-FileCopyrightText: 2014 David Faure SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include #include #include #include #include "jobuidelegatefactory.h" #include "kiotesthelper.h" #include #include #include #include class KFileCopyToMenuTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { QStandardPaths::setTestModeEnabled(true); qputenv("KIOWORKER_ENABLE_TESTMODE", "1"); // ensure the KIO workers call QStandardPaths::setTestModeEnabled too QVERIFY(m_tempDir.isValid()); QVERIFY(m_tempDestDir.isValid()); QVERIFY(m_nonWritableTempDir.isValid()); QVERIFY(QFile(m_nonWritableTempDir.path()).setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::ExeOwner | QFile::ExeUser)); m_srcDir = m_tempDir.path(); m_destDir = m_tempDestDir.path(); m_srcFile = m_srcDir + QStringLiteral("/srcfile"); KIO::setDefaultJobUiDelegateFactory(nullptr); // no "skip" dialogs // Set a recent dir KConfigGroup recentDirsGroup(KSharedConfig::openConfig(), QStringLiteral("kuick-copy")); m_recentDirs << m_destDir + QStringLiteral("/nonexistentsubdir") // will be action number count-3 << m_nonWritableTempDir.path() // will be action number count-2 << m_destDir; // will be action number count-1 recentDirsGroup.writeEntry("Paths", m_recentDirs); m_lastActionCount = 0; } void cleanupTestCase() { QVERIFY(QFile(m_nonWritableTempDir.path()) .setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::WriteOwner | QFile::WriteUser | QFile::ExeOwner | QFile::ExeUser)); } // Before every test method, ensure the test file m_srcFile exists void init() { if (QFile::exists(m_srcFile)) { QVERIFY(QFileInfo(m_srcFile).isWritable()); } else { QFile srcFile(m_srcFile); QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString())); srcFile.write("Hello world\n"); } QVERIFY(QFileInfo(m_srcFile).isWritable()); } void shouldHaveParentWidget() { KFileCopyToMenu generator(&m_parentWidget); QCOMPARE(generator.parent(), &m_parentWidget); } void shouldAddActions() { KFileCopyToMenu generator(&m_parentWidget); QMenu menu; generator.addActionsTo(&menu); QList urls; urls << QUrl::fromLocalFile(m_srcFile); generator.setUrls(urls); QCOMPARE(extractActionNames(menu), QStringList() << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu")); QAction *copyMenuAction = menu.actions().at(0); // When menu.setActiveAction(copyMenuAction); menu.popup(QPoint(-100, -100)); // Then const QStringList actionNames = extractActionNames(*copyMenuAction->menu()); QVERIFY(!actionNames.isEmpty()); QCOMPARE(actionNames.first(), QStringLiteral("home")); QVERIFY(actionNames.contains(QLatin1String("browse"))); QCOMPARE(actionNames.at(actionNames.count() - 2), m_nonWritableTempDir.path()); QCOMPARE(actionNames.last(), m_destDir); } void shouldTryCopyingToRecentPath_data() { QTest::addColumn("actionNumber"); // from the bottom of the menu, starting at 1; see the recentDirs list in initTestCase QTest::addColumn("expectedErrorCode"); QTest::newRow("working") << 1 << 0; // no error QTest::newRow("non_writable") << 2 << int(KIO::ERR_WRITE_ACCESS_DENIED); QTest::newRow("non_existing") << 3 << int(KIO::ERR_CANNOT_OPEN_FOR_WRITING); } void shouldTryCopyingToRecentPath() { QFETCH(int, actionNumber); QFETCH(int, expectedErrorCode); KFileCopyToMenu generator(&m_parentWidget); QMenu menu; QList urls; urls << QUrl::fromLocalFile(m_srcFile); generator.setUrls(urls); generator.addActionsTo(&menu); QAction *copyMenuAction = menu.actions().at(0); menu.setActiveAction(copyMenuAction); menu.popup(QPoint(-100, -100)); const QList actions = copyMenuAction->menu()->actions(); if (m_lastActionCount == 0) { m_lastActionCount = actions.count(); } else { QCOMPARE(actions.count(), m_lastActionCount); // should be stable, i.e. selecting a recent dir shouldn't duplicate it } QAction *copyAction = actions.at(actions.count() - actionNumber); QSignalSpy spy(&generator, &KFileCopyToMenu::error); // When copyAction->trigger(); // Then QTRY_COMPARE(spy.count(), expectedErrorCode ? 1 : 0); if (expectedErrorCode) { QCOMPARE(spy.at(0).at(0).toInt(), expectedErrorCode); } else { QTRY_VERIFY(QFile::exists(m_destDir + QStringLiteral("/srcfile"))); } if (actionNumber == 1) { // This part only makes sense if the copy is going to work QFile::remove(m_destDir + QStringLiteral("/srcfile")); // Remove the recent dirs entry for m_destDir from the config KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("kuick-copy")); auto list = group.readEntry("Paths", QStringList{}); list.removeOne(m_destDir); group.writeEntry("Paths", list); group.sync(); // Triggering the action again, should insert a recent dirs corresponding // to m_destDir, which calls KFileCopyToMainMenu::copyOrMoveTo(), which // trims the recent dirs config to 10 urls, and _shouldn't_ crash copyAction->trigger(); // Back to normal for the next tests to pass group.writeEntry("Paths", m_recentDirs); } } private: static QStringList extractActionNames(const QMenu &menu) { QStringList ret; const QList actionsList = menu.actions(); for (const QAction *action : actionsList) { ret.append(action->objectName()); } return ret; } QTemporaryDir m_tempDir; QString m_srcDir; QString m_srcFile; QTemporaryDir m_tempDestDir; QString m_destDir; QTemporaryDir m_nonWritableTempDir; QWidget m_parentWidget; QStringList m_recentDirs; int m_lastActionCount; }; QTEST_MAIN(KFileCopyToMenuTest) #include "kfilecopytomenutest.moc"