diff --git a/containments/desktop/plugins/folder/CMakeLists.txt b/containments/desktop/plugins/folder/CMakeLists.txt --- a/containments/desktop/plugins/folder/CMakeLists.txt +++ b/containments/desktop/plugins/folder/CMakeLists.txt @@ -1,3 +1,9 @@ +include(GenerateExportHeader) + +if(BUILD_TESTING) + add_definitions(-DBUILD_TESTING) +endif(BUILD_TESTING) + set(folderplugin_SRCS directorypicker.cpp foldermodel.cpp @@ -32,3 +38,9 @@ KF5::ConfigGui) install(TARGETS folderplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/private/desktopcontainment/folder) + +generate_export_header(folderplugin BASE_NAME folderplugin) + +if(BUILD_TESTING) + add_subdirectory(tests) +endif(BUILD_TESTING) diff --git a/containments/desktop/plugins/folder/foldermodel.h b/containments/desktop/plugins/folder/foldermodel.h --- a/containments/desktop/plugins/folder/foldermodel.h +++ b/containments/desktop/plugins/folder/foldermodel.h @@ -37,6 +37,8 @@ #include +#include "folderplugin_private_export.h" + class QDrag; class QItemSelectionModel; class QQuickItem; @@ -69,7 +71,7 @@ void handleError(KIO::Job *job) Q_DECL_OVERRIDE; }; -class FolderModel : public QSortFilterProxyModel +class FOLDERPLUGIN_TESTS_EXPORT FolderModel : public QSortFilterProxyModel { Q_OBJECT diff --git a/containments/desktop/plugins/folder/folderplugin_private_export.h b/containments/desktop/plugins/folder/folderplugin_private_export.h new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/folderplugin_private_export.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company * + * * + * Author: Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef FOLDERPLUGIN_PRIVATE_EXPORT_H +#define FOLDERPLUGIN_PRIVATE_EXPORT_H + +#include "folderplugin_export.h" + +/* Classes which are exported only for unit tests */ +#ifdef BUILD_TESTING +#ifndef FOLDERPLUGIN_TESTS_EXPORT +#define FOLDERPLUGIN_TESTS_EXPORT FOLDERPLUGIN_EXPORT +#endif +#else /* not compiling tests */ +#endif + +#endif // FOLDERPLUGIN_PRIVATE_EXPORT_H diff --git a/containments/desktop/plugins/folder/positioner.h b/containments/desktop/plugins/folder/positioner.h --- a/containments/desktop/plugins/folder/positioner.h +++ b/containments/desktop/plugins/folder/positioner.h @@ -22,11 +22,13 @@ #include +#include "folderplugin_private_export.h" + class FolderModel; class QTimer; -class Positioner : public QAbstractItemModel +class FOLDERPLUGIN_TESTS_EXPORT Positioner : public QAbstractItemModel { Q_OBJECT diff --git a/containments/desktop/plugins/folder/tests/CMakeLists.txt b/containments/desktop/plugins/folder/tests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test) + +include(ECMAddTests) + +include_directories("..";${include_directories}) + +ecm_add_tests( + foldermodeltest.cpp + positionertest.cpp + LINK_LIBRARIES Qt5::Test folderplugin +) + + diff --git a/containments/desktop/plugins/folder/tests/foldermodeltest.h b/containments/desktop/plugins/folder/tests/foldermodeltest.h new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/tests/foldermodeltest.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company * + * * + * Author: Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#ifndef FOLDERMODELTEST_H +#define FOLDERMODELTEST_H + +#include + +class QTemporaryDir; +class FolderModel; + +class FolderModelTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + void tst_listing(); + void tst_listingDescending(); + void tst_listingFolderNotFirst(); + void tst_filterListing(); + void tst_cd(); + void tst_rename_data(); + void tst_rename(); + void tst_selection(); + void tst_defaultValues(); + void tst_actionMenu(); + void tst_lockedChanged(); + +private: + FolderModel *m_folderModel; + QTemporaryDir *m_folderDir; +}; + +#endif // FOLDERMODELTEST_H diff --git a/containments/desktop/plugins/folder/tests/foldermodeltest.cpp b/containments/desktop/plugins/folder/tests/foldermodeltest.cpp new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/tests/foldermodeltest.cpp @@ -0,0 +1,264 @@ +/*************************************************************************** + * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company * + * * + * Author: Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "foldermodeltest.h" +#include "foldermodel.h" + +#include +#include +#include + +QTEST_MAIN(FolderModelTest) + +static const QLatin1String desktop(QLatin1String("Desktop")); + +void FolderModelTest::initTestCase() +{ + m_folderDir = new QTemporaryDir(); + + QDir dir(m_folderDir->path()); + dir.mkdir(desktop); + dir.cd(desktop); + dir.mkdir("firstDir"); + QFile f; + for (int i = 1; i < 10; i++) { + f.setFileName(QStringLiteral("%1/file%2.txt").arg(dir.path(), QString::number(i))); + f.open(QFile::WriteOnly); + f.close(); + } + +} + +void FolderModelTest::cleanupTestCase() +{ + delete m_folderDir; +} + +void FolderModelTest::init() +{ + m_folderModel = new FolderModel(this); + m_folderModel->setUrl(m_folderDir->path() + QDir::separator() + desktop ); + QSignalSpy s(m_folderModel, &FolderModel::listingCompleted); + s.wait(1000); +} + +void FolderModelTest::cleanup() +{ + delete m_folderModel; + m_folderModel = nullptr; +} + +void FolderModelTest::tst_listing() +{ + + QCOMPARE(m_folderModel->url(), m_folderDir->path() + QDir::separator() + desktop); + + const auto count = m_folderModel->rowCount(); + QCOMPARE(count, 10); + QCOMPARE(m_folderModel->index(0, 0).data(FolderModel::FileNameRole).toString(), + QLatin1String("firstDir")); + for (int i = 1; i < count; i++) { + const auto index = m_folderModel->index(i, 0); + QCOMPARE(index.data(FolderModel::FileNameRole).toString(), + QStringLiteral("file%1.txt").arg(i)); + } +} + +void FolderModelTest::tst_listingDescending() +{ + m_folderModel->setSortDesc(true); + QCOMPARE(m_folderModel->index(0, 0).data(FolderModel::FileNameRole).toString(), + QLatin1String("firstDir")); + const auto count = m_folderModel->rowCount(); + for (int i = 1; i < count; i++) { + const auto index = m_folderModel->index(i, 0); + QCOMPARE(index.data(FolderModel::FileNameRole).toString(), + QStringLiteral("file%1.txt").arg(count - i)); + } +} + +void FolderModelTest::tst_listingFolderNotFirst() +{ + const auto count = m_folderModel->rowCount(); + m_folderModel->setSortDirsFirst(false); + QCOMPARE(count, 10); + QCOMPARE(m_folderModel->index(9, 0).data(FolderModel::FileNameRole).toString(), + QLatin1String("firstDir")); + for (int i = 0; i < count - 1; i++) { + const auto index = m_folderModel->index(i, 0); + QCOMPARE(index.data(FolderModel::FileNameRole).toString(), + QStringLiteral("file%1.txt").arg(i + 1)); + } +} + +void FolderModelTest::tst_filterListing() +{ + // a little bit weird API, as both pattern and mimetype needs to be set + m_folderModel->setFilterPattern("*.txt"); + m_folderModel->setFilterMimeTypes({"all/all"}); + m_folderModel->setFilterMode(FolderModel::FilterShowMatches); + const auto count = m_folderModel->rowCount(); + QCOMPARE(count, 9); + for (int i = 0; i < count; i++) { + const auto index = m_folderModel->index(i, 0); + QCOMPARE(index.data(FolderModel::FileNameRole).toString(), + QStringLiteral("file%1.txt").arg(i + 1)); + } +} + +void FolderModelTest::tst_cd() +{ + QSignalSpy s(m_folderModel, &FolderModel::listingCompleted); + + //go into firstDir subfolder + const auto url = m_folderModel->resolvedUrl(); + m_folderModel->cd(0); + QVERIFY(s.wait(500)); + const auto url2 = m_folderModel->resolvedUrl(); + QVERIFY(url.isParentOf(url2)); + + //go back to Desktop + m_folderModel->up(); + QVERIFY(s.wait(500)); + QCOMPARE(m_folderModel->resolvedUrl(), url); + + //try to cd to an invalid entry (a file) + m_folderModel->cd(1); + //Signal is not emitted here as it's invalided + QVERIFY(!s.wait(500)); + QCOMPARE(m_folderModel->resolvedUrl(), url); +} + +void FolderModelTest::tst_rename_data() +{ + QTest::addColumn("row"); + QTest::addColumn("name"); + QTest::newRow("Folder rename") << 0 << "firstDirRenamed"; + QTest::newRow("File rename") << 1 << "file1.pdf"; + QTest::newRow("Invalid rename") << 11 << "foo"; +} + +void FolderModelTest::tst_rename() +{ + QFETCH(int, row); + QFETCH(QString, name); + m_folderModel->rename(row, name); + QSignalSpy s(m_folderModel, &FolderModel::listingCompleted); + const auto index = m_folderModel->index(row, 0); + s.wait(500); + QEXPECT_FAIL("Invalid rename", "This is expected to fail", Continue); + QCOMPARE(index.data(FolderModel::FileNameRole).toString(), name); +} + +void FolderModelTest::tst_selection() +{ + m_folderModel->setSelected(1); + QVERIFY(m_folderModel->hasSelection()); + QVERIFY(m_folderModel->isSelected(1)); + + m_folderModel->clearSelection(); + QVERIFY(!m_folderModel->hasSelection()); + + m_folderModel->toggleSelected(1); + QVERIFY(m_folderModel->isSelected(1)); + m_folderModel->toggleSelected(1); + QVERIFY(!m_folderModel->isSelected(1)); + + m_folderModel->setRangeSelected(1, 4); + QVERIFY(m_folderModel->hasSelection()); + for (int i = 1; i <= 4; i++) { + QVERIFY(m_folderModel->isSelected(i)); + } + + m_folderModel->updateSelection({5, 6}, false); + for (int i = 1; i <= 4; i++) { + QVERIFY(!m_folderModel->isSelected(i)); + } + QVERIFY(m_folderModel->isSelected(5)); + QVERIFY(m_folderModel->isSelected(6)); + + m_folderModel->setRangeSelected(1, 4); + m_folderModel->pinSelection(); + m_folderModel->updateSelection({5, 6}, true); + for (int i = 1; i <= 6; i++) { + QVERIFY(m_folderModel->isSelected(i)); + } + + m_folderModel->unpinSelection(); + m_folderModel->updateSelection({5, 6}, true); + for (int i = 1; i <= 6; i++) { + if (i < 5) { + QVERIFY(!m_folderModel->isSelected(i)); + } else { + QVERIFY(m_folderModel->isSelected(i)); + } + } +} + +void FolderModelTest::tst_defaultValues() +{ + FolderModel folderModel; + QCOMPARE(folderModel.status(), FolderModel::Status::None); + QVERIFY(folderModel.locked()); + QVERIFY(!folderModel.sortDesc()); + QVERIFY(folderModel.sortDirsFirst()); + QVERIFY(!folderModel.parseDesktopFiles()); + QVERIFY(!folderModel.previews()); + QVERIFY(!folderModel.usedByContainment()); + QCOMPARE(folderModel.sortMode(), 0); + QCOMPARE(folderModel.filterMode(), (int)FolderModel::NoFilter); + QVERIFY(folderModel.newMenu()); +} + +void FolderModelTest::tst_actionMenu() +{ + const QStringList lst { + QStringLiteral("open"), + QStringLiteral("cut"), + QStringLiteral("open"), + QStringLiteral("cut"), + QStringLiteral("undo"), + QStringLiteral("copy"), + QStringLiteral("paste"), + QStringLiteral("pasteto"), + QStringLiteral("reload"), + QStringLiteral("refresh"), + QStringLiteral("rename"), + QStringLiteral("trash"), + QStringLiteral("del"), + QStringLiteral("restoreFromTrash"), + QStringLiteral("emptyTrash") + }; + for (const QString &str : lst) { + QVERIFY(m_folderModel->action(str)); + } +} + +void FolderModelTest::tst_lockedChanged() +{ + QSignalSpy s(m_folderModel, &FolderModel::lockedChanged); + m_folderModel->setLocked(false); + QCOMPARE(s.count(), 1); + m_folderModel->setLocked(false); + QCOMPARE(s.count(), 1); + m_folderModel->setLocked(true); + QCOMPARE(s.count(), 2); +} diff --git a/containments/desktop/plugins/folder/tests/positionertest.h b/containments/desktop/plugins/folder/tests/positionertest.h new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/tests/positionertest.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company * + * * + * Author: Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + + +#ifndef POSITIONERTEST_H +#define POSITIONERTEST_H + +#include + +class QTemporaryDir; +class FolderModel; +class Positioner; + +class PositionerTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + void tst_positions_data(); + void tst_positions(); + void tst_map(); + void tst_move_data(); + void tst_move(); + void tst_nearestitem_data(); + void tst_nearestitem(); + void tst_isBlank(); + void tst_reset(); + void tst_defaultValues(); + +private: + void checkPositions(int perStripe); + + Positioner *m_positioner; + FolderModel *m_folderModel; + QTemporaryDir *m_folderDir; +}; + +#endif // POSITIONERTEST_H diff --git a/containments/desktop/plugins/folder/tests/positionertest.cpp b/containments/desktop/plugins/folder/tests/positionertest.cpp new file mode 100644 --- /dev/null +++ b/containments/desktop/plugins/folder/tests/positionertest.cpp @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company * + * * + * Author: Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * + ***************************************************************************/ + +#include "positionertest.h" + +#include + +#include +#include +#include + +#include "foldermodel.h" +#include "positioner.h" + +QTEST_MAIN(PositionerTest) + +static const QLatin1String desktop(QLatin1String("Desktop")); + +void PositionerTest::initTestCase() +{ + m_folderDir = new QTemporaryDir(); + + QDir dir(m_folderDir->path()); + dir.mkdir(desktop); + dir.cd(desktop); + dir.mkdir("firstDir"); + QFile f; + for (int i = 1; i < 10; i++) { + f.setFileName(QStringLiteral("%1/file%2.txt").arg(dir.path(), QString::number(i))); + f.open(QFile::WriteOnly); + f.close(); + } +} + +void PositionerTest::cleanupTestCase() +{ + delete m_folderDir; +} + +void PositionerTest::init() +{ + m_folderModel = new FolderModel(this); + m_positioner = new Positioner(this); + m_positioner->setEnabled(true); + m_positioner->setFolderModel(m_folderModel); + m_positioner->setPerStripe(3); + + m_folderModel->setUrl(m_folderDir->path() + QDir::separator() + desktop ); + QSignalSpy s(m_folderModel, &FolderModel::listingCompleted); + s.wait(1000); +} + +void PositionerTest::cleanup() +{ + delete m_folderModel; + m_folderModel = nullptr; + delete m_positioner; + m_positioner = nullptr; +} + +void PositionerTest::tst_positions_data() +{ + QTest::addColumn("perStripe"); + QTest::newRow("3 per column") << 3; + QTest::newRow("5 per column") << 5; +} + +void PositionerTest::tst_positions() +{ + QFETCH(int, perStripe); + m_positioner->setPerStripe(perStripe); + checkPositions(perStripe); +} + +void PositionerTest::tst_map() +{ + //by default the mapping is 1-1 + for (int i = 0; i < m_positioner->rowCount(); i++) { + QCOMPARE(m_positioner->map(i), i); + } +} + +void PositionerTest::tst_move_data() +{ + QTest::addColumn("moves"); + QTest::addColumn >("result"); + QTest::newRow("First to last") << QVariantList({0, 10}) + << QVector({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0}); + QTest::newRow("First to after last") << QVariantList({0, 11}) + << QVector({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, 0}); + QTest::newRow("Switch 2nd with 3rd ") << QVariantList({1, 2, 2, 1}) + << QVector({0, 2, 1, 3, 4, 5, 6, 7, 8, 9}); + QTest::newRow("Switch 2nd with 2nd ") << QVariantList({1, 1, 1, 1}) + << QVector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + QTest::newRow("2nd to last") << QVariantList({2, 10}) + << QVector({0, 1, -1, 3, 4, 5, 6, 7, 8, 9, 2}); +} + + +void PositionerTest::tst_move() +{ + QFETCH(QVariantList, moves); + QFETCH(QVector, result); + m_positioner->move(moves); + for (int i = 0; i < m_positioner->rowCount(); i++) { + QCOMPARE(m_positioner->map(i), result[i]); + } +} + +void PositionerTest::tst_nearestitem_data() +{ + QTest::addColumn("index"); + QTest::addColumn >("result"); + QTest::newRow("Around first") << 0 << QVector{-1, -1, 3, -1, 1}; + QTest::newRow("Around second") << 1 << QVector{-1, -1, 4, 0, 2}; + QTest::newRow("Around 5th") << 4 << QVector{-1, 1, 7, 3, 5}; + QTest::newRow("Around last") << 9 << QVector{-1, 6, -1, -1, 7}; + QTest::newRow("Around invalid") << 11 << QVector{-1, -1, -1, -1, -1}; + +} + +void PositionerTest::tst_nearestitem() +{ + QFETCH(int, index); + QFETCH(QVector, result); + for (int i = Qt::NoArrow; i <= Qt::RightArrow; i++) { + QCOMPARE(m_positioner->nearestItem(index, (Qt::ArrowType)i), result[i]); + } +} + +void PositionerTest::tst_isBlank() +{ + QCOMPARE(m_positioner->isBlank(0), false); + QCOMPARE(m_positioner->isBlank(11), true); + m_positioner->move({0, 10}); + QCOMPARE(m_positioner->isBlank(0), true); +} + +void PositionerTest::tst_reset() +{ + m_positioner->move({0, 10}); + m_positioner->reset(); + QSignalSpy s(m_positioner, &Positioner::positionsChanged); + s.wait(500); + + checkPositions(3); + + for (int i = 0; i < m_positioner->rowCount(); i++) { + QCOMPARE(m_positioner->map(i), i); + } +} + +void PositionerTest::tst_defaultValues() +{ + Positioner positioner; + QVERIFY(!positioner.enabled()); + QVERIFY(!positioner.folderModel()); + QCOMPARE(positioner.perStripe(), 0); + QVERIFY(positioner.positions().isEmpty()); +} + +void PositionerTest::checkPositions(int perStripe) +{ + QSignalSpy s(m_positioner, &Positioner::positionsChanged); + s.wait(500); + + const auto positions = m_positioner->positions(); + struct Pos { + int x; + int y; + }; + const auto fileCount = m_folderModel->rowCount(); + QHash posHash; + QCOMPARE(positions[0].toInt(), 1 + ((fileCount - 1) / perStripe)); // rows + QCOMPARE(positions[1].toInt(), perStripe); // columns + for (int i = 2; i < positions.length() - 2; i+=3) { + posHash[positions[i]] = {positions[i + 1].toInt(), positions[i + 2].toInt()}; + } + + int row = 0; + int col = 0; + for (int i = 0; i < fileCount; i++) { + const auto index = m_folderModel->index(i, 0); + const auto url = index.data(FolderModel::UrlRole).toString(); + const Pos pos = posHash[url]; + QCOMPARE(pos.x, row); + QCOMPARE(pos.y, col); + col++; + if (col == perStripe) { + row++; + col = 0; + } + } +}