Merge lp:~stolowski/unity-scopes-shell/model-update-crashfix into lp:unity-scopes-shell

Proposed by Paweł Stołowski
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 296
Merged at revision: 289
Proposed branch: lp:~stolowski/unity-scopes-shell/model-update-crashfix
Merge into: lp:unity-scopes-shell
Diff against target: 718 lines (+580/-14)
7 files modified
src/Unity/resultsmap.cpp (+11/-6)
src/Unity/resultsmap.h (+3/-1)
src/Unity/resultsmodel.cpp (+5/-2)
src/Unity/resultsmodel.h (+2/-2)
src/Unity/scope.cpp (+2/-2)
tests/data/mock-scope-manyresults/mock-scope-manyresults.cpp (+188/-1)
tests/resultstest.cpp (+369/-0)
To merge this branch: bzr merge lp:~stolowski/unity-scopes-shell/model-update-crashfix
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Michi Henning (community) Approve
Review via email: mp+286069@code.launchpad.net

Commit message

Fix crash on duplicated results - de-duplicate results (duplicates are ignored). Add stress tests for results model updates.

Description of the change

Fix crash on duplicated results. Add stress tests for results model updates (the test with "duplicated_results" search query triggers the crash without the fix).

To post a comment you must log in.
Revision history for this message
Michi Henning (michihenning) wrote :

I'm not fond of the constructor that modifies its argument via a non-const reference.

