Merge lp:~stolowski/unity-scopes-shell/model-update-crashfix into lp:unity-scopes-shell
- model-update-crashfix
- Merge into trunk
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 | ||||
Related bugs: |
|
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_
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:296
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
Top-approving (agreed with Michi).
Preview Diff
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) |
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.