Skip to content
Snippets Groups Projects
Commit 96767638 authored by vt4a2h's avatar vt4a2h
Browse files

Use QTest framework for both benchmarks and tests

parent f6b97975
No related branches found
No related tags found
No related merge requests found
......@@ -27,14 +27,10 @@ set(CMAKE_AUTOMOC ON)
include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/../range-v3/include
${CMAKE_SOURCE_DIR}/../src)
find_package(Qt5Core)
find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(INCLUDES "FooBar.h" "../src/qrange.h" ${QRANGE_IMPL_INCLUDE})
if(CMAKE_BUILD_TYPE EQUAL "RELEASE")
set(INCLUDES "${INCLUDES} benchmarks.h")
endif(CMAKE_BUILD_TYPE EQUAL "RELEASE")
add_executable(${PROJECT_NAME} "main.cpp" ${INCLUDES})
add_executable(${PROJECT_NAME} "main.cpp" "FooBar.h" "../src/qrange.h" ${QRANGE_IMPL_INCLUDE})
if(APPLE AND ${CMAKE_VERSION} VERSION_LESS "3.15.0" AND ${CMAKE_CXX_COMPILER_ID} MATCHES "AppleClang")
set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
......@@ -44,4 +40,4 @@ endif()
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(${PROJECT_NAME} Qt5::Core)
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Test)
#ifndef BENCHMARKS_H
#define BENCHMARKS_H
#ifdef NDEBUG
#include <chrono>
#include <iostream>
#include <memory>
#include <stack>
#include "qrange.h"
template <class F, class D>
void measureTime(const std::string &s, F f, D d, std::size_t times)
{
double time = 0;
for (std::size_t t = 0; t < times; ++t) {
auto data = d();
auto start = std::chrono::system_clock::now();
f(data);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
time += elapsed.count();
}
std::cout << s << ": " << (time / times) << " msec" << " (run " << times << " times)" << std::endl;
}
std::shared_ptr<QObject> genTree(int depth = 22, std::size_t nodeChildrenCount = 2)
{
auto root = std::make_shared<QObject>();
std::stack<QObject*> s;
s.push(root.get());
while (depth--) {
std::stack<QObject*> layer;
while (!s.empty()) {
auto e = s.top();
s.pop();
for (std::size_t cc = 0; cc < nodeChildrenCount; ++cc) {
layer.push(new QObject(e));
}
}
s.swap(layer);
}
return root;
}
void setChildrenNamesOld(const std::shared_ptr<QObject> &obj)
{
for (auto &&c : obj->findChildren<QObject*>()) {
c->setObjectName("foo");
}
}
void setChildrenNamesNew(const std::shared_ptr<QObject> &obj)
{
for (auto &&c : obj.get() | qt::find_children(Qt::FindChildrenRecursively)) {
c->setObjectName("foo");
}
}
void setChildrenNamesNewWithCopy(const std::shared_ptr<QObject> &obj)
{
const std::vector<QObject*> d = obj.get() | qt::find_children(Qt::FindChildrenRecursively);
for (auto &&c : d) {
c->setObjectName("foo");
}
}
void runBenchmarks()
{
auto gen = []{ static auto tree = genTree(); return tree; };
measureTime("Old" , &setChildrenNamesOld , gen, 10);
measureTime("New" , &setChildrenNamesNew , gen, 10);
measureTime("New copy", &setChildrenNamesNewWithCopy, gen, 10);
}
#endif // NDEBUG
#endif // BENCHMARKS_H
#include <vector>
#include <range/v3/algorithm/find.hpp>
#include <set>
#include <memory>
#include <tuple>
#include <stack>
#include "FooBar.h"
#ifdef NDEBUG
#include "benchmarks.h"
#include "qrange.h"
#include <QtTest/QtTest>
class TestQRange : public QObject
{
Q_OBJECT
public:
using QObjectVector = std::vector<QObject*>;
using RootPtr = std::shared_ptr<QObject>;
using Runner = std::function<void(const TestQRange::RootPtr &)>;
private Q_SLOTS:
void initTestCase();
void childrenLookup();
void childrenLookup_data();
void benchmarks();
void benchmarks_data();
private:
void generateSimpleTree();
static std::shared_ptr<QObject> generateTree(int depth, std::size_t nodeChildrenCount);
private:
RootPtr m_simpleTreeRoot;
QObjectVector m_simpleTreeChildren;
};
Q_DECLARE_METATYPE(TestQRange::QObjectVector);
Q_DECLARE_METATYPE(TestQRange::RootPtr);
Q_DECLARE_METATYPE(TestQRange::Runner);
void TestQRange::initTestCase() {
generateSimpleTree();
}
void TestQRange::childrenLookup()
{
QFETCH(QObjectVector, foundChildren);
QFETCH(QObjectVector, expectedChildren);
QCOMPARE(foundChildren.size(), expectedChildren.size());
QCOMPARE(ranges::to<std::set>(foundChildren), ranges::to<std::set>(expectedChildren));
}
void TestQRange::childrenLookup_data()
{
QTest::addColumn<QObjectVector>("foundChildren");
QTest::addColumn<QObjectVector>("expectedChildren");
QTest::addRow("All children")
<< QObjectVector(*m_simpleTreeRoot | qt::children)
<< m_simpleTreeChildren;
QTest::addRow("Direct children")
<< QObjectVector(*m_simpleTreeRoot | qt::find_children(Qt::FindDirectChildrenOnly))
<< QObjectVector{m_simpleTreeChildren[0], m_simpleTreeChildren[1]};
QTest::addRow("Children with type specified")
<< QObjectVector(*m_simpleTreeRoot | qt::children | qt::with_type<Foo*>)
<< QObjectVector{m_simpleTreeChildren[2]};
QTest::addRow("Children with name specified")
<< QObjectVector(*m_simpleTreeRoot | qt::children | qt::by_name("foo"))
<< QObjectVector{m_simpleTreeChildren[2]};
QTest::addRow("Children with name that matches regex")
<< QObjectVector(*m_simpleTreeRoot | qt::children | qt::by_re(QRegularExpression("^b")))
<< QObjectVector{m_simpleTreeChildren[3]};
}
void TestQRange::benchmarks()
{
QFETCH(Runner, runner);
QFETCH(int, depth);
QFETCH(int, childrenPerNode);
auto root = generateTree(depth, childrenPerNode);
QBENCHMARK {
runner(root);
};
}
void TestQRange::benchmarks_data()
{
#ifndef NDEBUG
QSKIP("It makes no sense to run any benchmarks in debug mode. Skipping...");
#endif // NDEBUG
#include "qrange.h"
qInfo() << "Run with \"-iterations n\" to adjust iterations count";
QTest::addColumn<Runner>("runner");
QTest::addColumn<int>("depth");
QTest::addColumn<int>("childrenPerNode");
Runner oldF = [](const std::shared_ptr<QObject> &obj){
for (auto &&c : obj->findChildren<QObject*>()) {
c->setObjectName("foo");
}
};
Runner newF = [](const std::shared_ptr<QObject> &obj){
for (auto &&c : obj.get() | qt::find_children(Qt::FindChildrenRecursively)) {
c->setObjectName("foo");
}
};
Runner newCopyF = [](const std::shared_ptr<QObject> &obj) {
const std::vector<QObject*> d = obj.get() | qt::find_children(Qt::FindChildrenRecursively);
for (auto &&c : d) {
c->setObjectName("foo");
}
};
// Change for some experiments...
for (int depth = 22; depth <= 22; ++depth) {
for (int childrenPerNode = 2; childrenPerNode <= 2; ++childrenPerNode) {
int elementsCount = childrenPerNode * (1 - std::pow(childrenPerNode, depth)) / (1 - childrenPerNode);
QTest::addRow("Old (elements: %i)", elementsCount)
<< oldF << depth << childrenPerNode;
QTest::addRow("New (elements: %i)", elementsCount)
<< newF << depth << childrenPerNode;
int main(int argc, char *argv[])
QTest::addRow("New copy (elements: %i)", elementsCount)
<< newCopyF << depth << childrenPerNode;
}
}
}
void TestQRange::generateSimpleTree()
{
QObject root;
m_simpleTreeRoot = std::make_shared<QObject>();
auto l1 = new QObject(&root);
auto l2 = new QObject(&root);
auto l1 = new QObject(m_simpleTreeRoot.get());
m_simpleTreeChildren.push_back(l1);
auto l2 = new QObject(m_simpleTreeRoot.get());
m_simpleTreeChildren.push_back(l2);
auto l3 = new Foo(l1);
l3->setObjectName("foo");
m_simpleTreeChildren.push_back(l3);
auto l4 = new Bar(l2);
l4->setObjectName("baz");
m_simpleTreeChildren.push_back(l4);
}
{
std::vector<QObject*> children = root | qt::children;
assert(children.size() == 4);
}
std::shared_ptr<QObject> TestQRange::generateTree(int depth, std::size_t nodeChildrenCount)
{
auto root = std::make_shared<QObject>();
{
std::vector<QObject*> children = root | qt::find_children(Qt::FindDirectChildrenOnly);
assert(children.size() == 2);
assert(ranges::find(children, l1) != ranges::end(children));
assert(ranges::find(children, l2) != ranges::end(children));
}
std::stack<QObject*> s;
s.push(root.get());
{
std::vector<QObject*> children = root | qt::children | qt::with_type<Foo*>;
assert(children.size() == 1);
assert(children.at(0) == l3);
}
while (depth --> 0) {
std::stack<QObject*> layer;
while (!s.empty()) {
auto e = s.top();
s.pop();
{
std::vector<QObject*> children = root | qt::children | qt::by_name("foo");
assert(children.size() == 1);
assert(children.at(0) == l3);
}
for (std::size_t cc = 0; cc < nodeChildrenCount; ++cc) {
layer.push(new QObject(e));
}
}
{
std::vector<QObject*> children = root | qt::children | qt::by_re(QRegularExpression("^b"));
assert(children.size() == 1);
assert(children.at(0) == l4);
s.swap(layer);
}
#ifdef NDEBUG
runBenchmarks();
#endif // NDEBUG
return 0;
return root;
}
QTEST_MAIN(TestQRange)
#include "main.moc"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment