File indexing completed on 2024-06-23 10:37:22

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2014 David Faure <faure@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 <QMenu>
0009 #include <QSignalSpy>
0010 #include <QTemporaryDir>
0011 #include <QTest>
0012 
0013 #include "jobuidelegatefactory.h"
0014 #include "kiotesthelper.h"
0015 #include <KConfigGroup>
0016 #include <KFileCopyToMenu>
0017 #include <KSharedConfig>
0018 
0019 #include <KIO/CopyJob>
0020 
0021 class KFileCopyToMenuTest : public QObject
0022 {
0023     Q_OBJECT
0024 
0025 private Q_SLOTS:
0026     void initTestCase()
0027     {
0028         QStandardPaths::setTestModeEnabled(true);
0029         qputenv("KIOSLAVE_ENABLE_TESTMODE", "1"); // ensure the KIO workers call QStandardPaths::setTestModeEnabled too
0030 
0031         QVERIFY(m_tempDir.isValid());
0032         QVERIFY(m_tempDestDir.isValid());
0033         QVERIFY(m_nonWritableTempDir.isValid());
0034         QVERIFY(QFile(m_nonWritableTempDir.path()).setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::ExeOwner | QFile::ExeUser));
0035         m_srcDir = m_tempDir.path();
0036         m_destDir = m_tempDestDir.path();
0037 
0038         m_srcFile = m_srcDir + QStringLiteral("/srcfile");
0039 
0040         KIO::setDefaultJobUiDelegateFactoryV2(nullptr); // no "skip" dialogs
0041 
0042         // Set a recent dir
0043         KConfigGroup recentDirsGroup(KSharedConfig::openConfig(), "kuick-copy");
0044         m_recentDirs << m_destDir + QStringLiteral("/nonexistentsubdir") // will be action number count-3
0045                      << m_nonWritableTempDir.path() // will be action number count-2
0046                      << m_destDir; // will be action number count-1
0047         recentDirsGroup.writeEntry("Paths", m_recentDirs);
0048 
0049         m_lastActionCount = 0;
0050     }
0051 
0052     void cleanupTestCase()
0053     {
0054         QVERIFY(QFile(m_nonWritableTempDir.path())
0055                     .setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::WriteOwner | QFile::WriteUser | QFile::ExeOwner | QFile::ExeUser));
0056     }
0057 
0058     // Before every test method, ensure the test file m_srcFile exists
0059     void init()
0060     {
0061         if (QFile::exists(m_srcFile)) {
0062             QVERIFY(QFileInfo(m_srcFile).isWritable());
0063         } else {
0064             QFile srcFile(m_srcFile);
0065             QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString()));
0066             srcFile.write("Hello world\n");
0067         }
0068         QVERIFY(QFileInfo(m_srcFile).isWritable());
0069     }
0070 
0071     void shouldHaveParentWidget()
0072     {
0073         KFileCopyToMenu generator(&m_parentWidget);
0074         QCOMPARE(generator.parent(), &m_parentWidget);
0075     }
0076 
0077     void shouldAddActions()
0078     {
0079         KFileCopyToMenu generator(&m_parentWidget);
0080         QMenu menu;
0081         generator.addActionsTo(&menu);
0082         QList<QUrl> urls;
0083         urls << QUrl::fromLocalFile(m_srcFile);
0084         generator.setUrls(urls);
0085         QCOMPARE(extractActionNames(menu), QStringList() << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu"));
0086         QAction *copyMenuAction = menu.actions().at(0);
0087 
0088         // When
0089         menu.setActiveAction(copyMenuAction);
0090         menu.popup(QPoint(-100, -100));
0091 
0092         // Then
0093         const QStringList actionNames = extractActionNames(*copyMenuAction->menu());
0094         QVERIFY(!actionNames.isEmpty());
0095         QCOMPARE(actionNames.first(), QStringLiteral("home"));
0096         QVERIFY(actionNames.contains(QLatin1String("browse")));
0097         QCOMPARE(actionNames.at(actionNames.count() - 2), m_nonWritableTempDir.path());
0098         QCOMPARE(actionNames.last(), m_destDir);
0099     }
0100 
0101     void shouldTryCopyingToRecentPath_data()
0102     {
0103         QTest::addColumn<int>("actionNumber"); // from the bottom of the menu, starting at 1; see the recentDirs list in initTestCase
0104         QTest::addColumn<int>("expectedErrorCode");
0105 
0106         QTest::newRow("working") << 1 << 0; // no error
0107         QTest::newRow("non_writable") << 2 << int(KIO::ERR_WRITE_ACCESS_DENIED);
0108         QTest::newRow("non_existing") << 3 << int(KIO::ERR_CANNOT_OPEN_FOR_WRITING);
0109     }
0110 
0111     void shouldTryCopyingToRecentPath()
0112     {
0113         QFETCH(int, actionNumber);
0114         QFETCH(int, expectedErrorCode);
0115 
0116         KFileCopyToMenu generator(&m_parentWidget);
0117         QMenu menu;
0118         QList<QUrl> urls;
0119         urls << QUrl::fromLocalFile(m_srcFile);
0120         generator.setUrls(urls);
0121         generator.addActionsTo(&menu);
0122         QAction *copyMenuAction = menu.actions().at(0);
0123         menu.setActiveAction(copyMenuAction);
0124 
0125         menu.popup(QPoint(-100, -100));
0126         const QList<QAction *> actions = copyMenuAction->menu()->actions();
0127         if (m_lastActionCount == 0) {
0128             m_lastActionCount = actions.count();
0129         } else {
0130             QCOMPARE(actions.count(), m_lastActionCount); // should be stable, i.e. selecting a recent dir shouldn't duplicate it
0131         }
0132         QAction *copyAction = actions.at(actions.count() - actionNumber);
0133         QSignalSpy spy(&generator, &KFileCopyToMenu::error);
0134 
0135         // When
0136         copyAction->trigger();
0137 
0138         // Then
0139         QTRY_COMPARE(spy.count(), expectedErrorCode ? 1 : 0);
0140         if (expectedErrorCode) {
0141             QCOMPARE(spy.at(0).at(0).toInt(), expectedErrorCode);
0142         } else {
0143             QTRY_VERIFY(QFile::exists(m_destDir + QStringLiteral("/srcfile")));
0144         }
0145 
0146         if (actionNumber == 1) { // This part only makes sense if the copy is going to work
0147             QFile::remove(m_destDir + QStringLiteral("/srcfile"));
0148             // Remove the recent dirs entry for m_destDir from the config
0149             KConfigGroup group(KSharedConfig::openConfig(), "kuick-copy");
0150             auto list = group.readEntry("Paths", QStringList{});
0151             list.removeOne(m_destDir);
0152             group.writeEntry("Paths", list);
0153             group.sync();
0154             // Triggering the action again, should insert a recent dirs corresponding
0155             // to m_destDir, which calls KFileCopyToMainMenu::copyOrMoveTo(), which
0156             // trims the recent dirs config to 10 urls, and _shouldn't_ crash
0157             copyAction->trigger();
0158 
0159             // Back to normal for the next tests to pass
0160             group.writeEntry("Paths", m_recentDirs);
0161         }
0162     }
0163 
0164 private:
0165     static QStringList extractActionNames(const QMenu &menu)
0166     {
0167         QStringList ret;
0168         const QList<QAction *> actionsList = menu.actions();
0169         for (const QAction *action : actionsList) {
0170             ret.append(action->objectName());
0171         }
0172         return ret;
0173     }
0174 
0175     QTemporaryDir m_tempDir;
0176     QString m_srcDir;
0177     QString m_srcFile;
0178     QTemporaryDir m_tempDestDir;
0179     QString m_destDir;
0180     QTemporaryDir m_nonWritableTempDir;
0181     QWidget m_parentWidget;
0182     QStringList m_recentDirs;
0183     int m_lastActionCount;
0184 };
0185 
0186 QTEST_MAIN(KFileCopyToMenuTest)
0187 
0188 #include "kfilecopytomenutest.moc"