diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-31 17:32:45 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-31 19:38:57 +0100 |
commit | 546d86aae7cd0b9766f3a0ea73f3777334d55814 (patch) | |
tree | 58667b891038926e732e1b6fd9b50a8433bf465b | |
parent | 871a048580d5a464fb697713a5e0e2c52dee5208 (diff) | |
download | sink-546d86aae7cd0b9766f3a0ea73f3777334d55814.tar.gz sink-546d86aae7cd0b9766f3a0ea73f3777334d55814.zip |
A model stress test to try to crash the result emitter when used with
threads.
-rw-r--r-- | common/asyncutils.h | 26 | ||||
-rw-r--r-- | 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 @@ | |||
25 | 25 | ||
26 | namespace async { | 26 | namespace async { |
27 | template <typename T> | 27 | template <typename T> |
28 | KAsync::Job<T> run(const std::function<T()> &f) | 28 | KAsync::Job<T> run(const std::function<T()> &f, bool runAsync = true) |
29 | { | 29 | { |
30 | return KAsync::start<T>([f](KAsync::Future<T> &future) { | 30 | if (runAsync) { |
31 | auto result = QtConcurrent::run(f); | 31 | return KAsync::start<T>([f](KAsync::Future<T> &future) { |
32 | auto watcher = new QFutureWatcher<T>; | 32 | auto result = QtConcurrent::run(f); |
33 | watcher->setFuture(result); | 33 | auto watcher = new QFutureWatcher<T>; |
34 | QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() { | 34 | watcher->setFuture(result); |
35 | future.setValue(watcher->future().result()); | 35 | QObject::connect(watcher, &QFutureWatcher<T>::finished, watcher, [&future, watcher]() { |
36 | delete watcher; | 36 | future.setValue(watcher->future().result()); |
37 | future.setFinished(); | 37 | delete watcher; |
38 | future.setFinished(); | ||
39 | }); | ||
38 | }); | 40 | }); |
39 | }); | 41 | } else { |
42 | return KAsync::syncStart<T>([f]() { | ||
43 | return f(); | ||
44 | }); | ||
45 | } | ||
40 | } | 46 | } |
41 | } | 47 | } |
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 @@ | |||
9 | #include "resultprovider.h" | 9 | #include "resultprovider.h" |
10 | #include "facadefactory.h" | 10 | #include "facadefactory.h" |
11 | #include "test.h" | 11 | #include "test.h" |
12 | #include "asyncutils.h" | ||
12 | 13 | ||
13 | template <typename T> | 14 | template <typename T> |
14 | class TestDummyResourceFacade : public Sink::StoreFacade<T> | 15 | class TestDummyResourceFacade : public Sink::StoreFacade<T> |
@@ -53,38 +54,41 @@ public: | |||
53 | }; | 54 | }; |
54 | QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename T::Ptr>::Ptr> load(const Sink::Query &query, const Sink::Log::Context &ctx) Q_DECL_OVERRIDE | 55 | QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename T::Ptr>::Ptr> load(const Sink::Query &query, const Sink::Log::Context &ctx) Q_DECL_OVERRIDE |
55 | { | 56 | { |
56 | auto resultProvider = new Sink::ResultProvider<typename T::Ptr>(); | 57 | auto resultProvider = QSharedPointer<Sink::ResultProvider<typename T::Ptr>>::create(); |
57 | resultProvider->onDone([resultProvider,ctx]() { | 58 | resultProvider->onDone([resultProvider,ctx]() { |
58 | SinkTraceCtx(ctx) << "Result provider is done"; | 59 | SinkTraceCtx(ctx) << "Result provider is done"; |
59 | delete resultProvider; | ||
60 | }); | 60 | }); |
61 | // We have to do it this way, otherwise we're not setting the fetcher right | 61 | // We have to do it this way, otherwise we're not setting the fetcher right |
62 | auto emitter = resultProvider->emitter(); | 62 | auto emitter = resultProvider->emitter(); |
63 | 63 | ||
64 | resultProvider->setFetcher([query, resultProvider, this, ctx](const typename T::Ptr &parent) { | 64 | resultProvider->setFetcher([query, resultProvider, this, ctx](const typename T::Ptr &parent) { |
65 | if (parent) { | 65 | async::run<int>([=] { |
66 | SinkTraceCtx(ctx) << "Running the fetcher " << parent->identifier(); | 66 | if (parent) { |
67 | } else { | 67 | SinkTraceCtx(ctx) << "Running the fetcher " << parent->identifier(); |
68 | SinkTraceCtx(ctx) << "Running the fetcher."; | 68 | } else { |
69 | } | 69 | SinkTraceCtx(ctx) << "Running the fetcher."; |
70 | SinkTraceCtx(ctx) << "-------------------------."; | ||
71 | for (const auto &res : results) { | ||
72 | SinkTraceCtx(ctx) << "Parent filter " << query.getFilter("parent").value.toByteArray() << res->identifier() << res->getProperty("parent").toByteArray(); | ||
73 | auto parentProperty = res->getProperty("parent").toByteArray(); | ||
74 | if ((!parent && parentProperty.isEmpty()) || (parent && parentProperty == parent->identifier()) || query.parentProperty().isEmpty()) { | ||
75 | SinkTraceCtx(ctx) << "Found a hit" << res->identifier(); | ||
76 | resultProvider->add(res); | ||
77 | } | 70 | } |
78 | } | 71 | SinkTraceCtx(ctx) << "-------------------------."; |
79 | resultProvider->initialResultSetComplete(parent, true); | 72 | for (const auto &res : results) { |
73 | // SinkTraceCtx(ctx) << "Parent filter " << query.getFilter("parent").value.toByteArray() << res->identifier() << res->getProperty("parent").toByteArray(); | ||
74 | auto parentProperty = res->getProperty("parent").toByteArray(); | ||
75 | if ((!parent && parentProperty.isEmpty()) || (parent && parentProperty == parent->identifier()) || query.parentProperty().isEmpty()) { | ||
76 | // SinkTraceCtx(ctx) << "Found a hit" << res->identifier(); | ||
77 | resultProvider->add(res); | ||
78 | } | ||
79 | } | ||
80 | resultProvider->initialResultSetComplete(parent, true); | ||
81 | return 0; | ||
82 | }, runAsync).exec(); | ||
80 | }); | 83 | }); |
81 | auto job = KAsync::syncStart<void>([query, resultProvider]() {}); | 84 | auto job = KAsync::syncStart<void>([query, resultProvider]() {}); |
82 | mResultProvider = resultProvider; | 85 | mResultProvider = resultProvider.data(); |
83 | return qMakePair(job, emitter); | 86 | return qMakePair(job, emitter); |
84 | } | 87 | } |
85 | 88 | ||
86 | QList<typename T::Ptr> results; | 89 | QList<typename T::Ptr> results; |
87 | Sink::ResultProviderInterface<typename T::Ptr> *mResultProvider; | 90 | Sink::ResultProviderInterface<typename T::Ptr> *mResultProvider; |
91 | bool runAsync = false; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | 94 | ||
@@ -280,6 +284,27 @@ private slots: | |||
280 | QVERIFY(!result.errorCode()); | 284 | QVERIFY(!result.errorCode()); |
281 | QVERIFY(gotValue); | 285 | QVERIFY(gotValue); |
282 | } | 286 | } |
287 | |||
288 | void testModelStress() | ||
289 | { | ||
290 | auto facade = TestDummyResourceFacade<Sink::ApplicationDomain::Folder>::registerFacade(); | ||
291 | facade->runAsync = true; | ||
292 | for (int i = 0; i < 100; i++) { | ||
293 | facade->results << QSharedPointer<Sink::ApplicationDomain::Folder>::create("resource", "id" + QByteArray::number(i), 0, QSharedPointer<Sink::ApplicationDomain::MemoryBufferAdaptor>::create()); | ||
294 | } | ||
295 | ResourceConfig::addResource("dummyresource.instance1", "dummyresource"); | ||
296 | |||
297 | Sink::Query query; | ||
298 | query.resourceFilter("dummyresource.instance1"); | ||
299 | |||
300 | for (int i = 0; i < 100; i++) { | ||
301 | auto model = Sink::Store::loadModel<Sink::ApplicationDomain::Folder>(query); | ||
302 | model->fetchMore(QModelIndex()); | ||
303 | QTest::qWait(1); | ||
304 | } | ||
305 | QTest::qWait(100); | ||
306 | } | ||
307 | |||
283 | }; | 308 | }; |
284 | 309 | ||
285 | QTEST_MAIN(ClientAPITest) | 310 | QTEST_MAIN(ClientAPITest) |