Merge lp:~jamesh/mediascanner2/model-auto-update into lp:mediascanner2
- model-auto-update
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Jussi Pakkanen | ||||||||
Approved revision: | 270 | ||||||||
Merged at revision: | 261 | ||||||||
Proposed branch: | lp:~jamesh/mediascanner2/model-auto-update | ||||||||
Merge into: | lp:mediascanner2 | ||||||||
Diff against target: |
1081 lines (+287/-172) 21 files modified
debian/control (+0/-1) src/daemon/InvalidationSender.cc (+28/-15) src/daemon/InvalidationSender.hh (+7/-2) src/daemon/scannerdaemon.cc (+44/-5) src/qml/Ubuntu/MediaScanner/AlbumsModel.cc (+1/-8) src/qml/Ubuntu/MediaScanner/ArtistsModel.cc (+4/-9) src/qml/Ubuntu/MediaScanner/CMakeLists.txt (+1/-1) src/qml/Ubuntu/MediaScanner/GenresModel.cc (+1/-8) src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.cc (+12/-0) src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.hh (+6/-0) src/qml/Ubuntu/MediaScanner/SongsModel.cc (+1/-8) src/qml/Ubuntu/MediaScanner/SongsSearchModel.cc (+1/-6) src/qml/Ubuntu/MediaScanner/StreamingModel.cc (+49/-10) src/qml/Ubuntu/MediaScanner/StreamingModel.hh (+17/-4) src/qml/Ubuntu/MediaScanner/plugin.qmltypes (+11/-1) test/basic.cc (+0/-2) test/qml/tst_albumsmodel.qml (+26/-27) test/qml/tst_artistsmodel.qml (+22/-20) test/qml/tst_genresmodel.qml (+11/-4) test/qml/tst_songsearchmodel.qml (+14/-6) test/qml/tst_songsmodel.qml (+31/-35) |
||||||||
To merge this branch: | bzr merge lp:~jamesh/mediascanner2/model-auto-update | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Jussi Pakkanen (community) | Approve | ||
Review via email: mp+229903@code.launchpad.net |
Commit message
Automatically update models when the daemon sends the InvalidateResults D-Bus signal. Add a status property to models to let loading progress be tracked. Rename rowCount property to count, keeping the old name around for compatibility.
Description of the change
Automatically update the QML models when the scanner daemon sends out an invalidation signal.
I am reusing the signal sent for the benefit of the dash, and as part of the changes brought the broadcast code in-process so that it could be matched with a well known bus name.
I also added a StreamingModel:
Lastly, I've renamed the "rowCount" property to "count" to match e.g. SortFilterModel from the Ubuntu UI toolkit. The old name has been retained for backward compatibility though.
PS Jenkins bot (ps-jenkins) wrote : | # |
Jussi Pakkanen (jpakkane) wrote : | # |
Looking good. Only nitpick:
ModelStatus status_;
Why the trailing underscore? No other variable in the class has decoration. If it's this:
void StreamingModel:
then change the argument to new_status or something similar.
Regardless of that, this looks fine.
James Henstridge (jamesh) wrote : | # |
It was the StreamingModel:
- 270. By James Henstridge
-
Rename method and member based on Jussi's review.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:270
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Pope πΊπ§π± π¦ (popey) wrote : | # |
Can the music app devs be notified when this hits a silo so we can do some testing?
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2014-07-30 08:38:15 +0000 |
3 | +++ debian/control 2014-08-12 05:42:14 +0000 |
4 | @@ -52,7 +52,6 @@ |
5 | Pre-Depends: ${misc:Pre-Depends}, |
6 | Depends: gstreamer1.0-plugins-base, |
7 | gstreamer1.0-plugins-good, |
8 | -# For dbus-send. |
9 | dbus, |
10 | ${misc:Depends}, |
11 | ${shlibs:Depends}, |
12 | |
13 | === modified file 'src/daemon/InvalidationSender.cc' |
14 | --- src/daemon/InvalidationSender.cc 2014-03-06 02:49:18 +0000 |
15 | +++ src/daemon/InvalidationSender.cc 2014-08-12 05:42:14 +0000 |
16 | @@ -22,13 +22,19 @@ |
17 | #include<cstdlib> |
18 | #include<cstdio> |
19 | #include <glib.h> |
20 | +#include <gio/gio.h> |
21 | |
22 | using namespace std; |
23 | |
24 | // timer delay in seconds |
25 | const unsigned int DELAY = 1; |
26 | |
27 | -InvalidationSender::InvalidationSender() : enabled(true), timeout_id(0) { |
28 | +static const char SCOPES_DBUS_IFACE[] = "com.canonical.unity.scopes"; |
29 | +static const char SCOPES_DBUS_PATH[] = "/com/canonical/unity/scopes"; |
30 | +static const char SCOPES_INVALIDATE_RESULTS[] = "InvalidateResults"; |
31 | + |
32 | +InvalidationSender::InvalidationSender() : |
33 | + bus(nullptr, g_object_unref), timeout_id(0) { |
34 | } |
35 | |
36 | InvalidationSender::~InvalidationSender() { |
37 | @@ -37,8 +43,12 @@ |
38 | } |
39 | } |
40 | |
41 | +void InvalidationSender::setBus(GDBusConnection *bus) { |
42 | + this->bus.reset(static_cast<GDBusConnection*>(g_object_ref(bus))); |
43 | +} |
44 | + |
45 | void InvalidationSender::invalidate() { |
46 | - if (!enabled) { |
47 | + if (!bus) { |
48 | return; |
49 | } |
50 | if (timeout_id != 0) { |
51 | @@ -49,22 +59,25 @@ |
52 | |
53 | int InvalidationSender::callback(void *data) { |
54 | auto invalidator = static_cast<InvalidationSender*>(data); |
55 | + GError *error = nullptr; |
56 | |
57 | - string invocation("dbus-send /com/canonical/unity/scopes "); |
58 | - invocation += "com.canonical.unity.scopes.InvalidateResults string:"; |
59 | - const string m_invoc = invocation + "mediascanner-music"; |
60 | - const string v_invoc = invocation + "mediascanner-video"; |
61 | - if(system(m_invoc.c_str()) != 0) { |
62 | - fprintf(stderr, "Could not invalidate music scope results.\n"); |
63 | + if (!g_dbus_connection_emit_signal( |
64 | + invalidator->bus.get(), nullptr, |
65 | + SCOPES_DBUS_PATH, SCOPES_DBUS_IFACE, SCOPES_INVALIDATE_RESULTS, |
66 | + g_variant_new("(s)", "mediascanner-music"), &error)) { |
67 | + fprintf(stderr, "Could not invalidate music scope results: %s\n", error->message); |
68 | + g_error_free(error); |
69 | + error = nullptr; |
70 | } |
71 | - if(system(v_invoc.c_str()) != 0) { |
72 | - fprintf(stderr, "Could not invalidate video scope results.\n"); |
73 | + if (!g_dbus_connection_emit_signal( |
74 | + invalidator->bus.get(), nullptr, |
75 | + SCOPES_DBUS_PATH, SCOPES_DBUS_IFACE, SCOPES_INVALIDATE_RESULTS, |
76 | + g_variant_new("(s)", "mediascanner-video"), &error)) { |
77 | + fprintf(stderr, "Could not invalidate video scope results: %s\n", error->message); |
78 | + g_error_free(error); |
79 | + error = nullptr; |
80 | } |
81 | |
82 | invalidator->timeout_id = 0; |
83 | - return FALSE; |
84 | -} |
85 | - |
86 | -void InvalidationSender::disable() { |
87 | - enabled = false; |
88 | + return G_SOURCE_REMOVE; |
89 | } |
90 | |
91 | === modified file 'src/daemon/InvalidationSender.hh' |
92 | --- src/daemon/InvalidationSender.hh 2014-03-06 02:49:18 +0000 |
93 | +++ src/daemon/InvalidationSender.hh 2014-08-12 05:42:14 +0000 |
94 | @@ -20,6 +20,10 @@ |
95 | #ifndef INVALIDATIONSENDER_HH |
96 | #define INVALIDATIONSENDER_HH |
97 | |
98 | +#include <memory> |
99 | + |
100 | +typedef struct _GDBusConnection GDBusConnection; |
101 | + |
102 | /** |
103 | * A class that sends a broadcast signal that the state of media |
104 | * files has changed. |
105 | @@ -33,11 +37,12 @@ |
106 | InvalidationSender& operator=(const InvalidationSender &o) = delete; |
107 | |
108 | void invalidate(); |
109 | - void disable(); |
110 | + void setBus(GDBusConnection *bus); |
111 | + |
112 | private: |
113 | static int callback(void *data); |
114 | |
115 | - bool enabled; |
116 | + std::unique_ptr<GDBusConnection, void(*)(void*)> bus; |
117 | unsigned int timeout_id; |
118 | }; |
119 | |
120 | |
121 | === modified file 'src/daemon/scannerdaemon.cc' |
122 | --- src/daemon/scannerdaemon.cc 2014-07-25 09:31:02 +0000 |
123 | +++ src/daemon/scannerdaemon.cc 2014-08-12 05:42:14 +0000 |
124 | @@ -32,6 +32,7 @@ |
125 | |
126 | #include<glib.h> |
127 | #include<glib-unix.h> |
128 | +#include<gio/gio.h> |
129 | #include<gst/gst.h> |
130 | |
131 | #include "../mediascanner/MediaFile.hh" |
132 | @@ -46,6 +47,8 @@ |
133 | |
134 | using namespace mediascanner; |
135 | |
136 | +static const char BUS_NAME[] = "com.canonical.MediaScanner2.Daemon"; |
137 | + |
138 | class ScannerDaemon final { |
139 | public: |
140 | ScannerDaemon(); |
141 | @@ -54,6 +57,7 @@ |
142 | |
143 | private: |
144 | |
145 | + void setupBus(); |
146 | void setupSignals(); |
147 | void setupMountWatcher(); |
148 | void readFiles(MediaStore &store, const string &subdir, const MediaType type); |
149 | @@ -61,12 +65,13 @@ |
150 | void removeDir(const string &dir); |
151 | static gboolean sourceCallback(int, GIOCondition, gpointer data); |
152 | static gboolean signalCallback(gpointer data); |
153 | + static void busNameLostCallback(GDBusConnection *connection, const char *name, gpointer data); |
154 | void processEvents(); |
155 | void addMountedVolumes(); |
156 | |
157 | int mountfd; |
158 | unique_ptr<GSource,void(*)(GSource*)> mount_source; |
159 | - int sigint_id, sigterm_id; |
160 | + unsigned int sigint_id = 0, sigterm_id = 0; |
161 | string mountDir; |
162 | string cachedir; |
163 | unique_ptr<MediaStore> store; |
164 | @@ -74,6 +79,8 @@ |
165 | map<string, unique_ptr<SubtreeWatcher>> subtrees; |
166 | InvalidationSender invalidator; |
167 | unique_ptr<GMainLoop,void(*)(GMainLoop*)> main_loop; |
168 | + unique_ptr<GDBusConnection,void(*)(void*)> session_bus; |
169 | + unsigned int bus_name_id = 0; |
170 | }; |
171 | |
172 | static std::string getCurrentUser() { |
173 | @@ -88,8 +95,10 @@ |
174 | } |
175 | |
176 | ScannerDaemon::ScannerDaemon() : |
177 | - mount_source(nullptr, g_source_unref), sigint_id(0), sigterm_id(0), |
178 | - main_loop(g_main_loop_new(nullptr, FALSE), g_main_loop_unref) { |
179 | + mount_source(nullptr, g_source_unref), |
180 | + main_loop(g_main_loop_new(nullptr, FALSE), g_main_loop_unref), |
181 | + session_bus(nullptr, g_object_unref) { |
182 | + setupBus(); |
183 | mountDir = string("/media/") + getCurrentUser(); |
184 | unique_ptr<MediaStore> tmp(new MediaStore(MS_READ_WRITE, "/media/")); |
185 | store = move(tmp); |
186 | @@ -127,13 +136,43 @@ |
187 | if (mount_source) { |
188 | g_source_destroy(mount_source.get()); |
189 | } |
190 | + if (bus_name_id != 0) { |
191 | + g_bus_unown_name(bus_name_id); |
192 | + } |
193 | close(mountfd); |
194 | } |
195 | |
196 | +void ScannerDaemon::busNameLostCallback(GDBusConnection *, const char *name, |
197 | + gpointer data) { |
198 | + ScannerDaemon *daemon = static_cast<ScannerDaemon*>(data); |
199 | + fprintf(stderr, "Exiting due to loss of control of bus name %s\n", name); |
200 | + daemon->bus_name_id = 0; |
201 | + g_main_loop_quit(daemon->main_loop.get()); |
202 | +} |
203 | + |
204 | +void ScannerDaemon::setupBus() { |
205 | + GError *error = nullptr; |
206 | + session_bus.reset(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error)); |
207 | + if (!session_bus) { |
208 | + string errortxt(error->message); |
209 | + g_error_free(error); |
210 | + string msg = "Failed to connect to session bus: "; |
211 | + msg += errortxt; |
212 | + throw runtime_error(msg); |
213 | + } |
214 | + invalidator.setBus(session_bus.get()); |
215 | + |
216 | + bus_name_id = g_bus_own_name_on_connection( |
217 | + session_bus.get(), BUS_NAME, static_cast<GBusNameOwnerFlags>( |
218 | + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | |
219 | + G_BUS_NAME_OWNER_FLAGS_REPLACE), |
220 | + nullptr, &ScannerDaemon::busNameLostCallback, this, nullptr); |
221 | +} |
222 | + |
223 | gboolean ScannerDaemon::signalCallback(gpointer data) { |
224 | ScannerDaemon *daemon = static_cast<ScannerDaemon*>(data); |
225 | g_main_loop_quit(daemon->main_loop.get()); |
226 | - return TRUE; |
227 | + return G_SOURCE_CONTINUE; |
228 | } |
229 | |
230 | void ScannerDaemon::setupSignals() { |
231 | @@ -201,7 +240,7 @@ |
232 | gboolean ScannerDaemon::sourceCallback(int, GIOCondition, gpointer data) { |
233 | ScannerDaemon *daemon = static_cast<ScannerDaemon*>(data); |
234 | daemon->processEvents(); |
235 | - return TRUE; |
236 | + return G_SOURCE_CONTINUE; |
237 | } |
238 | |
239 | void ScannerDaemon::setupMountWatcher() { |
240 | |
241 | === modified file 'src/qml/Ubuntu/MediaScanner/AlbumsModel.cc' |
242 | --- src/qml/Ubuntu/MediaScanner/AlbumsModel.cc 2014-07-10 06:44:15 +0000 |
243 | +++ src/qml/Ubuntu/MediaScanner/AlbumsModel.cc 2014-08-12 05:42:14 +0000 |
244 | @@ -18,7 +18,6 @@ |
245 | */ |
246 | |
247 | #include "AlbumsModel.hh" |
248 | -#include <exception> |
249 | #include <QDebug> |
250 | |
251 | using namespace mediascanner::qml; |
252 | @@ -102,12 +101,6 @@ |
253 | auto limit_filter = filter; |
254 | limit_filter.setLimit(limit); |
255 | limit_filter.setOffset(offset); |
256 | - std::vector<mediascanner::Album> albums; |
257 | - try { |
258 | - albums = store->listAlbums(limit_filter); |
259 | - } catch (const std::exception &e) { |
260 | - qWarning() << "Failed to retrieve album list:" << e.what(); |
261 | - } |
262 | return std::unique_ptr<StreamingModel::RowData>( |
263 | - new AlbumRowData(std::move(albums))); |
264 | + new AlbumRowData(store->listAlbums(limit_filter))); |
265 | } |
266 | |
267 | === modified file 'src/qml/Ubuntu/MediaScanner/ArtistsModel.cc' |
268 | --- src/qml/Ubuntu/MediaScanner/ArtistsModel.cc 2014-07-10 06:01:58 +0000 |
269 | +++ src/qml/Ubuntu/MediaScanner/ArtistsModel.cc 2014-08-12 05:42:14 +0000 |
270 | @@ -18,7 +18,6 @@ |
271 | */ |
272 | |
273 | #include "ArtistsModel.hh" |
274 | -#include <exception> |
275 | #include <QDebug> |
276 | |
277 | using namespace mediascanner::qml; |
278 | @@ -104,14 +103,10 @@ |
279 | limit_filter.setLimit(limit); |
280 | limit_filter.setOffset(offset); |
281 | std::vector<std::string> artists; |
282 | - try { |
283 | - if (album_artists) { |
284 | - artists = store->listAlbumArtists(limit_filter); |
285 | - } else { |
286 | - artists = store->listArtists(limit_filter); |
287 | - } |
288 | - } catch (const std::exception &e) { |
289 | - qWarning() << "Failed to retrieve artist list:" << e.what(); |
290 | + if (album_artists) { |
291 | + artists = store->listAlbumArtists(limit_filter); |
292 | + } else { |
293 | + artists = store->listArtists(limit_filter); |
294 | } |
295 | return std::unique_ptr<StreamingModel::RowData>( |
296 | new ArtistRowData(std::move(artists))); |
297 | |
298 | === modified file 'src/qml/Ubuntu/MediaScanner/CMakeLists.txt' |
299 | --- src/qml/Ubuntu/MediaScanner/CMakeLists.txt 2014-07-10 03:02:27 +0000 |
300 | +++ src/qml/Ubuntu/MediaScanner/CMakeLists.txt 2014-08-12 05:42:14 +0000 |
301 | @@ -19,7 +19,7 @@ |
302 | ) |
303 | |
304 | set_target_properties(mediascanner-qml PROPERTIES AUTOMOC TRUE) |
305 | -qt5_use_modules(mediascanner-qml Qml Concurrent) |
306 | +qt5_use_modules(mediascanner-qml Qml Concurrent DBus) |
307 | target_link_libraries(mediascanner-qml mediascanner ms-dbus) |
308 | |
309 | install( |
310 | |
311 | === modified file 'src/qml/Ubuntu/MediaScanner/GenresModel.cc' |
312 | --- src/qml/Ubuntu/MediaScanner/GenresModel.cc 2014-07-10 06:01:58 +0000 |
313 | +++ src/qml/Ubuntu/MediaScanner/GenresModel.cc 2014-08-12 05:42:14 +0000 |
314 | @@ -18,7 +18,6 @@ |
315 | */ |
316 | |
317 | #include "GenresModel.hh" |
318 | -#include <exception> |
319 | #include <QDebug> |
320 | |
321 | using namespace mediascanner::qml; |
322 | @@ -70,14 +69,8 @@ |
323 | auto limit_filter = filter; |
324 | limit_filter.setLimit(limit); |
325 | limit_filter.setOffset(offset); |
326 | - std::vector<std::string> genres; |
327 | - try { |
328 | - genres = store->listGenres(limit_filter); |
329 | - } catch (const std::exception &e) { |
330 | - qWarning() << "Failed to retrieve genre list:" << e.what(); |
331 | - } |
332 | return std::unique_ptr<StreamingModel::RowData>( |
333 | - new GenreRowData(std::move(genres))); |
334 | + new GenreRowData(store->listGenres(limit_filter))); |
335 | } |
336 | |
337 | void GenresModel::appendRows(std::unique_ptr<StreamingModel::RowData> &&row_data) { |
338 | |
339 | === modified file 'src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.cc' |
340 | --- src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.cc 2014-06-24 13:43:25 +0000 |
341 | +++ src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.cc 2014-08-12 05:42:14 +0000 |
342 | @@ -21,6 +21,7 @@ |
343 | #include <cstdlib> |
344 | #include <cstring> |
345 | #include <exception> |
346 | +#include <QDBusConnection> |
347 | #include <QDebug> |
348 | #include <QQmlEngine> |
349 | |
350 | @@ -49,6 +50,13 @@ |
351 | } else { |
352 | store.reset(new mediascanner::MediaStore(MS_READ_ONLY)); |
353 | } |
354 | + |
355 | + QDBusConnection::sessionBus().connect( |
356 | + "com.canonical.MediaScanner2.Daemon", |
357 | + "/com/canonical/unity/scopes", |
358 | + "com.canonical.unity.scopes", "InvalidateResults", |
359 | + QStringList{"mediascanner-music"}, "s", |
360 | + this, SLOT(resultsInvalidated())); |
361 | } |
362 | |
363 | QList<QObject*> MediaStoreWrapper::query(const QString &q, MediaType type) { |
364 | @@ -75,3 +83,7 @@ |
365 | QQmlEngine::setObjectOwnership(wrapper, QQmlEngine::JavaScriptOwnership); |
366 | return wrapper; |
367 | } |
368 | + |
369 | +void MediaStoreWrapper::resultsInvalidated() { |
370 | + Q_EMIT updated(); |
371 | +} |
372 | |
373 | === modified file 'src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.hh' |
374 | --- src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.hh 2014-07-10 03:02:27 +0000 |
375 | +++ src/qml/Ubuntu/MediaScanner/MediaStoreWrapper.hh 2014-08-12 05:42:14 +0000 |
376 | @@ -49,6 +49,12 @@ |
377 | Q_INVOKABLE mediascanner::qml::MediaFileWrapper *lookup(const QString &filename); |
378 | |
379 | std::shared_ptr<mediascanner::MediaStoreBase> store; |
380 | + |
381 | +Q_SIGNALS: |
382 | + void updated(); |
383 | + |
384 | +private Q_SLOTS: |
385 | + void resultsInvalidated(); |
386 | }; |
387 | |
388 | } |
389 | |
390 | === modified file 'src/qml/Ubuntu/MediaScanner/SongsModel.cc' |
391 | --- src/qml/Ubuntu/MediaScanner/SongsModel.cc 2014-07-10 06:44:15 +0000 |
392 | +++ src/qml/Ubuntu/MediaScanner/SongsModel.cc 2014-08-12 05:42:14 +0000 |
393 | @@ -18,7 +18,6 @@ |
394 | */ |
395 | |
396 | #include "SongsModel.hh" |
397 | -#include <exception> |
398 | #include <QDebug> |
399 | |
400 | using namespace mediascanner::qml; |
401 | @@ -123,12 +122,6 @@ |
402 | auto limit_filter = filter; |
403 | limit_filter.setLimit(limit); |
404 | limit_filter.setOffset(offset); |
405 | - std::vector<mediascanner::MediaFile> songs; |
406 | - try { |
407 | - songs = store->listSongs(limit_filter); |
408 | - } catch (const std::exception &e) { |
409 | - qWarning() << "Failed to retrieve song list:" << e.what(); |
410 | - } |
411 | return std::unique_ptr<StreamingModel::RowData>( |
412 | - new MediaFileRowData(std::move(songs))); |
413 | + new MediaFileRowData(store->listSongs(limit_filter))); |
414 | } |
415 | |
416 | === modified file 'src/qml/Ubuntu/MediaScanner/SongsSearchModel.cc' |
417 | --- src/qml/Ubuntu/MediaScanner/SongsSearchModel.cc 2014-07-10 06:44:15 +0000 |
418 | +++ src/qml/Ubuntu/MediaScanner/SongsSearchModel.cc 2014-08-12 05:42:14 +0000 |
419 | @@ -18,7 +18,6 @@ |
420 | */ |
421 | |
422 | #include "SongsSearchModel.hh" |
423 | -#include <exception> |
424 | #include <QDebug> |
425 | |
426 | using namespace mediascanner::qml; |
427 | @@ -42,11 +41,7 @@ |
428 | std::vector<mediascanner::MediaFile> songs; |
429 | // No batching support, so only send results for the zero offset. |
430 | if (offset == 0) { |
431 | - try { |
432 | - songs = store->query(query.toStdString(), mediascanner::AudioMedia); |
433 | - } catch (const std::exception &e) { |
434 | - qWarning() << "Failed to retrieve song search results:" << e.what(); |
435 | - } |
436 | + songs = store->query(query.toStdString(), mediascanner::AudioMedia); |
437 | } |
438 | return std::unique_ptr<StreamingModel::RowData>( |
439 | new MediaFileRowData(std::move(songs))); |
440 | |
441 | === modified file 'src/qml/Ubuntu/MediaScanner/StreamingModel.cc' |
442 | --- src/qml/Ubuntu/MediaScanner/StreamingModel.cc 2014-08-05 10:47:07 +0000 |
443 | +++ src/qml/Ubuntu/MediaScanner/StreamingModel.cc 2014-08-12 05:42:14 +0000 |
444 | @@ -20,6 +20,7 @@ |
445 | #include "StreamingModel.hh" |
446 | |
447 | #include <cassert> |
448 | +#include <exception> |
449 | |
450 | #include <QEvent> |
451 | #include <QCoreApplication> |
452 | @@ -35,14 +36,22 @@ |
453 | class AdditionEvent : public QEvent { |
454 | private: |
455 | std::unique_ptr<StreamingModel::RowData> rows; |
456 | + bool error = false; |
457 | int generation; |
458 | |
459 | public: |
460 | - AdditionEvent(std::unique_ptr<StreamingModel::RowData> &&rows, int generation) : |
461 | - QEvent(AdditionEvent::additionEventType()), rows(std::move(rows)), generation(generation) { |
462 | + AdditionEvent(int generation) : |
463 | + QEvent(AdditionEvent::additionEventType()), generation(generation) { |
464 | } |
465 | |
466 | + void setRows(std::unique_ptr<StreamingModel::RowData> &&r) { |
467 | + rows = std::move(r); |
468 | + } |
469 | + void setError(bool e) { |
470 | + error = e; |
471 | + } |
472 | std::unique_ptr<StreamingModel::RowData>& getRows() { return rows; } |
473 | + bool getError() const { return error; } |
474 | int getGeneration() const { return generation; } |
475 | |
476 | static QEvent::Type additionEventType() |
477 | @@ -62,8 +71,14 @@ |
478 | if(model->shouldWorkerStop()) { |
479 | return; |
480 | } |
481 | - QScopedPointer<AdditionEvent> e( |
482 | - new AdditionEvent(model->retrieveRows(store, BATCH_SIZE, offset), generation)); |
483 | + QScopedPointer<AdditionEvent> e(new AdditionEvent(generation)); |
484 | + try { |
485 | + e->setRows(model->retrieveRows(store, BATCH_SIZE, offset)); |
486 | + } catch (const std::exception &exc) { |
487 | + qWarning() << "Failed to retrieve rows:" << exc.what(); |
488 | + e->setError(true); |
489 | + return; |
490 | + } |
491 | cursize = e->getRows()->size(); |
492 | if (model->shouldWorkerStop()) { |
493 | return; |
494 | @@ -75,7 +90,8 @@ |
495 | |
496 | } |
497 | |
498 | -StreamingModel::StreamingModel(QObject *parent) : QAbstractListModel(parent), generation(0) { |
499 | +StreamingModel::StreamingModel(QObject *parent) : |
500 | + QAbstractListModel(parent), generation(0), status(Ready) { |
501 | } |
502 | |
503 | StreamingModel::~StreamingModel() { |
504 | @@ -90,9 +106,10 @@ |
505 | void StreamingModel::updateModel() { |
506 | if (store.isNull()) { |
507 | query_future = QFuture<void>(); |
508 | - Q_EMIT filled(); |
509 | + setStatus(Ready); |
510 | return; |
511 | } |
512 | + setStatus(Loading); |
513 | setWorkerStop(false); |
514 | query_future = QtConcurrent::run(runQuery, ++generation, this, store->store); |
515 | } |
516 | @@ -114,35 +131,57 @@ |
517 | return true; |
518 | } |
519 | |
520 | + if (ae->getError()) { |
521 | + setStatus(Error); |
522 | + return true; |
523 | + } |
524 | + |
525 | auto &newrows = ae->getRows(); |
526 | bool lastBatch = newrows->size() < BATCH_SIZE; |
527 | beginInsertRows(QModelIndex(), rowCount(), rowCount()+newrows->size()-1); |
528 | appendRows(std::move(newrows)); |
529 | endInsertRows(); |
530 | - Q_EMIT rowCountChanged(); |
531 | + Q_EMIT countChanged(); |
532 | if (lastBatch) { |
533 | - Q_EMIT filled(); |
534 | + setStatus(Ready); |
535 | } |
536 | return true; |
537 | } |
538 | |
539 | -MediaStoreWrapper *StreamingModel::getStore() { |
540 | +MediaStoreWrapper *StreamingModel::getStore() const { |
541 | return store.data(); |
542 | } |
543 | |
544 | void StreamingModel::setStore(MediaStoreWrapper *store) { |
545 | if (this->store != store) { |
546 | + if (this->store) { |
547 | + disconnect(this->store, &MediaStoreWrapper::updated, |
548 | + this, &StreamingModel::invalidate); |
549 | + } |
550 | this->store = store; |
551 | + if (store) { |
552 | + connect(this->store, &MediaStoreWrapper::updated, |
553 | + this, &StreamingModel::invalidate); |
554 | + } |
555 | invalidate(); |
556 | } |
557 | } |
558 | |
559 | +StreamingModel::ModelStatus StreamingModel::getStatus() const { |
560 | + return status; |
561 | +} |
562 | + |
563 | +void StreamingModel::setStatus(StreamingModel::ModelStatus status) { |
564 | + this->status = status; |
565 | + Q_EMIT statusChanged(); |
566 | +} |
567 | + |
568 | void StreamingModel::invalidate() { |
569 | setWorkerStop(true); |
570 | query_future.waitForFinished(); |
571 | beginResetModel(); |
572 | clearBacking(); |
573 | endResetModel(); |
574 | - Q_EMIT rowCountChanged(); |
575 | + Q_EMIT countChanged(); |
576 | updateModel(); |
577 | } |
578 | |
579 | === modified file 'src/qml/Ubuntu/MediaScanner/StreamingModel.hh' |
580 | --- src/qml/Ubuntu/MediaScanner/StreamingModel.hh 2014-07-17 10:17:23 +0000 |
581 | +++ src/qml/Ubuntu/MediaScanner/StreamingModel.hh 2014-08-12 05:42:14 +0000 |
582 | @@ -34,9 +34,18 @@ |
583 | |
584 | class StreamingModel : public QAbstractListModel { |
585 | Q_OBJECT |
586 | + Q_ENUMS(ModelStatus) |
587 | Q_PROPERTY(mediascanner::qml::MediaStoreWrapper* store READ getStore WRITE setStore) |
588 | - Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) |
589 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
590 | + Q_PROPERTY(int rowCount READ rowCount NOTIFY countChanged) |
591 | + Q_PROPERTY(ModelStatus status READ getStatus NOTIFY statusChanged) |
592 | public: |
593 | + enum ModelStatus { |
594 | + Ready, |
595 | + Loading, |
596 | + Error |
597 | + }; |
598 | + |
599 | explicit StreamingModel(QObject *parent=nullptr); |
600 | virtual ~StreamingModel(); |
601 | |
602 | @@ -56,9 +65,12 @@ |
603 | virtual void clearBacking() = 0; |
604 | |
605 | protected: |
606 | - MediaStoreWrapper *getStore(); |
607 | + MediaStoreWrapper *getStore() const; |
608 | void setStore(MediaStoreWrapper *store); |
609 | |
610 | + ModelStatus getStatus() const; |
611 | + void setStatus(ModelStatus status); |
612 | + |
613 | private: |
614 | void updateModel(); |
615 | void setWorkerStop(bool new_stop_status) noexcept { stopflag.store(new_stop_status, std::memory_order_release); } |
616 | @@ -67,10 +79,11 @@ |
617 | QFuture<void> query_future; |
618 | int generation; |
619 | std::atomic<bool> stopflag; |
620 | + ModelStatus status; |
621 | |
622 | Q_SIGNALS: |
623 | - void rowCountChanged(); |
624 | - void filled(); |
625 | + void countChanged(); |
626 | + void statusChanged(); |
627 | |
628 | public Q_SLOTS: |
629 | |
630 | |
631 | === modified file 'src/qml/Ubuntu/MediaScanner/plugin.qmltypes' |
632 | --- src/qml/Ubuntu/MediaScanner/plugin.qmltypes 2014-07-10 06:46:09 +0000 |
633 | +++ src/qml/Ubuntu/MediaScanner/plugin.qmltypes 2014-08-12 05:42:14 +0000 |
634 | @@ -124,6 +124,7 @@ |
635 | "AllMedia": 255 |
636 | } |
637 | } |
638 | + Signal { name: "updated" } |
639 | Method { |
640 | name: "query" |
641 | type: "QList<QObject*>" |
642 | @@ -157,9 +158,18 @@ |
643 | Component { |
644 | name: "mediascanner::qml::StreamingModel" |
645 | prototype: "QAbstractListModel" |
646 | + Enum { |
647 | + name: "ModelStatus" |
648 | + values: { |
649 | + "Ready": 0, |
650 | + "Loading": 1, |
651 | + "Error": 2 |
652 | + } |
653 | + } |
654 | Property { name: "store"; type: "mediascanner::qml::MediaStoreWrapper"; isPointer: true } |
655 | + Property { name: "count"; type: "int"; isReadonly: true } |
656 | Property { name: "rowCount"; type: "int"; isReadonly: true } |
657 | - Signal { name: "filled" } |
658 | + Property { name: "status"; type: "ModelStatus"; isReadonly: true } |
659 | Method { name: "invalidate" } |
660 | Method { |
661 | name: "get" |
662 | |
663 | === modified file 'test/basic.cc' |
664 | --- test/basic.cc 2014-05-05 09:19:17 +0000 |
665 | +++ test/basic.cc 2014-08-12 05:42:14 +0000 |
666 | @@ -99,7 +99,6 @@ |
667 | MediaStore store(":memory:", MS_READ_WRITE); |
668 | MetadataExtractor extractor; |
669 | InvalidationSender invalidator; |
670 | - invalidator.disable(); |
671 | SubtreeWatcher watcher(store, extractor, invalidator); |
672 | watcher.addDir(subdir); |
673 | ASSERT_EQ(store.size(), 0); |
674 | @@ -122,7 +121,6 @@ |
675 | MediaStore store(":memory:", MS_READ_WRITE); |
676 | MetadataExtractor extractor; |
677 | InvalidationSender invalidator; |
678 | - invalidator.disable(); |
679 | SubtreeWatcher watcher(store, extractor, invalidator); |
680 | ASSERT_EQ(watcher.directoryCount(), 0); |
681 | watcher.addDir(testdir); |
682 | |
683 | === modified file 'test/qml/tst_albumsmodel.qml' |
684 | --- test/qml/tst_albumsmodel.qml 2014-07-10 06:44:15 +0000 |
685 | +++ test/qml/tst_albumsmodel.qml 2014-08-12 05:42:14 +0000 |
686 | @@ -15,27 +15,26 @@ |
687 | } |
688 | |
689 | SignalSpy { |
690 | - id: modelFilled |
691 | + id: modelStatus |
692 | target: model |
693 | - signalName: "filled" |
694 | + signalName: "statusChanged" |
695 | } |
696 | |
697 | TestCase { |
698 | name: "AlbumsModelTests" |
699 | |
700 | + function waitForReady() { |
701 | + while (model.status == AlbumsModel.Loading) { |
702 | + modelStatus.wait(); |
703 | + } |
704 | + compare(model.status, AlbumsModel.Ready); |
705 | + } |
706 | + |
707 | function cleanup() { |
708 | - if (model.artist != undefined) { |
709 | - model.artist = undefined; |
710 | - modelFilled.wait(); |
711 | - } |
712 | - if (model.albumArtist != undefined) { |
713 | - model.albumArtist = undefined; |
714 | - modelFilled.wait(); |
715 | - } |
716 | - if (model.genre != undefined) { |
717 | - model.genre = undefined; |
718 | - modelFilled.wait(); |
719 | - } |
720 | + model.artist = undefined; |
721 | + model.albumArtist = undefined; |
722 | + model.genre = undefined; |
723 | + waitForReady(); |
724 | } |
725 | |
726 | function test_initial_state() { |
727 | @@ -43,7 +42,7 @@ |
728 | compare(model.albumArtist, undefined); |
729 | compare(model.genre, undefined); |
730 | |
731 | - compare(model.rowCount, 4); |
732 | + compare(model.count, 4); |
733 | compare(model.get(0, AlbumsModel.RoleTitle), "Ivy and the Big Apples"); |
734 | compare(model.get(0, AlbumsModel.RoleArtist), "Spiderbait"); |
735 | |
736 | @@ -67,40 +66,40 @@ |
737 | |
738 | function test_artist() { |
739 | model.artist = "The John Butler Trio"; |
740 | - modelFilled.wait(); |
741 | - compare(model.rowCount, 2); |
742 | + waitForReady(); |
743 | + compare(model.count, 2); |
744 | |
745 | compare(model.get(0, AlbumsModel.RoleTitle), "April Uprising"); |
746 | compare(model.get(0, AlbumsModel.RoleArtist), "The John Butler Trio"); |
747 | |
748 | model.artist = "unknown"; |
749 | - modelFilled.wait(); |
750 | - compare(model.rowCount, 0); |
751 | + waitForReady(); |
752 | + compare(model.count, 0); |
753 | } |
754 | |
755 | function test_album_artist() { |
756 | model.albumArtist = "The John Butler Trio"; |
757 | - modelFilled.wait(); |
758 | - compare(model.rowCount, 2); |
759 | + waitForReady(); |
760 | + compare(model.count, 2); |
761 | |
762 | compare(model.get(0, AlbumsModel.RoleTitle), "April Uprising"); |
763 | compare(model.get(0, AlbumsModel.RoleArtist), "The John Butler Trio"); |
764 | |
765 | model.albumArtist = "unknown"; |
766 | - modelFilled.wait(); |
767 | - compare(model.rowCount, 0); |
768 | + waitForReady(); |
769 | + compare(model.count, 0); |
770 | } |
771 | |
772 | function test_genre() { |
773 | model.genre = "rock"; |
774 | - modelFilled.wait(); |
775 | - compare(model.rowCount, 2); |
776 | + waitForReady(); |
777 | + compare(model.count, 2); |
778 | compare(model.get(0, AlbumsModel.RoleTitle), "Ivy and the Big Apples"); |
779 | compare(model.get(1, AlbumsModel.RoleTitle), "Spiderbait"); |
780 | |
781 | model.genre = "unknown"; |
782 | - modelFilled.wait(); |
783 | - compare(model.rowCount, 0); |
784 | + waitForReady(); |
785 | + compare(model.count, 0); |
786 | } |
787 | |
788 | } |
789 | |
790 | === modified file 'test/qml/tst_artistsmodel.qml' |
791 | --- test/qml/tst_artistsmodel.qml 2014-07-10 06:01:58 +0000 |
792 | +++ test/qml/tst_artistsmodel.qml 2014-08-12 05:42:14 +0000 |
793 | @@ -15,31 +15,33 @@ |
794 | } |
795 | |
796 | SignalSpy { |
797 | - id: modelFilled |
798 | + id: modelStatus |
799 | target: model |
800 | - signalName: "filled" |
801 | + signalName: "statusChanged" |
802 | } |
803 | |
804 | TestCase { |
805 | name: "ArtistsModelTests" |
806 | |
807 | + function waitForReady() { |
808 | + while (model.status == ArtistsModel.Loading) { |
809 | + modelStatus.wait(); |
810 | + } |
811 | + compare(model.status, ArtistsModel.Ready); |
812 | + } |
813 | + |
814 | function cleanup() { |
815 | - if (model.albumArtists != false) { |
816 | - model.albumArtists = false; |
817 | - modelFilled.wait(); |
818 | - } |
819 | - if (model.genre != undefined) { |
820 | - model.genre = undefined; |
821 | - modelFilled.wait(); |
822 | - } |
823 | + model.albumArtists = false; |
824 | + model.genre = undefined; |
825 | + waitForReady(); |
826 | } |
827 | |
828 | function test_initial_state() { |
829 | compare(model.albumArtists, false); |
830 | compare(model.genre, undefined); |
831 | |
832 | - //modelFilled.wait(); |
833 | - compare(model.rowCount, 2); |
834 | + waitForReady(); |
835 | + compare(model.count, 2); |
836 | compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
837 | compare(model.get(1, ArtistsModel.RoleArtist), "The John Butler Trio"); |
838 | } |
839 | @@ -54,8 +56,8 @@ |
840 | |
841 | function test_album_artists() { |
842 | model.albumArtists = true; |
843 | - modelFilled.wait(); |
844 | - compare(model.rowCount, 2); |
845 | + waitForReady(); |
846 | + compare(model.count, 2); |
847 | |
848 | compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
849 | compare(model.get(1, ArtistsModel.RoleArtist), "The John Butler Trio"); |
850 | @@ -63,18 +65,18 @@ |
851 | |
852 | function test_genre() { |
853 | model.genre = "rock"; |
854 | - modelFilled.wait(); |
855 | - compare(model.rowCount, 1); |
856 | + waitForReady(); |
857 | + compare(model.count, 1); |
858 | compare(model.get(0, ArtistsModel.RoleArtist), "Spiderbait"); |
859 | |
860 | model.genre = "roots"; |
861 | - modelFilled.wait(); |
862 | - compare(model.rowCount, 1); |
863 | + waitForReady(); |
864 | + compare(model.count, 1); |
865 | compare(model.get(0, ArtistsModel.RoleArtist), "The John Butler Trio"); |
866 | |
867 | model.genre = "unknown"; |
868 | - modelFilled.wait(); |
869 | - compare(model.rowCount, 0); |
870 | + waitForReady(); |
871 | + compare(model.count, 0); |
872 | } |
873 | |
874 | } |
875 | |
876 | === modified file 'test/qml/tst_genresmodel.qml' |
877 | --- test/qml/tst_genresmodel.qml 2014-07-10 06:01:58 +0000 |
878 | +++ test/qml/tst_genresmodel.qml 2014-08-12 05:42:14 +0000 |
879 | @@ -15,20 +15,27 @@ |
880 | } |
881 | |
882 | SignalSpy { |
883 | - id: modelFilled |
884 | + id: modelStatus |
885 | target: model |
886 | - signalName: "filled" |
887 | + signalName: "statusChanged" |
888 | } |
889 | |
890 | TestCase { |
891 | name: "GenresModelTests" |
892 | |
893 | + function waitForReady() { |
894 | + while (model.status == GenresModel.Loading) { |
895 | + modelStatus.wait(); |
896 | + } |
897 | + compare(model.status, GenresModel.Ready); |
898 | + } |
899 | + |
900 | function cleanup() { |
901 | } |
902 | |
903 | function test_initial_state() { |
904 | - modelFilled.wait(); |
905 | - compare(model.rowCount, 2); |
906 | + waitForReady(); |
907 | + compare(model.count, 2); |
908 | compare(model.get(0, ArtistsModel.RoleGenre), "rock"); |
909 | compare(model.get(1, ArtistsModel.RoleGenre), "roots"); |
910 | } |
911 | |
912 | === modified file 'test/qml/tst_songsearchmodel.qml' |
913 | --- test/qml/tst_songsearchmodel.qml 2014-07-10 07:19:57 +0000 |
914 | +++ test/qml/tst_songsearchmodel.qml 2014-08-12 05:42:14 +0000 |
915 | @@ -15,21 +15,29 @@ |
916 | } |
917 | |
918 | SignalSpy { |
919 | - id: modelFilled |
920 | + id: modelStatus |
921 | target: model |
922 | - signalName: "filled" |
923 | + signalName: "statusChanged" |
924 | } |
925 | |
926 | TestCase { |
927 | name: "SongsSearchModelTests" |
928 | + |
929 | + function waitForReady() { |
930 | + while (model.status == SongsSearchModel.Loading) { |
931 | + modelStatus.wait(); |
932 | + } |
933 | + compare(model.status, SongsSearchModel.Ready); |
934 | + } |
935 | + |
936 | function test_search() { |
937 | // By default, the model lists all songs. |
938 | - modelFilled.wait(); |
939 | - compare(model.rowCount, 7, "songs_model.rowCount == 7"); |
940 | + waitForReady(); |
941 | + compare(model.count, 7, "songs_model.count == 7"); |
942 | |
943 | model.query = "revolution"; |
944 | - modelFilled.wait(); |
945 | - compare(model.rowCount, 1, "songs_model.rowCount == 1"); |
946 | + waitForReady(); |
947 | + compare(model.count, 1, "songs_model.count == 1"); |
948 | compare(model.get(0, SongsSearchModel.RoleTitle), "Revolution"); |
949 | } |
950 | } |
951 | |
952 | === modified file 'test/qml/tst_songsmodel.qml' |
953 | --- test/qml/tst_songsmodel.qml 2014-07-10 06:44:15 +0000 |
954 | +++ test/qml/tst_songsmodel.qml 2014-08-12 05:42:14 +0000 |
955 | @@ -15,31 +15,27 @@ |
956 | } |
957 | |
958 | SignalSpy { |
959 | - id: modelFilled |
960 | + id: modelStatus |
961 | target: model |
962 | - signalName: "filled" |
963 | + signalName: "statusChanged" |
964 | } |
965 | |
966 | TestCase { |
967 | name: "SongsModelTests" |
968 | |
969 | + function waitForReady() { |
970 | + while (model.status == SongsModel.Loading) { |
971 | + modelStatus.wait(); |
972 | + } |
973 | + compare(model.status, SongsModel.Ready); |
974 | + } |
975 | + |
976 | function cleanup() { |
977 | - if (model.artist != undefined) { |
978 | - model.artist = undefined; |
979 | - modelFilled.wait(); |
980 | - } |
981 | - if (model.albumArtist != undefined) { |
982 | - model.albumArtist = undefined; |
983 | - modelFilled.wait(); |
984 | - } |
985 | - if (model.album != undefined) { |
986 | - model.album = undefined; |
987 | - modelFilled.wait(); |
988 | - } |
989 | - if (model.genre != undefined) { |
990 | - model.genre = undefined; |
991 | - modelFilled.wait(); |
992 | - } |
993 | + model.artist = undefined; |
994 | + model.albumArtist = undefined; |
995 | + model.album = undefined; |
996 | + model.genre = undefined; |
997 | + waitForReady(); |
998 | } |
999 | |
1000 | function test_initial_state() { |
1001 | @@ -47,7 +43,7 @@ |
1002 | compare(model.albumArtist, undefined); |
1003 | compare(model.album, undefined); |
1004 | |
1005 | - compare(model.rowCount, 7); |
1006 | + compare(model.count, 7); |
1007 | compare(model.get(0, SongsModel.RoleTitle), "Buy Me a Pony") |
1008 | compare(model.get(0, SongsModel.RoleAlbum), "Ivy and the Big Apples"); |
1009 | compare(model.get(0, SongsModel.RoleAuthor), "Spiderbait"); |
1010 | @@ -70,55 +66,55 @@ |
1011 | |
1012 | function test_artist() { |
1013 | model.artist = "The John Butler Trio"; |
1014 | - modelFilled.wait(); |
1015 | - compare(model.rowCount, 4); |
1016 | + waitForReady(); |
1017 | + compare(model.count, 4); |
1018 | |
1019 | compare(model.get(0, SongsModel.RoleTitle), "Revolution"); |
1020 | compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1021 | |
1022 | model.artist = "unknown"; |
1023 | - modelFilled.wait(); |
1024 | - compare(model.rowCount, 0); |
1025 | + waitForReady(); |
1026 | + compare(model.count, 0); |
1027 | } |
1028 | |
1029 | function test_album_artist() { |
1030 | model.albumArtist = "The John Butler Trio"; |
1031 | - modelFilled.wait(); |
1032 | - compare(model.rowCount, 4); |
1033 | + waitForReady(); |
1034 | + compare(model.count, 4); |
1035 | |
1036 | compare(model.get(0, SongsModel.RoleTitle), "Revolution"); |
1037 | compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1038 | |
1039 | model.albumArtist = "unknown"; |
1040 | - modelFilled.wait(); |
1041 | - compare(model.rowCount, 0); |
1042 | + waitForReady(); |
1043 | + compare(model.count, 0); |
1044 | } |
1045 | |
1046 | function test_album() { |
1047 | model.album = "Sunrise Over Sea"; |
1048 | - modelFilled.wait(); |
1049 | - compare(model.rowCount, 2); |
1050 | + waitForReady(); |
1051 | + compare(model.count, 2); |
1052 | |
1053 | compare(model.get(0, SongsModel.RoleTitle), "Peaches & Cream"); |
1054 | compare(model.get(0, SongsModel.RoleAuthor), "The John Butler Trio"); |
1055 | |
1056 | model.albumArtist = "unknown"; |
1057 | - modelFilled.wait(); |
1058 | - compare(model.rowCount, 0); |
1059 | + waitForReady(); |
1060 | + compare(model.count, 0); |
1061 | } |
1062 | |
1063 | function test_genre() { |
1064 | model.genre = "rock"; |
1065 | - modelFilled.wait(); |
1066 | - compare(model.rowCount, 3); |
1067 | + waitForReady(); |
1068 | + compare(model.count, 3); |
1069 | |
1070 | compare(model.get(0, SongsModel.RoleTitle), "Buy Me a Pony"); |
1071 | compare(model.get(1, SongsModel.RoleTitle), "Straight Through The Sun"); |
1072 | compare(model.get(2, SongsModel.RoleTitle), "It's Beautiful"); |
1073 | |
1074 | model.albumArtist = "unknown"; |
1075 | - modelFilled.wait(); |
1076 | - compare(model.rowCount, 0); |
1077 | + waitForReady(); |
1078 | + compare(model.count, 0); |
1079 | } |
1080 | |
1081 | } |
PASSED: Continuous integration, rev:269 jenkins. qa.ubuntu. com/job/ mediascanner2- ci/113/ jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- amd64-ci/ 54 jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- armhf-ci/ 54 jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- armhf-ci/ 54/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- i386-ci/ 54
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mediascanne r2-ci/113/ rebuild
http://