But, seeing that this fixes this for now, I'm good with it.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Top-approving (agreed with Michi).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Unity/resultsmap.cpp'
2--- src/Unity/resultsmap.cpp 2015-10-06 14:35:18 +0000
3+++ src/Unity/resultsmap.cpp 2016-02-16 08:14:59 +0000
4@@ -25,14 +25,19 @@
5 rebuild(results);
6 }
7
8-ResultsMap::ResultsMap(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const &results)
9+ResultsMap::ResultsMap(QList<std::shared_ptr<unity::scopes::CategorisedResult>> &results)
10 {
11 int pos = 0;
12- for (auto result: results) {
13- std::shared_ptr<unity::scopes::Result> res = result;
14- assert(res);
15- const ResultPos rpos { res, pos++ };
16- m_results.insert({result->uri(), rpos });
17+ for (auto it = results.begin(); it != results.end(); ) {
18+ std::shared_ptr<unity::scopes::Result> result = *it;
19+ assert(result);
20+ if (find(result) < 0) {
21+ const ResultPos rpos { result, pos++ };
22+ m_results.insert({result->uri(), rpos });
23+ ++it;
24+ } else {
25+ it = results.erase(it);
26+ }
27 }
28 }
29
30
31=== modified file 'src/Unity/resultsmap.h'
32--- src/Unity/resultsmap.h 2015-10-06 14:35:18 +0000
33+++ src/Unity/resultsmap.h 2016-02-16 08:14:59 +0000
34@@ -33,7 +33,9 @@
35 {
36 public:
37 ResultsMap(QList<std::shared_ptr<unity::scopes::Result>> const &results);
38- ResultsMap(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const &results);
39+
40+ // note: this constructor modifies the input results list (de-duplicates it).
41+ ResultsMap(QList<std::shared_ptr<unity::scopes::CategorisedResult>> &results);
42 int find(std::shared_ptr<unity::scopes::Result> const& result) const;
43
44 void rebuild(QList<std::shared_ptr<unity::scopes::Result>> const &results);
45
46=== modified file 'src/Unity/resultsmodel.cpp'
47--- src/Unity/resultsmodel.cpp 2015-11-30 09:23:32 +0000
48+++ src/Unity/resultsmodel.cpp 2016-02-16 08:14:59 +0000
49@@ -73,7 +73,7 @@
50 m_maxAttributes = count;
51 }
52
53-void ResultsModel::addUpdateResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const& results)
54+void ResultsModel::addUpdateResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& results)
55 {
56 if (results.count() == 0) {
57 return;
58@@ -129,7 +129,7 @@
59 }
60 }
61
62-void ResultsModel::addResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const& results)
63+void ResultsModel::addResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& results)
64 {
65 if (results.count() == 0) {
66 return;
67@@ -137,6 +137,9 @@
68
69 m_purge = false;
70
71+ ResultsMap newResultsMap(results); // deduplicate results
72+ Q_UNUSED(newResultsMap);
73+
74 beginInsertRows(QModelIndex(), m_results.count(), m_results.count() + results.count() - 1);
75 Q_FOREACH(std::shared_ptr<scopes::CategorisedResult> const& result, results) {
76 m_results.append(result);
77
78=== modified file 'src/Unity/resultsmodel.h'
79--- src/Unity/resultsmodel.h 2015-10-06 14:35:18 +0000
80+++ src/Unity/resultsmodel.h 2016-02-16 08:14:59 +0000
81@@ -44,8 +44,8 @@
82 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
83 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
84
85- void addResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const&);
86- void addUpdateResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>> const&);
87+ void addResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>&);
88+ void addUpdateResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>&);
89 void clearResults();
90
91 /* getters */
92
93=== modified file 'src/Unity/scope.cpp'
94--- src/Unity/scope.cpp 2015-11-30 09:23:32 +0000
95+++ src/Unity/scope.cpp 2016-02-16 08:14:59 +0000
96@@ -595,11 +595,11 @@
97 if (category_model == nullptr) {
98 category_model.reset(new ResultsModel(m_categories.data()));
99 category_model->setCategoryId(QString::fromStdString(category->id()));
100- category_model->addResults(m_category_results[category->id()]);
101+ category_model->addResults(m_category_results[category->id()]); // de-duplicates m_category_results
102 m_categories->registerCategory(category, category_model);
103 } else {
104 m_categories->registerCategory(category, QSharedPointer<ResultsModel>());
105- category_model->addUpdateResults(m_category_results[category->id()]);
106+ category_model->addUpdateResults(m_category_results[category->id()]); // de-duplicates m_category_results
107 m_categories->updateResultCount(category_model);
108 }
109 }
110
111=== modified file 'tests/data/mock-scope-manyresults/mock-scope-manyresults.cpp'
112--- tests/data/mock-scope-manyresults/mock-scope-manyresults.cpp 2015-10-12 14:48:41 +0000
113+++ tests/data/mock-scope-manyresults/mock-scope-manyresults.cpp 2016-02-16 08:14:59 +0000
114@@ -21,6 +21,8 @@
115
116 #include <iostream>
117 #include <thread>
118+#include <cstdlib>
119+#include <chrono>
120
121 #define EXPORT __attribute__ ((visibility ("default")))
122
123@@ -53,6 +55,21 @@
124 auto cat1 = reply->register_category("cat1", "Category 1", "", meta_rndr);
125 auto cat2 = reply->register_category("cat2", "Category 2", "", meta_rndr);
126
127+ if (query_ == "")
128+ {
129+ // 2000 items
130+ for (int i = 0; i<200; i++)
131+ {
132+ CategorisedResult res(cat1);
133+ res.set_uri("cat1_uri" + std::to_string(i));
134+ res.set_title("result5 for: \"" + query_ + "\"");
135+ reply->push(res);
136+
137+ if (i % 100 == 0) {
138+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
139+ }
140+ }
141+ }
142 if (query_ == "search1")
143 {
144 // five results with uris 0..4 in a single category cat1
145@@ -114,7 +131,177 @@
146 reply->push(res);
147 }
148 }
149-
150+ else if (query_ == "lots_of_results")
151+ {
152+ // 2000 items
153+ for (int i = 0; i<2000; i++)
154+ {
155+ CategorisedResult res(cat1);
156+ res.set_uri("cat1_uri" + std::to_string(i));
157+ res.set_title("result5 for: \"" + query_ + "\"");
158+ reply->push(res);
159+
160+ if (i % 100 == 0) {
161+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
162+ }
163+ }
164+ }
165+ else if (query_ == "lots_of_results_reversed")
166+ {
167+ // 2000 items, in the reversed order of search5
168+ for (int i = 1999; i>=0; i--)
169+ {
170+ CategorisedResult res(cat1);
171+ res.set_uri("cat1_uri" + std::to_string(i));
172+ res.set_title("result5 for: \"" + query_ + "\"");
173+ reply->push(res);
174+
175+ if (i % 100 == 0) {
176+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
177+ }
178+ }
179+ }
180+ else if (query_ == "lots_of_results_reversed_plus_some")
181+ {
182+ // 2100 items, in the reversed order of search5, plus 100 extra results
183+ for (int i = 2099; i>=0; i--)
184+ {
185+ CategorisedResult res(cat1);
186+ res.set_uri("cat1_uri" + std::to_string(i));
187+ res.set_title("result5 for: \"" + query_ + "\"");
188+ reply->push(res);
189+
190+ if (i % 100 == 0) {
191+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
192+ }
193+ }
194+ }
195+ else if (query_ == "lots_of_results_half_of_them_missing")
196+ {
197+ // 1000 items with uris matching every other item from previous search
198+ for (int i = 0; i<1000; i++)
199+ {
200+ CategorisedResult res(cat1);
201+ res.set_uri("cat1_uri" + std::to_string(i*2));
202+ res.set_title("result5 for: \"" + query_ + "\"");
203+ reply->push(res);
204+
205+ if (i % 100 == 0) {
206+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
207+ }
208+ }
209+ }
210+ else if (query_ == "lots_of_results_2")
211+ {
212+ // 2000 items, all different than in previous searches
213+ for (int i = 0; i<2000; i++)
214+ {
215+ CategorisedResult res(cat1);
216+ res.set_uri("cat1_uri" + std::to_string(5000 + i));
217+ res.set_title("result5 for: \"" + query_ + "\"");
218+ reply->push(res);
219+
220+ if (i % 100 == 0) {
221+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
222+ }
223+ }
224+ }
225+ else if (query_ == "lots_of_results_fast")
226+ {
227+ // 100 items
228+ for (int i = 0; i<10; i++)
229+ {
230+ CategorisedResult res(cat1);
231+ res.set_uri("cat1_uri" + std::to_string(i));
232+ res.set_title("result5 for: \"" + query_ + "\"");
233+ reply->push(res);
234+ }
235+ }
236+ else if (query_ == "lots_of_results_reversed_fast")
237+ {
238+ // 100 items, in the reversed order of search5
239+ for (int i = 9; i>=0; i--)
240+ {
241+ CategorisedResult res(cat1);
242+ res.set_uri("cat1_uri" + std::to_string(i));
243+ res.set_title("result5 for: \"" + query_ + "\"");
244+ reply->push(res);
245+ }
246+ }
247+ else if (query_ == "duplicated_uris1")
248+ {
249+ for (int i = 0; i<10; i++)
250+ {
251+ CategorisedResult res(cat1);
252+ res.set_uri("uri");
253+ res.set_title("result " + std::to_string(i));
254+ reply->push(res);
255+ }
256+ }
257+ else if (query_ == "duplicated_uris2")
258+ {
259+ for (int i = 0; i<10; i++)
260+ {
261+ CategorisedResult res(cat1);
262+ if (i % 2 == 0) { // every other result uses same uri
263+ res.set_uri("uri");
264+ } else {
265+ res.set_uri("uri" + std::to_string(i));
266+ }
267+ res.set_title("result " + std::to_string(i));
268+ reply->push(res);
269+ }
270+ }
271+ else if (query_ == "duplicated_results")
272+ {
273+ for (int i = 0; i<2; i++)
274+ {
275+ CategorisedResult res(cat1);
276+ res.set_uri("uri");
277+ res.set_title("result");
278+ reply->push(res);
279+ }
280+ }
281+ else if (query_ == "two-categories")
282+ {
283+ auto cat3 = reply->register_category("cat3", "Category 3", "", meta_rndr);
284+ for (int i = 0; i<10; i++)
285+ {
286+ CategorisedResult res(cat1);
287+ res.set_uri("uri" + std::to_string(i));
288+ res.set_title("result " + std::to_string(i));
289+ reply->push(res);
290+
291+ CategorisedResult res2(cat3);
292+ res2.set_uri("uri" + std::to_string(i));
293+ res2.set_title("result " + std::to_string(i));
294+ reply->push(res2);
295+ }
296+ }
297+ else if (query_ == "two-categories-second-gone")
298+ {
299+ for (int i = 0; i<10; i++)
300+ {
301+ CategorisedResult res(cat1);
302+ res.set_uri("uri" + std::to_string(i));
303+ res.set_title("result " + std::to_string(i));
304+ reply->push(res);
305+ }
306+ }
307+ else if (query_.find("random") == 0) // "random", followed by an int for the number of results
308+ {
309+ auto rand_start = rand() % 30;
310+ for (int i = 0; i<std::stoi(query_.substr(6)); i++)
311+ {
312+ CategorisedResult res(cat1);
313+ res.set_uri("cat1_uri" + std::to_string(rand_start + i));
314+ res.set_title("result" + std::to_string(i));
315+ reply->push(res);
316+ if (i % 2 == 0) {
317+ std::this_thread::sleep_for(std::chrono::milliseconds(40));
318+ }
319+ }
320+ }
321 }
322
323 private:
324
325=== modified file 'tests/resultstest.cpp'
326--- tests/resultstest.cpp 2015-10-12 15:42:50 +0000
327+++ tests/resultstest.cpp 2016-02-16 08:14:59 +0000
328@@ -22,7 +22,10 @@
329 #include <QTimer>
330 #include <QSignalSpy>
331 #include <QDBusConnection>
332+#include <QDebug>
333
334+#include <chrono>
335+#include <cstdlib>
336 #include <Unity/resultsmodel.h>
337
338 #include <unity/shell/scopes/CategoriesInterface.h>
339@@ -97,6 +100,7 @@
340 void initTestCase()
341 {
342 qputenv("UNITY_SCOPES_NO_WAIT_LOCATION", "1");
343+ qputenv("UNITY_SCOPES_CARDINALITY_OVERRIDE", "9999");
344 m_harness = sh::ScopeHarness::newFromScopeList(
345 shr::CustomRegistry::Parameters({
346 TEST_DATA_DIR "mock-scope/mock-scope.ini",
347@@ -1063,6 +1067,371 @@
348 .match(resultsView->categories())
349 );
350 }
351+
352+ void testResultsModelChangesWithDuplicatedUris()
353+ {
354+ auto resultsView = m_harness->resultsView();
355+ resultsView->setActiveScope("mock-scope-manyresults");
356+
357+ // first search run
358+ {
359+ resultsView->setQuery("duplicated_uris1");
360+ QVERIFY_MATCHRESULT(
361+ shm::CategoryListMatcher()
362+ .hasExactly(1)
363+ .category(shm::CategoryMatcher("cat1")
364+ .hasAtLeast(10)
365+ )
366+ .match(resultsView->categories())
367+ );
368+
369+ auto const results = resultsView->category("cat1").results();
370+ QCOMPARE(static_cast<unsigned long>(results.size()), 10UL);
371+ for (unsigned i = 0; i<results.size(); i++) {
372+ QCOMPARE(
373+ QString::fromStdString(results[i].uri()),
374+ QString::fromStdString("uri")
375+ );
376+ }
377+ }
378+
379+ // second search run
380+ {
381+ resultsView->setQuery("duplicated_uris2");
382+ QVERIFY_MATCHRESULT(
383+ shm::CategoryListMatcher()
384+ .hasExactly(1)
385+ .category(shm::CategoryMatcher("cat1")
386+ .hasAtLeast(10)
387+ )
388+ .match(resultsView->categories())
389+ );
390+
391+ auto const results = resultsView->category("cat1").results();
392+ QCOMPARE(static_cast<unsigned long>(results.size()), 10UL);
393+ for (unsigned i = 0; i<results.size(); i++) {
394+ QCOMPARE(
395+ QString::fromStdString(results[i].uri()),
396+ (i % 2 == 0) ? QString::fromStdString("uri") : QString::fromStdString("uri" + std::to_string(i))
397+ );
398+ }
399+ }
400+ }
401+
402+ void testResultsModelChangesWithDuplicatedResults()
403+ {
404+ auto resultsView = m_harness->resultsView();
405+ resultsView->setActiveScope("mock-scope-manyresults");
406+
407+ {
408+ resultsView->setQuery("duplicated_results");
409+ QVERIFY_MATCHRESULT(
410+ shm::CategoryListMatcher()
411+ .hasExactly(1)
412+ .category(shm::CategoryMatcher("cat1")
413+ .hasAtLeast(1)
414+ )
415+ .match(resultsView->categories())
416+ );
417+
418+ auto const results = resultsView->category("cat1").results();
419+ QCOMPARE(static_cast<unsigned long>(results.size()), 1UL);
420+ }
421+ }
422+
423+ void testResultsMassiveModelChanges()
424+ {
425+ auto resultsView = m_harness->resultsView();
426+ resultsView->setActiveScope("mock-scope-manyresults");
427+
428+ // first search run
429+ {
430+ auto const start = std::chrono::system_clock::now();
431+
432+ resultsView->setQuery("lots_of_results");
433+ QVERIFY_MATCHRESULT(
434+ shm::CategoryListMatcher()
435+ .hasExactly(1)
436+ .category(shm::CategoryMatcher("cat1")
437+ .hasAtLeast(2000)
438+ )
439+ .match(resultsView->categories())
440+ );
441+ auto end = std::chrono::system_clock::now();
442+ auto search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
443+ qDebug() << "Search #1 duration: " << search_dur;
444+
445+ auto const results = resultsView->category("cat1").results();
446+ QCOMPARE(static_cast<unsigned long>(results.size()), 2000UL);
447+ for (unsigned i = 0; i<results.size(); i++) {
448+ QCOMPARE(
449+ QString::fromStdString(results[i].uri()),
450+ QString::fromStdString("cat1_uri" + std::to_string(i))
451+ );
452+ }
453+ }
454+
455+ // second search run, reversed order of results
456+ {
457+ auto const start = std::chrono::system_clock::now();
458+
459+ resultsView->setQuery("lots_of_results_reversed_plus_some");
460+ QVERIFY_MATCHRESULT(
461+ shm::CategoryListMatcher()
462+ .hasExactly(1)
463+ .category(shm::CategoryMatcher("cat1")
464+ .hasAtLeast(2100)
465+ )
466+ .match(resultsView->categories())
467+ );
468+
469+ auto const end = std::chrono::system_clock::now();
470+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
471+ qDebug() << "Search #2 duration: " << search_dur;
472+
473+ auto const results = resultsView->category("cat1").results();
474+ QCOMPARE(static_cast<unsigned long>(results.size()), 2100UL);
475+ for (unsigned i = 0; i<results.size(); i++) {
476+ QCOMPARE(
477+ QString::fromStdString(results[i].uri()),
478+ QString::fromStdString("cat1_uri" + std::to_string(2099-i))
479+ );
480+ }
481+ }
482+
483+ // second search run, reversed order of results
484+ {
485+ auto const start = std::chrono::system_clock::now();
486+
487+ resultsView->setQuery("lots_of_results_reversed");
488+ QVERIFY_MATCHRESULT(
489+ shm::CategoryListMatcher()
490+ .hasExactly(1)
491+ .category(shm::CategoryMatcher("cat1")
492+ .hasAtLeast(2000)
493+ )
494+ .match(resultsView->categories())
495+ );
496+
497+ auto const end = std::chrono::system_clock::now();
498+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
499+ qDebug() << "Search #3 duration: " << search_dur;
500+
501+ auto const results = resultsView->category("cat1").results();
502+ QCOMPARE(static_cast<unsigned long>(results.size()), 2000UL);
503+ for (unsigned i = 0; i<results.size(); i++) {
504+ QCOMPARE(
505+ QString::fromStdString(results[i].uri()),
506+ QString::fromStdString("cat1_uri" + std::to_string(1999-i))
507+ );
508+ }
509+ }
510+
511+ // 1000 results, every other matches previous set
512+ {
513+ auto const start = std::chrono::system_clock::now();
514+
515+ resultsView->setQuery("lots_of_results_half_of_them_missing");
516+ QVERIFY_MATCHRESULT(
517+ shm::CategoryListMatcher()
518+ .hasExactly(1)
519+ .category(shm::CategoryMatcher("cat1")
520+ .hasAtLeast(1000)
521+ )
522+ .match(resultsView->categories())
523+ );
524+
525+ auto const end = std::chrono::system_clock::now();
526+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
527+ qDebug() << "Search #4 duration: " << search_dur;
528+
529+ auto const results = resultsView->category("cat1").results();
530+ QCOMPARE(static_cast<unsigned long>(results.size()), 1000UL);
531+ for (unsigned i = 0; i<results.size(); i++) {
532+ QCOMPARE(
533+ QString::fromStdString(results[i].uri()),
534+ QString::fromStdString("cat1_uri" + std::to_string(i*2))
535+ );
536+ }
537+ }
538+
539+ // third search run, different result set
540+ {
541+ auto const start = std::chrono::system_clock::now();
542+
543+ resultsView->setQuery("lots_of_results_2");
544+ QVERIFY_MATCHRESULT(
545+ shm::CategoryListMatcher()
546+ .hasExactly(1)
547+ .category(shm::CategoryMatcher("cat1")
548+ .hasAtLeast(2000)
549+ )
550+ .match(resultsView->categories())
551+ );
552+
553+ auto const end = std::chrono::system_clock::now();
554+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
555+ qDebug() << "Search #5 duration: " << search_dur;
556+
557+ auto const results = resultsView->category("cat1").results();
558+ QCOMPARE(static_cast<unsigned long>(results.size()), 2000UL);
559+ for (unsigned i = 0; i<results.size(); i++) {
560+ QCOMPARE(
561+ QString::fromStdString(results[i].uri()),
562+ QString::fromStdString("cat1_uri" + std::to_string(5000+i))
563+ );
564+ }
565+ }
566+
567+ // fourth search run, no delays in the scope
568+ {
569+ auto const start = std::chrono::system_clock::now();
570+
571+ resultsView->setQuery("lots_of_results_fast");
572+ QVERIFY_MATCHRESULT(
573+ shm::CategoryListMatcher()
574+ .hasExactly(1)
575+ .category(shm::CategoryMatcher("cat1")
576+ .hasAtLeast(10)
577+ )
578+ .match(resultsView->categories())
579+ );
580+ auto end = std::chrono::system_clock::now();
581+ auto search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
582+ qDebug() << "Search #6 duration: " << search_dur;
583+
584+ auto const results = resultsView->category("cat1").results();
585+ QCOMPARE(static_cast<unsigned long>(results.size()), 10UL);
586+ for (unsigned i = 0; i<results.size(); i++) {
587+ QCOMPARE(
588+ QString::fromStdString(results[i].uri()),
589+ QString::fromStdString("cat1_uri" + std::to_string(i))
590+ );
591+ }
592+ }
593+
594+ // fifth search run, reversed order of results, no delays in the scope
595+ {
596+ auto const start = std::chrono::system_clock::now();
597+
598+ resultsView->setQuery("lots_of_results_reversed_fast");
599+ QVERIFY_MATCHRESULT(
600+ shm::CategoryListMatcher()
601+ .hasExactly(1)
602+ .category(shm::CategoryMatcher("cat1")
603+ .hasAtLeast(10)
604+ )
605+ .match(resultsView->categories())
606+ );
607+
608+ auto const end = std::chrono::system_clock::now();
609+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
610+ qDebug() << "Search #7 duration: " << search_dur;
611+
612+ auto const results = resultsView->category("cat1").results();
613+ QCOMPARE(static_cast<unsigned long>(results.size()), 10UL);
614+ for (unsigned i = 0; i<results.size(); i++) {
615+ QCOMPARE(
616+ QString::fromStdString(results[i].uri()),
617+ QString::fromStdString("cat1_uri" + std::to_string(9-i))
618+ );
619+ }
620+ }
621+ // search with empty string
622+ {
623+ auto const start = std::chrono::system_clock::now();
624+
625+ resultsView->setQuery("");
626+ QVERIFY_MATCHRESULT(
627+ shm::CategoryListMatcher()
628+ .hasExactly(1)
629+ .category(shm::CategoryMatcher("cat1")
630+ .hasAtLeast(200)
631+ )
632+ .match(resultsView->categories())
633+ );
634+
635+ auto const end = std::chrono::system_clock::now();
636+ auto const search_dur = std::chrono::duration_cast<std::chrono::seconds>(end.time_since_epoch()).count() - std::chrono::duration_cast<std::chrono::seconds>(start.time_since_epoch()).count();
637+ qDebug() << "Search #8 duration: " << search_dur;
638+
639+ auto const results = resultsView->category("cat1").results();
640+ QCOMPARE(static_cast<unsigned long>(results.size()), 200UL);
641+ for (unsigned i = 0; i<results.size(); i++) {
642+ QCOMPARE(
643+ QString::fromStdString(results[i].uri()),
644+ QString::fromStdString("cat1_uri" + std::to_string(i))
645+ );
646+ }
647+ }
648+
649+ }
650+
651+ void testResultsModelUpdatesRandomSearches()
652+ {
653+ // the aim of this test is to ensure no crashes; results art random and not verified
654+ auto resultsView = m_harness->resultsView();
655+ resultsView->setActiveScope("mock-scope-manyresults");
656+
657+ for (int i = 0; i<10; i++)
658+ {
659+ const unsigned long n = 1 + rand() % 100; // up to 100 results
660+
661+ resultsView->setQuery("random" + std::to_string(n));
662+ QVERIFY_MATCHRESULT(
663+ shm::CategoryListMatcher()
664+ .hasExactly(1)
665+ .category(shm::CategoryMatcher("cat1")
666+ .hasAtLeast(n)
667+ )
668+ .match(resultsView->categories())
669+ );
670+
671+ auto const results = resultsView->category("cat1").results();
672+ QCOMPARE(static_cast<unsigned long>(results.size()), n);
673+ }
674+ }
675+
676+ void testResultsModelUpdatesTwoCategories()
677+ {
678+ auto resultsView = m_harness->resultsView();
679+ resultsView->setActiveScope("mock-scope-manyresults");
680+
681+ {
682+ resultsView->setQuery("two-categories");
683+ QVERIFY_MATCHRESULT(
684+ shm::CategoryListMatcher()
685+ .hasExactly(2)
686+ .category(shm::CategoryMatcher("cat1")
687+ .hasAtLeast(10)
688+ )
689+ .category(shm::CategoryMatcher("cat3")
690+ .hasAtLeast(10)
691+ )
692+ .match(resultsView->categories())
693+ );
694+
695+ auto const results1 = resultsView->category("cat1").results();
696+ QCOMPARE(static_cast<unsigned long>(results1.size()), 10UL);
697+
698+ auto const results2 = resultsView->category("cat3").results();
699+ QCOMPARE(static_cast<unsigned long>(results2.size()), 10UL);
700+ }
701+ {
702+ resultsView->setQuery("two-categories-second-gone");
703+ QVERIFY_MATCHRESULT(
704+ shm::CategoryListMatcher()
705+ .hasExactly(1)
706+ .category(shm::CategoryMatcher("cat1")
707+ .hasAtLeast(10)
708+ )
709+ .match(resultsView->categories())
710+ );
711+
712+ auto const results1 = resultsView->category("cat1").results();
713+ QCOMPARE(static_cast<unsigned long>(results1.size()), 10UL);
714+ }
715+ }
716 };
717
718 QTEST_GUILESS_MAIN(ResultsTest)

Subscribers

People subscribed via source and target branches

to all changes: