From 546d86aae7cd0b9766f3a0ea73f3777334d55814 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Tue, 31 Jan 2017 17:32:45 +0100 Subject: A model stress test to try to crash the result emitter when used with threads. --- common/asyncutils.h | 26 +++++++++++++--------- tests/clientapitest.cpp | 59 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/common/asyncutils.h b/common/asyncutils.h index 2cf010e..6cbcee8 100644 --- a/common/asyncutils.h +++ b/common/asyncutils.h @@ -25,17 +25,23 @@ namespace async { template -KAsync::Job run(const std::function &f) +KAsync::Job run(const std::function &f, bool runAsync = true) { - return KAsync::start([f](KAsync::Future &future) { - auto result = QtConcurrent::run(f); - auto watcher = new QFutureWatcher; - watcher->setFuture(result); - QObject::connect(watcher, &QFutureWatcher::finished, watcher, [&future, watcher]() { - future.setValue(watcher->future().result()); - delete watcher; - future.setFinished(); + if (runAsync) { + return KAsync::start([f](KAsync::Future &future) { + auto result = QtConcurrent::run(f); + auto watcher = new QFutureWatcher; + watcher->setFuture(result); + QObject::connect(watcher, &QFutureWatcher::finished, watcher, [&future, watcher]() { + future.setValue(watcher->future().result()); + delete watcher; + future.setFinished(); + }); }); - }); + } else { + return KAsync::syncStart([f]() { + return f(); + }); + } } } diff --git a/tests/clientapitest.cpp b/tests/clientapitest.cpp index 159789f..55fdcfc 100644 --- a/tests/clientapitest.cpp +++ b/tests/clientapitest.cpp @@ -9,6 +9,7 @@ #include "resultprovider.h" #include "facadefactory.h" #include "test.h" +#include "asyncutils.h" template class TestDummyResourceFacade : public Sink::StoreFacade @@ -53,38 +54,41 @@ public: }; QPair, typename Sink::ResultEmitter::Ptr> load(const Sink::Query &query, const Sink::Log::Context &ctx) Q_DECL_OVERRIDE { - auto resultProvider = new Sink::ResultProvider(); + auto resultProvider = QSharedPointer>::create(); resultProvider->onDone([resultProvider,ctx]() { SinkTraceCtx(ctx) << "Result provider is done"; - delete resultProvider; }); // We have to do it this way, otherwise we're not setting the fetcher right auto emitter = resultProvider->emitter(); resultProvider->setFetcher([query, resultProvider, this, ctx](const typename T::Ptr &parent) { - if (parent) { - SinkTraceCtx(ctx) << "Running the fetcher " << parent->identifier(); - } else { - SinkTraceCtx(ctx) << "Running the fetcher."; - } - SinkTraceCtx(ctx) << "-------------------------."; - for (const auto &res : results) { - SinkTraceCtx(ctx) << "Parent filter " << query.getFilter("parent").value.toByteArray() << res->identifier() << res->getProperty("parent").toByteArray(); - auto parentProperty = res->getProperty("parent").toByteArray(); - if ((!parent && parentProperty.isEmpty()) || (parent && parentProperty == parent->identifier()) || query.parentProperty().isEmpty()) { - SinkTraceCtx(ctx) << "Found a hit" << res->identifier(); - resultProvider->add(res); + async::run([=] { + if (parent) { + SinkTraceCtx(ctx) << "Running the fetcher " << parent->identifier(); + } else { + SinkTraceCtx(ctx) << "Running the fetcher."; } - } - resultProvider->initialResultSetComplete(parent, true); + SinkTraceCtx(ctx) << "-------------------------."; + for (const auto &res : results) { + // SinkTraceCtx(ctx) << "Parent filter " << query.getFilter("parent").value.toByteArray() << res->identifier() << res->getProperty("parent").toByteArray(); + auto parentProperty = res->getProperty("parent").toByteArray(); + if ((!parent && parentProperty.isEmpty()) || (parent && parentProperty == parent->identifier()) || query.parentProperty().isEmpty()) { + // SinkTraceCtx(ctx) << "Found a hit" << res->identifier(); + resultProvider->add(res); + } + } + resultProvider->initialResultSetComplete(parent, true); + return 0; + }, runAsync).exec(); }); auto job = KAsync::syncStart([query, resultProvider]() {}); - mResultProvider = resultProvider; + mResultProvider = resultProvider.data(); return qMakePair(job, emitter); } QList results; Sink::ResultProviderInterface *mResultProvider; + bool runAsync = false; }; @@ -280,6 +284,27 @@ private slots: QVERIFY(!result.errorCode()); QVERIFY(gotValue); } + + void testModelStress() + { + auto facade = TestDummyResourceFacade::registerFacade(); + facade->runAsync = true; + for (int i = 0; i < 100; i++) { + facade->results << QSharedPointer::create("resource", "id" + QByteArray::number(i), 0, QSharedPointer::create()); + } + ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); + + Sink::Query query; + query.resourceFilter("dummyresource.instance1"); + + for (int i = 0; i < 100; i++) { + auto model = Sink::Store::loadModel(query); + model->fetchMore(QModelIndex()); + QTest::qWait(1); + } + QTest::qWait(100); + } + }; QTEST_MAIN(ClientAPITest) -- cgit v1.2.3