diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e53c1f2d..a0fd6df6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,5 +4,11 @@ include_directories(${CMAKE_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_BINARY_DIR}) find_package(Qt5Test) -add_executable(tst_docks tst_docks.cpp utils.cpp) +set(TESTING_SRCS utils.cpp Testing.cpp) + +add_executable(tst_docks tst_docks.cpp ${TESTING_SRCS}) target_link_libraries(tst_docks kddockwidgets Qt5::Widgets Qt5::Test) + +##### Fuzzer +add_executable(fuzzer fuzzer.cpp ${TESTING_SRCS}) +target_link_libraries(fuzzer kddockwidgets Qt5::Widgets Qt5::Test) diff --git a/tests/Testing.cpp b/tests/Testing.cpp new file mode 100644 index 00000000..7e2256ac --- /dev/null +++ b/tests/Testing.cpp @@ -0,0 +1,127 @@ +/* + This file is part of KDDockWidgets. + + Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Author: Sérgio Martins + + 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, see . +*/ + +#include "Testing.h" +#include "MainWindow.h" +#include "DockWidget.h" +#include "DockRegistry_p.h" + +#include + +using namespace KDDockWidgets; +using namespace KDDockWidgets::Testing; + + +class HostedWidget : public QWidget +{ +public: + + explicit HostedWidget(QSize minSz = QSize(1,1)) + : m_minSz(minSz) + { + + } + + QSize sizeHint() const override + { + return m_minSz; + } + + QSize minimumSizeHint() const override + { + return m_minSz; + } + + void setMinSize(QSize s) + { + m_minSz = s; + updateGeometry(); + } + + QSize m_minSz; +}; + +static MainWindow* createMainWindow(const Testing::MainWindowDescriptor &mwd) +{ + static int count = 0; + count++; + auto mainWindow = new MainWindow(QStringLiteral("MainWindow-%1").arg(count), mwd.mainWindowOption); + + mainWindow->setGeometry(mwd.geometry); + + mainWindow->show(); + return mainWindow; +} + +static DockWidget* createDockWidget(const Testing::DockWidgetDescriptor &dwd) +{ + static int count = 0; + count++; + auto dockWidget = new DockWidget(QStringLiteral("DockWidget-%1").arg(count)); + + dockWidget->setWidget(new HostedWidget(dwd.minSize)); + + if (dwd.isFloating) + dockWidget->setGeometry(dwd.geometry); + + if (dwd.isVisible) + dockWidget->show(); + + return dockWidget; +} + +static void createLayout(const Layout &layout) +{ + for (const Testing::MainWindowDescriptor &mwd : layout.mainWindows) { + createMainWindow(mwd); + } + + for (const Testing::DockWidgetDescriptor &dwd : layout.dockWidgets) { + createDockWidget(dwd); + } + +} + +static void runOperation(const Operation &) +{ +} + +void Testing::runTest(const Test &test) +{ + if (!DockRegistry::self()->isEmpty()) + qFatal("There's dock widgets and the start runTest"); + + createLayout(test.initialLayout); + for (const auto &op : test.operations) { + runOperation(op); + } + + for (MainWindowBase *mw : DockRegistry::self()->mainwindows()) + delete mw; + + for (FloatingWindow *fw : DockRegistry::self()->nestedwindows()) + delete fw; + + for (DockWidgetBase *dw : DockRegistry::self()->dockwidgets()) + delete dw; + + if (!DockRegistry::self()->isEmpty()) + qFatal("There's still dock widgets and the end of runTest"); +} diff --git a/tests/Testing.h b/tests/Testing.h new file mode 100644 index 00000000..46c5b509 --- /dev/null +++ b/tests/Testing.h @@ -0,0 +1,84 @@ +/* + This file is part of KDDockWidgets. + + Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Author: Sérgio Martins + + 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, see . +*/ + +// We don't care about performance related checks in the tests +// clazy:excludeall=ctor-missing-parent-argument,missing-qobject-macro,range-loop,missing-typeinfo,detaching-member,function-args-by-ref,non-pod-global-static,reserve-candidates,qstring-allocations + +#include "KDDockWidgets.h" + +#include +#include +#include + +/** + * @file + * @brief Namespace for tests related enums and classes + */ + +namespace KDDockWidgets { +namespace Testing { + + enum OperationType { + OperationType_None = 0, + OperationType_CloseViaDockWidgetAPI, ///< Closing programatically via DockWidget::close() + OperationType_HideViaDockWidgetAPI, ///< Hidding programatically via DockWidget::hide() + OperationType_ShowViaDockWidgetAPI, ///< Hidding programatically via DockWidget::show() + }; + + ///@brief Describes a testable action. + struct Operation { + typedef QVector List; + OperationType operationType; + // Extra arguments which depend on the type go here: + }; + + + ///@brief Describes a dock widget. + struct DockWidgetDescriptor { + typedef QVector List; + QSize minSize; // the minSize of the hosted widget + QRect geometry; + bool isFloating; + bool isVisible; + }; + + struct MainWindowDescriptor { + typedef QVector List; + QRect geometry; + MainWindowOption mainWindowOption; + }; + + ///@brief Describes our initial window layout, which will be subject to a test. + struct Layout { + typedef QVector List; + MainWindowDescriptor::List mainWindows; + DockWidgetDescriptor::List dockWidgets; + }; + + /// @brief a test is an initial layout and the list of operations to run on it + struct Test { + typedef QVector List; + Layout initialLayout; + Operation::List operations; + }; + + void runTest(const Testing::Test &); +} +} diff --git a/tests/fuzzer.cpp b/tests/fuzzer.cpp new file mode 100644 index 00000000..b4c66f87 --- /dev/null +++ b/tests/fuzzer.cpp @@ -0,0 +1,183 @@ +/* + This file is part of KDDockWidgets. + + Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Author: Sérgio Martins + + 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, see . +*/ + +// We don't care about performance related checks in the tests +// clazy:excludeall=ctor-missing-parent-argument,missing-qobject-macro,range-loop,missing-typeinfo,detaching-member,function-args-by-ref,non-pod-global-static,reserve-candidates,qstring-allocations + +#include "Testing.h" + +#include +#include +#include + +#include + +using namespace KDDockWidgets; +using namespace KDDockWidgets::Testing; + +#define OPERATIONS_PER_TEST 200 + +class Fuzzer : public QObject +{ + Q_OBJECT +public: + + struct FuzzerConfig + { + int numTests; + int numOperationsPerTest; + bool singleMainWindow; + }; + + Fuzzer(FuzzerConfig config, QObject *parent = nullptr) + : QObject(parent) + , m_randomEngine(m_randomDevice()) + , m_fuzzerConfig(config) + { + } + + Testing::Layout generateRandomLayout() + { + // for now we only support 1 main window + Testing::Layout layout; + Testing::MainWindowDescriptor mainWindow; + mainWindow.geometry = randomGeometry(); + mainWindow.mainWindowOption = MainWindowOption_None; // TODO: Maybe test other options + layout.mainWindows << mainWindow; + + + std::uniform_int_distribution<> numDocksDistrib(1, 10); // TODO: Increase + const int numDockWidgets = numDocksDistrib(m_randomEngine); + for (int i = 0; i < numDockWidgets; ++i) { + layout.dockWidgets << generateRandomDockWidget(); + } + + return layout; + } + + Testing::DockWidgetDescriptor generateRandomDockWidget() + { + Testing::DockWidgetDescriptor dwd; + + dwd.isFloating = getRandomBool(35); + dwd.isVisible = getRandomBool(70); + + std::uniform_int_distribution<> minSizeDistriv(150, 600); + + dwd.minSize.setWidth(minSizeDistriv(m_randomEngine)); + dwd.minSize.setHeight(minSizeDistriv(m_randomEngine)); + + QPoint pos = getRandomPos(); + std::uniform_int_distribution<> widthDistrib(dwd.minSize.width() + 50, dwd.minSize.width() + 600); + std::uniform_int_distribution<> heightDistrib(dwd.minSize.height() + 50, dwd.minSize.height() + 600); + dwd.geometry = QRect(pos, QSize(widthDistrib(m_randomEngine), heightDistrib(m_randomEngine))); + + return dwd; + } + + Testing::DockWidgetDescriptor::List generateRandomDockWidgets(int num) + { + Testing::DockWidgetDescriptor::List dockWidgets; + for (int i = 0; i < num; ++i) { + dockWidgets << generateRandomDockWidget(); + } + return dockWidgets; + } + + bool getRandomBool(int truePercentage = 50) + { + std::uniform_int_distribution<> distrib(1, 100); + return distrib(m_randomEngine) < truePercentage; + } + + QPoint getRandomPos() + { + std::uniform_int_distribution<> posDistrib(0, 500); + const int x = posDistrib(m_randomEngine); + const int y = posDistrib(m_randomEngine); + return {x, y}; + } + + Testing::Operation getRandomOperation() + { + Testing::Operation operation; + operation.operationType = OperationType_CloseViaDockWidgetAPI; // TODO + return operation; + } + + Testing::Test generateRandomTest() + { + Testing::Test test; + test.initialLayout = generateRandomLayout(); + + const int numOperationsPerTest = OPERATIONS_PER_TEST; + test.operations.reserve(numOperationsPerTest); + for (int i = 0; i < numOperationsPerTest; ++i) + test.operations << getRandomOperation(); + + return test; + } + Testing::Test::List generateRandomTests(int num) + { + Testing::Test::List tests; + + for (int i = 0; i < num; ++i) { + tests << generateRandomTest(); + } + + return tests; + } + + void fuzz() + { + const Testing::Test::List tests = generateRandomTests(m_fuzzerConfig.numTests); + for (const auto &test : tests) + Testing::runTest(test); + } + + QRect randomGeometry() + { + std::uniform_int_distribution<> posDistrib(0, 500); + std::uniform_int_distribution<> sizeDistrib(700, 1500); + const int width = posDistrib(m_randomEngine); + const int height = sizeDistrib(m_randomEngine); + + QPoint pos = getRandomPos(); + + return QRect(pos, QSize(width, height)); + } + + std::random_device m_randomDevice; + std::mt19937 m_randomEngine; + FuzzerConfig m_fuzzerConfig; +}; + + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + Fuzzer fuzzer({ 1, 10, true }); + QTimer::singleShot(0, &fuzzer, &Fuzzer::fuzz); + + return app.exec(); +} + +#include