Merge lp:~jpakkane/mediascanner/immutable-mediafile into lp:mediascanner/v2
- immutable-mediafile
- Merge into v2
Status: | Merged |
---|---|
Approved by: | James Henstridge |
Approved revision: | 198 |
Merged at revision: | 189 |
Proposed branch: | lp:~jpakkane/mediascanner/immutable-mediafile |
Merge into: | lp:mediascanner/v2 |
Diff against target: |
680 lines (+382/-91) 12 files modified
src/daemon/MetadataExtractor.cc (+21/-17) src/mediascanner/CMakeLists.txt (+1/-0) src/mediascanner/MediaFile.cc (+3/-53) src/mediascanner/MediaFile.hh (+4/-12) src/mediascanner/MediaFileBuilder.cc (+116/-0) src/mediascanner/MediaFileBuilder.hh (+83/-0) src/mediascanner/utils.cc (+18/-0) src/mediascanner/utils.hh (+1/-0) test/CMakeLists.txt (+4/-0) test/basic.cc (+7/-4) test/test_mediastore.cc (+4/-5) test/test_mfbuilder.cc (+120/-0) |
To merge this branch: | bzr merge lp:~jpakkane/mediascanner/immutable-mediafile |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Henstridge | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+197074@code.launchpad.net |
Commit message
Made MediaFile immutable.
Description of the change
Made MediaFile immutable. This is done by creating a new MediaFileBuilder class that you can use to incrementally store MediaFile properties. MediaFile can then be built with one atomic step. This ensures no half-initialised MediaFiles can escape into the wild.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 191. By Jussi Pakkanen
-
Merged trunk.
- 192. By Jussi Pakkanen
-
Update tests.
- 193. By Jussi Pakkanen
-
Capitalisation fix.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:191
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:193
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
James Henstridge (jamesh) wrote : | # |
Is there any reason why you removed MediaFile.addUri()? I added it because I was using it in the scope.
James Henstridge (jamesh) wrote : | # |
Also, as far as API designs go, what do you think of the one described here?
- 194. By Jussi Pakkanen
-
Removed unnecessary includes.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:194
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Jussi Pakkanen (jpakkane) wrote : | # |
MediaFileBuilder is already almost exactly that. It would just need to change the setXXX functions to return reference to *this. I can make that change if it is deemed useful.
The reason I moved the Uri function (I assume you meant getUri instead of addUri) is that after changing to mediafilebuilder the uri function was used only in one place (gstreamer pipeline building) and at that point a MediaFile had not been created yet.
If it is necessary I guess I can put it back, but since Mediascanner only deals with local files, it seems cleaner to me to only deal with filenames.
James Henstridge (jamesh) wrote : | # |
We needed URIs in the daemon internals, and we needed URIs in some users of the media scanner (the scope is an obvious example, Drag and Drop also deals with URIs, etc).
It's not clear that having the method makes the interface more difficult to understand, so I'd prefer to keep it unless there is a good reason for it to go.
- 195. By Jussi Pakkanen
-
Return of getUri.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:195
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
James Henstridge (jamesh) wrote : | # |
[1]
Since the aim of this branch is to make MediaFile act like a value type, would it make sense to declare its data members to be const? That would ensure that the the MediaFile code itself doesn't start mutating the instance.
[2]
Since the big long argument list for the MediaFile constructor is a problem, would it be worth getting rid of it all together? At present, it is only being used in (a) MediaStore.
If we followed the example from the C++ FAQ, we could have a MediaFile constructor taking a Builder instance.
[3]
I'm on the fence on this one, but would it make sense for the MediaFileBuilder constructor to take the file name directly? This is the one field that is mandatory in a MediaFile, so it doesn't seem so bad to require it when creating the builder.
Jussi Pakkanen (jpakkane) wrote : | # |
[1] This is something I thought about for a long time. It you make instance variables const, you can't copy them. Meaning you can't do this:
mf = some_func();
This would make Mediafile assigment atomic but not constant. Sometimes this is what you need. But I see the const issue as well. I guess we could start by making them const and unconsting them if necessary.
[2]
I'm a bit on the fence about this. I like being able to explicitly instantiate objects without other helper objects. This is a potential source of bugs, though.
[3]
Yes, change coming up.
- 196. By Jussi Pakkanen
-
Make MediaFileBuilder take the filename in a constructor because that is mandatory.
- 197. By Jussi Pakkanen
-
Use initialisers in a constructor.
James Henstridge (jamesh) wrote : | # |
Looks good. After discussing in person, I think we can clean up the other improvement issues post-merge.
- 198. By Jussi Pakkanen
-
Fixed initialisation order.
Preview Diff
1 | === modified file 'src/daemon/MetadataExtractor.cc' |
2 | --- src/daemon/MetadataExtractor.cc 2013-11-28 04:08:20 +0000 |
3 | +++ src/daemon/MetadataExtractor.cc 2013-12-02 14:22:54 +0000 |
4 | @@ -18,6 +18,8 @@ |
5 | */ |
6 | |
7 | #include "../mediascanner/MediaFile.hh" |
8 | +#include "../mediascanner/MediaFileBuilder.hh" |
9 | +#include "../mediascanner/utils.hh" |
10 | #include "MetadataExtractor.hh" |
11 | |
12 | #include <glib-object.h> |
13 | @@ -32,6 +34,7 @@ |
14 | |
15 | using namespace std; |
16 | |
17 | + |
18 | struct MetadataExtractorPrivate { |
19 | std::unique_ptr<GstDiscoverer,void(*)(void *)> discoverer; |
20 | MetadataExtractorPrivate() : discoverer(nullptr, g_object_unref) {}; |
21 | @@ -102,7 +105,7 @@ |
22 | |
23 | static void |
24 | extract_tag_info (const GstTagList * list, const gchar * tag, gpointer user_data) { |
25 | - MediaFile *mf = (MediaFile *) user_data; |
26 | + MediaFileBuilder *mfb = (MediaFileBuilder *) user_data; |
27 | int i, num; |
28 | string tagname(tag); |
29 | |
30 | @@ -113,23 +116,23 @@ |
31 | val = gst_tag_list_get_value_index (list, tag, i); |
32 | if (G_VALUE_HOLDS_STRING(val)) { |
33 | if (tagname == GST_TAG_ARTIST) |
34 | - mf->setAuthor(g_value_get_string(val)); |
35 | + mfb->setAuthor(g_value_get_string(val)); |
36 | else if (tagname == GST_TAG_TITLE) |
37 | - mf->setTitle(g_value_get_string(val)); |
38 | + mfb->setTitle(g_value_get_string(val)); |
39 | else if (tagname == GST_TAG_ALBUM) |
40 | - mf->setAlbum(g_value_get_string(val)); |
41 | + mfb->setAlbum(g_value_get_string(val)); |
42 | else if (tagname == GST_TAG_ALBUM_ARTIST) |
43 | - mf->setAlbumArtist(g_value_get_string(val)); |
44 | + mfb->setAlbumArtist(g_value_get_string(val)); |
45 | } else if (G_VALUE_HOLDS(val, GST_TYPE_DATE_TIME)) { |
46 | if (tagname == GST_TAG_DATE_TIME) { |
47 | GstDateTime *dt = static_cast<GstDateTime*>(g_value_get_boxed(val)); |
48 | char *dt_string = gst_date_time_to_iso8601_string(dt); |
49 | - mf->setDate(dt_string); |
50 | + mfb->setDate(dt_string); |
51 | g_free(dt_string); |
52 | } |
53 | } else if (G_VALUE_HOLDS_UINT(val)) { |
54 | if (tagname == GST_TAG_TRACK_NUMBER) { |
55 | - mf->setTrackNumber(g_value_get_uint(val)); |
56 | + mfb->setTrackNumber(g_value_get_uint(val)); |
57 | } |
58 | } |
59 | |
60 | @@ -137,11 +140,12 @@ |
61 | } |
62 | |
63 | MediaFile MetadataExtractor::extract(const DetectedFile &d) { |
64 | - MediaFile mf(d.filename); |
65 | - mf.setETag(d.etag); |
66 | - mf.setContentType(d.content_type); |
67 | - mf.setType(d.type); |
68 | - string uri = mf.getUri(); |
69 | + MediaFileBuilder mfb(d.filename); |
70 | + mfb.setETag(d.etag); |
71 | + mfb.setContentType(d.content_type); |
72 | + mfb.setType(d.type); |
73 | + string uri = getUri(d.filename); |
74 | + |
75 | GError *error = nullptr; |
76 | unique_ptr<GstDiscovererInfo, void(*)(void *)> info( |
77 | gst_discoverer_discover_uri(p->discoverer.get(), uri.c_str(), &error), |
78 | @@ -151,21 +155,21 @@ |
79 | g_error_free(error); |
80 | |
81 | string msg = "Discovery of file "; |
82 | - msg += mf.getFileName(); |
83 | + msg += d.filename; |
84 | msg += " failed: "; |
85 | msg += errortxt; |
86 | throw runtime_error(msg); |
87 | } |
88 | |
89 | if (gst_discoverer_info_get_result(info.get()) != GST_DISCOVERER_OK) { |
90 | - throw runtime_error("Unable to discover file " + mf.getFileName()); |
91 | + throw runtime_error("Unable to discover file " + d.filename); |
92 | } |
93 | |
94 | const GstTagList *tags = gst_discoverer_info_get_tags(info.get()); |
95 | if (tags != NULL) { |
96 | - gst_tag_list_foreach(tags, extract_tag_info, &mf); |
97 | + gst_tag_list_foreach(tags, extract_tag_info, &mfb); |
98 | } |
99 | - mf.setDuration(static_cast<int>( |
100 | + mfb.setDuration(static_cast<int>( |
101 | gst_discoverer_info_get_duration(info.get())/GST_SECOND)); |
102 | - return mf; |
103 | + return mfb.build(); |
104 | } |
105 | |
106 | === modified file 'src/mediascanner/CMakeLists.txt' |
107 | --- src/mediascanner/CMakeLists.txt 2013-11-14 15:04:14 +0000 |
108 | +++ src/mediascanner/CMakeLists.txt 2013-12-02 14:22:54 +0000 |
109 | @@ -1,5 +1,6 @@ |
110 | add_library(mediascanner SHARED |
111 | MediaFile.cc |
112 | + MediaFileBuilder.cc |
113 | Album.cc |
114 | MediaStore.cc |
115 | utils.cc |
116 | |
117 | === modified file 'src/mediascanner/MediaFile.cc' |
118 | --- src/mediascanner/MediaFile.cc 2013-11-25 07:53:45 +0000 |
119 | +++ src/mediascanner/MediaFile.cc 2013-12-02 14:22:54 +0000 |
120 | @@ -17,9 +17,8 @@ |
121 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
122 | */ |
123 | |
124 | -#include <stdexcept> |
125 | -#include <glib.h> |
126 | #include "MediaFile.hh" |
127 | +#include "utils.hh" |
128 | |
129 | using namespace std; |
130 | |
131 | @@ -73,44 +72,8 @@ |
132 | return type; |
133 | } |
134 | |
135 | -void MediaFile::setContentType(const std::string &content_type) noexcept { |
136 | - this->content_type = content_type; |
137 | -} |
138 | - |
139 | -void MediaFile::setETag(const std::string &etag) noexcept { |
140 | - this->etag = etag; |
141 | -} |
142 | - |
143 | -void MediaFile::setTitle(const std::string &title) noexcept { |
144 | - this->title = title; |
145 | -} |
146 | - |
147 | -void MediaFile::setAuthor(const std::string &author) noexcept { |
148 | - this->author = author; |
149 | -} |
150 | - |
151 | -void MediaFile::setAlbum(const std::string &album) noexcept { |
152 | - this->album = album; |
153 | -} |
154 | - |
155 | -void MediaFile::setAlbumArtist(const std::string &album_artist) noexcept { |
156 | - this->album_artist = album_artist; |
157 | -} |
158 | - |
159 | -void MediaFile::setDate(const std::string &date) noexcept { |
160 | - this->date = date; |
161 | -} |
162 | - |
163 | -void MediaFile::setTrackNumber(int track_number) noexcept { |
164 | - this->track_number = track_number; |
165 | -} |
166 | - |
167 | -void MediaFile::setDuration(int duration) noexcept { |
168 | - this->duration = duration; |
169 | -} |
170 | - |
171 | -void MediaFile::setType(MediaType type) noexcept { |
172 | - this->type = type; |
173 | +std::string MediaFile::getUri() const { |
174 | + return ::getUri(filename); |
175 | } |
176 | |
177 | bool MediaFile::operator==(const MediaFile &other) const { |
178 | @@ -132,16 +95,3 @@ |
179 | return !(*this == other); |
180 | } |
181 | |
182 | -std::string MediaFile::getUri() const { |
183 | - GError *error = NULL; |
184 | - char *uristr = g_filename_to_uri(filename.c_str(), "", &error); |
185 | - if (error) { |
186 | - string msg("Could not build URI: "); |
187 | - msg += error->message; |
188 | - g_error_free(error); |
189 | - throw runtime_error(msg); |
190 | - } |
191 | - string uri(uristr); |
192 | - g_free(uristr); |
193 | - return uri; |
194 | -} |
195 | |
196 | === modified file 'src/mediascanner/MediaFile.hh' |
197 | --- src/mediascanner/MediaFile.hh 2013-11-25 07:53:45 +0000 |
198 | +++ src/mediascanner/MediaFile.hh 2013-12-02 14:22:54 +0000 |
199 | @@ -40,24 +40,16 @@ |
200 | const std::string& getAlbum() const noexcept; |
201 | const std::string& getAlbumArtist() const noexcept; |
202 | const std::string& getDate() const noexcept; |
203 | + std::string getUri() const; |
204 | + |
205 | int getTrackNumber() const noexcept; |
206 | int getDuration() const noexcept; |
207 | MediaType getType() const noexcept; |
208 | bool operator==(const MediaFile &other) const; |
209 | bool operator!=(const MediaFile &other) const; |
210 | |
211 | - void setContentType(const std::string& content_type) noexcept; |
212 | - void setETag(const std::string& etag) noexcept; |
213 | - void setTitle(const std::string& title) noexcept; |
214 | - void setAuthor(const std::string& author) noexcept; |
215 | - void setAlbum(const std::string& album) noexcept; |
216 | - void setAlbumArtist(const std::string& album_artist) noexcept; |
217 | - void setDate(const std::string& date) noexcept; |
218 | - void setTrackNumber(int track_number) noexcept; |
219 | - void setDuration(int duration) noexcept; |
220 | - void setType(MediaType type) noexcept; |
221 | - |
222 | - std::string getUri() const; |
223 | + // There are no setters. MediaFiles are immutable. |
224 | + // For piecewise construction use MediaFileBuilder. |
225 | |
226 | private: |
227 | std::string filename; |
228 | |
229 | === added file 'src/mediascanner/MediaFileBuilder.cc' |
230 | --- src/mediascanner/MediaFileBuilder.cc 1970-01-01 00:00:00 +0000 |
231 | +++ src/mediascanner/MediaFileBuilder.cc 2013-12-02 14:22:54 +0000 |
232 | @@ -0,0 +1,116 @@ |
233 | +/* |
234 | + * Copyright (C) 2013 Canonical, Ltd. |
235 | + * |
236 | + * Authors: |
237 | + * Jussi Pakkanen <jussi.pakkanen@canonical.com> |
238 | + * |
239 | + * This library is free software; you can redistribute it and/or modify it under |
240 | + * the terms of version 3 of the GNU General Public License as published |
241 | + * by the Free Software Foundation. |
242 | + * |
243 | + * This library is distributed in the hope that it will be useful, but WITHOUT |
244 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
245 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
246 | + * details. |
247 | + * |
248 | + * You should have received a copy of the GNU General Public License |
249 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
250 | + */ |
251 | + |
252 | +#include"MediaFileBuilder.hh" |
253 | +#include"MediaFile.hh" |
254 | +#include<stdexcept> |
255 | + |
256 | +MediaFileBuilder::MediaFileBuilder(const std::string &fname) { |
257 | + filename = fname; |
258 | +} |
259 | + |
260 | +MediaFileBuilder::MediaFileBuilder(const MediaFile &mf) : |
261 | + type(mf.getType()), |
262 | + filename(mf.getFileName()), |
263 | + content_type(mf.getContentType()), |
264 | + etag(mf.getETag()), |
265 | + title(mf.getTitle()), |
266 | + date(mf.getDate()), |
267 | + author(mf.getAuthor()), |
268 | + album(mf.getAlbum()), |
269 | + album_artist(mf.getAlbumArtist()), |
270 | + track_number(mf.getTrackNumber()), |
271 | + duration(mf.getDuration()) { |
272 | +} |
273 | + |
274 | +MediaFile MediaFileBuilder::build() const { |
275 | + return MediaFile(filename, content_type, etag, title, date, author, |
276 | + album, album_artist, track_number, duration, type); |
277 | +} |
278 | + |
279 | +void MediaFileBuilder::setType(MediaType t) { |
280 | + if(type_set) |
281 | + throw std::invalid_argument("Tried to set type when it was already set."); |
282 | + type = t; |
283 | + type_set = true; |
284 | +} |
285 | + |
286 | +void MediaFileBuilder::setETag(const std::string &e) { |
287 | + if(etag_set) |
288 | + throw std::invalid_argument("Tried to set filename when it was already set."); |
289 | + etag = e; |
290 | + etag_set = true; |
291 | + |
292 | +} |
293 | +void MediaFileBuilder::setContentType(const std::string &c) { |
294 | + if(content_type_set) |
295 | + throw std::invalid_argument("Tried to set filename when it was already set."); |
296 | + content_type = c; |
297 | + content_type_set = true; |
298 | + |
299 | +} |
300 | + |
301 | +void MediaFileBuilder::setTitle(const std::string &t) { |
302 | + if(title_set) |
303 | + throw std::invalid_argument("Tried to set title when it was already set."); |
304 | + title = t; |
305 | + title_set = true; |
306 | +} |
307 | + |
308 | +void MediaFileBuilder::setDate(const std::string &d) { |
309 | + if(date_set) |
310 | + throw std::invalid_argument("Tried to set date when it was already set."); |
311 | + date = d; |
312 | + date_set = true; |
313 | +} |
314 | + |
315 | +void MediaFileBuilder::setAuthor(const std::string &a) { |
316 | + if(author_set) |
317 | + throw std::invalid_argument("Tried to set author when it was already set."); |
318 | + author = a; |
319 | + author_set = true; |
320 | +} |
321 | + |
322 | +void MediaFileBuilder::setAlbum(const std::string &a) { |
323 | + if(album_set) |
324 | + throw std::invalid_argument("Tried to set album when it was already set."); |
325 | + album = a; |
326 | + album_set = true; |
327 | +} |
328 | + |
329 | +void MediaFileBuilder::setAlbumArtist(const std::string &a) { |
330 | + if(album_artist_set) |
331 | + throw std::invalid_argument("Tried to set album artist when it was already set."); |
332 | + album_artist = a; |
333 | + album_artist_set = true; |
334 | +} |
335 | + |
336 | +void MediaFileBuilder::setTrackNumber(int n) { |
337 | + if(track_number_set) |
338 | + throw std::invalid_argument("Tried to set track number when it was already set."); |
339 | + track_number = n; |
340 | + track_number_set = true; |
341 | +} |
342 | + |
343 | +void MediaFileBuilder::setDuration(int n) { |
344 | + if(duration_set) |
345 | + throw std::invalid_argument("Tried to set duration when it was already set."); |
346 | + duration = n; |
347 | + duration_set = true; |
348 | +} |
349 | |
350 | === added file 'src/mediascanner/MediaFileBuilder.hh' |
351 | --- src/mediascanner/MediaFileBuilder.hh 1970-01-01 00:00:00 +0000 |
352 | +++ src/mediascanner/MediaFileBuilder.hh 2013-12-02 14:22:54 +0000 |
353 | @@ -0,0 +1,83 @@ |
354 | +/* |
355 | + * Copyright (C) 2013 Canonical, Ltd. |
356 | + * |
357 | + * Authors: |
358 | + * Jussi Pakkanen <jussi.pakkanen@canonical.com> |
359 | + * |
360 | + * This library is free software; you can redistribute it and/or modify it under |
361 | + * the terms of version 3 of the GNU General Public License as published |
362 | + * by the Free Software Foundation. |
363 | + * |
364 | + * This library is distributed in the hope that it will be useful, but WITHOUT |
365 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
366 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
367 | + * details. |
368 | + * |
369 | + * You should have received a copy of the GNU General Public License |
370 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
371 | + */ |
372 | + |
373 | +#ifndef MEDIAFILEBUILDER_H_ |
374 | +#define MEDIAFILEBUILDER_H_ |
375 | + |
376 | +#include"scannercore.hh" |
377 | +#include<string> |
378 | + |
379 | +class MediaFile; |
380 | + |
381 | +class MediaFileBuilder final { |
382 | +public: |
383 | + MediaFileBuilder(const std::string &filename); |
384 | + MediaFileBuilder(const MediaFile &mf); |
385 | + MediaFileBuilder(const MediaFileBuilder &) = delete; |
386 | + MediaFileBuilder& operator=(MediaFileBuilder &) = delete; |
387 | + |
388 | + MediaFile build() const; |
389 | + |
390 | + void setType(MediaType t); |
391 | + void setETag(const std::string &e); |
392 | + void setContentType(const std::string &c); |
393 | + void setTitle(const std::string &t); |
394 | + void setDate(const std::string &d); |
395 | + void setAuthor(const std::string &a); |
396 | + void setAlbum(const std::string &a); |
397 | + void setAlbumArtist(const std::string &a); |
398 | + void setTrackNumber(int n); |
399 | + void setDuration(int d); |
400 | + |
401 | +private: |
402 | + bool type_set = false; |
403 | + MediaType type = UnknownMedia; |
404 | + |
405 | + std::string filename; |
406 | + |
407 | + bool content_type_set = false; |
408 | + std::string content_type; |
409 | + |
410 | + bool etag_set = false; |
411 | + std::string etag; |
412 | + |
413 | + bool title_set = false; |
414 | + std::string title; |
415 | + |
416 | + bool date_set = false; |
417 | + std::string date; |
418 | + |
419 | + bool author_set = false; |
420 | + std::string author; |
421 | + |
422 | + bool album_set = false; |
423 | + std::string album; |
424 | + |
425 | + bool album_artist_set = false; |
426 | + std::string album_artist; |
427 | + |
428 | + int track_number = 0; |
429 | + bool track_number_set = false; |
430 | + |
431 | + int duration = 0; |
432 | + bool duration_set = false; |
433 | +}; |
434 | + |
435 | + |
436 | +#endif |
437 | |
438 | === modified file 'src/mediascanner/utils.cc' |
439 | --- src/mediascanner/utils.cc 2013-11-12 04:35:16 +0000 |
440 | +++ src/mediascanner/utils.cc 2013-12-02 14:22:54 +0000 |
441 | @@ -20,6 +20,8 @@ |
442 | #include"utils.hh" |
443 | |
444 | #include<vector> |
445 | +#include<stdexcept> |
446 | +#include<glib.h> |
447 | |
448 | std::string sqlQuote(const std::string &input) { |
449 | std::vector<char> out; |
450 | @@ -67,3 +69,19 @@ |
451 | } |
452 | return result; |
453 | } |
454 | + |
455 | +std::string getUri(const std::string &filename) { |
456 | + GError *error = NULL; |
457 | + char *uristr = g_filename_to_uri(filename.c_str(), "", &error); |
458 | + if (error) { |
459 | + std::string msg("Could not build URI: "); |
460 | + msg += error->message; |
461 | + g_error_free(error); |
462 | + throw std::runtime_error(msg); |
463 | + } |
464 | + |
465 | + std::string uri(uristr); |
466 | + g_free(uristr); |
467 | + return uri; |
468 | +} |
469 | + |
470 | |
471 | === modified file 'src/mediascanner/utils.hh' |
472 | --- src/mediascanner/utils.hh 2013-11-12 04:35:16 +0000 |
473 | +++ src/mediascanner/utils.hh 2013-12-02 14:22:54 +0000 |
474 | @@ -24,5 +24,6 @@ |
475 | |
476 | std::string sqlQuote(const std::string &input); |
477 | std::string filenameToTitle(const std::string &filename); |
478 | +std::string getUri(const std::string &filename); |
479 | |
480 | #endif |
481 | |
482 | === modified file 'test/CMakeLists.txt' |
483 | --- test/CMakeLists.txt 2013-11-18 10:23:49 +0000 |
484 | +++ test/CMakeLists.txt 2013-12-02 14:22:54 +0000 |
485 | @@ -38,3 +38,7 @@ |
486 | add_executable(test_sqliteutils test_sqliteutils.cc) |
487 | target_link_libraries(test_sqliteutils gtest ${MEDIASCANNER_LIBRARIES}) |
488 | add_test(test_sqliteutils test_sqliteutils) |
489 | + |
490 | +add_executable(test_mfbuilder test_mfbuilder.cc) |
491 | +target_link_libraries(test_mfbuilder gtest mediascanner) |
492 | +add_test(test_mfbuilder test_mfbuilder) |
493 | |
494 | === modified file 'test/basic.cc' |
495 | --- test/basic.cc 2013-11-28 04:08:20 +0000 |
496 | +++ test/basic.cc 2013-12-02 14:22:54 +0000 |
497 | @@ -18,6 +18,7 @@ |
498 | */ |
499 | |
500 | #include <mediascanner/MediaFile.hh> |
501 | +#include <mediascanner/MediaFileBuilder.hh> |
502 | #include <mediascanner/MediaStore.hh> |
503 | #include <daemon/MetadataExtractor.hh> |
504 | #include <daemon/SubtreeWatcher.hh> |
505 | @@ -181,8 +182,9 @@ |
506 | MediaFile media = store.lookup(outfile); |
507 | EXPECT_NE(media.getETag(), ""); |
508 | EXPECT_EQ(media.getTitle(), "track1"); |
509 | - media.setTitle("something else"); |
510 | - store.insert(media); |
511 | + MediaFileBuilder mfb(media); |
512 | + mfb.setTitle("something else"); |
513 | + store.insert(mfb.build()); |
514 | |
515 | /* Scan again, and note that the data hasn't been updated */ |
516 | scanFiles(store, testdir, AudioMedia); |
517 | @@ -190,8 +192,9 @@ |
518 | EXPECT_EQ(media.getTitle(), "something else"); |
519 | |
520 | /* Now change the stored etag, to trigger an update */ |
521 | - media.setETag("old-etag"); |
522 | - store.insert(media); |
523 | + MediaFileBuilder mfb2(media); |
524 | + mfb2.setETag("old-etag"); |
525 | + store.insert(mfb2.build()); |
526 | scanFiles(store, testdir, AudioMedia); |
527 | media = store.lookup(outfile); |
528 | EXPECT_EQ(media.getTitle(), "track1"); |
529 | |
530 | === modified file 'test/test_mediastore.cc' |
531 | --- test/test_mediastore.cc 2013-11-28 03:54:39 +0000 |
532 | +++ test/test_mediastore.cc 2013-12-02 14:22:54 +0000 |
533 | @@ -47,6 +47,10 @@ |
534 | TEST_F(MediaStoreTest, init) { |
535 | MediaStore store(":memory:", MS_READ_WRITE); |
536 | } |
537 | +TEST_F(MediaStoreTest, mediafile_uri) { |
538 | + MediaFile media("/path/to/file.ogg"); |
539 | + EXPECT_EQ(media.getUri(), "file:///path/to/file.ogg"); |
540 | +} |
541 | |
542 | TEST_F(MediaStoreTest, equality) { |
543 | MediaFile audio1("a", "type", "etag", "1900", "b", "c", "d", "e", 1, 5, AudioMedia); |
544 | @@ -64,11 +68,6 @@ |
545 | EXPECT_NE(audio2, video2); |
546 | } |
547 | |
548 | -TEST_F(MediaStoreTest, mediafile_uri) { |
549 | - MediaFile media("/path/to/file.ogg"); |
550 | - EXPECT_EQ(media.getUri(), "file:///path/to/file.ogg"); |
551 | -} |
552 | - |
553 | TEST_F(MediaStoreTest, lookup) { |
554 | MediaFile audio("aaa", "type", "etag", "bbb bbb", "1900-01-01", "ccc", "ddd", "eee", 3, 5, AudioMedia); |
555 | MediaStore store(":memory:", MS_READ_WRITE); |
556 | |
557 | === added file 'test/test_mfbuilder.cc' |
558 | --- test/test_mfbuilder.cc 1970-01-01 00:00:00 +0000 |
559 | +++ test/test_mfbuilder.cc 2013-12-02 14:22:54 +0000 |
560 | @@ -0,0 +1,120 @@ |
561 | +/* |
562 | + * Copyright (C) 2013 Canonical, Ltd. |
563 | + * |
564 | + * Authors: |
565 | + * Jussi Pakkanen <jussi.pakkanen@canonical.com> |
566 | + * |
567 | + * This library is free software; you can redistribute it and/or modify it under |
568 | + * the terms of version 3 of the GNU General Public License as published |
569 | + * by the Free Software Foundation. |
570 | + * |
571 | + * This library is distributed in the hope that it will be useful, but WITHOUT |
572 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
573 | + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
574 | + * details. |
575 | + * |
576 | + * You should have received a copy of the GNU General Public License |
577 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
578 | + */ |
579 | + |
580 | +#include<gtest/gtest.h> |
581 | +#include<stdexcept> |
582 | +#include"mediascanner/MediaFile.hh" |
583 | +#include"mediascanner/MediaFileBuilder.hh" |
584 | + |
585 | +/** |
586 | + * This is a helper class to build MediaFiles. Since we want them |
587 | + * to be immutable and always valid, the user needs to always list |
588 | + * all variables in the constructor. This is cumbersome so this class |
589 | + * allows you to gather them one by one and then finally construct |
590 | + * a fully valid MediaFile. |
591 | + * |
592 | + * If you try to assign the same property twice, an exception is thrown. |
593 | + * This means that MediaFileBuilders are meant to build only one |
594 | + * MediaFile. To build a new one create a new MediaFileBuilder. This is to |
595 | + * ensure that no state leaks from the first MediaFile to the second. |
596 | + */ |
597 | + |
598 | +class MFBTest : public ::testing::Test { |
599 | + protected: |
600 | + MFBTest() { |
601 | + } |
602 | + |
603 | + virtual ~MFBTest() { |
604 | + } |
605 | + |
606 | + virtual void SetUp() { |
607 | + } |
608 | + |
609 | + virtual void TearDown() { |
610 | + } |
611 | +}; |
612 | + |
613 | +TEST_F(MFBTest, basic) { |
614 | + MediaType type(AudioMedia); |
615 | + std::string fname("abc"); |
616 | + std::string title("def"); |
617 | + std::string date("ghi"); |
618 | + std::string author("jkl"); |
619 | + std::string album("mno"); |
620 | + std::string album_artist("pqr"); |
621 | + std::string etag("stu"); |
622 | + std::string content_type("vwx"); |
623 | + int track_number = 13; |
624 | + int duration = 99; |
625 | + |
626 | + MediaFileBuilder b(fname); |
627 | + |
628 | + b.setType(type); |
629 | + ASSERT_THROW(b.setType(type), std::invalid_argument); |
630 | + |
631 | + b.setTitle(title); |
632 | + ASSERT_THROW(b.setTitle(fname), std::invalid_argument); |
633 | + |
634 | + b.setDate(date); |
635 | + ASSERT_THROW(b.setDate(date), std::invalid_argument); |
636 | + |
637 | + b.setAuthor(author); |
638 | + ASSERT_THROW(b.setAuthor(author), std::invalid_argument); |
639 | + |
640 | + b.setAlbum(album); |
641 | + ASSERT_THROW(b.setAlbum(album), std::invalid_argument); |
642 | + |
643 | + b.setAlbumArtist(album_artist); |
644 | + ASSERT_THROW(b.setAlbumArtist(album_artist), std::invalid_argument); |
645 | + |
646 | + b.setTrackNumber(track_number); |
647 | + ASSERT_THROW(b.setTrackNumber(track_number), std::invalid_argument); |
648 | + |
649 | + b.setDuration(duration); |
650 | + ASSERT_THROW(b.setDuration(duration), std::invalid_argument); |
651 | + |
652 | + b.setETag(etag); |
653 | + ASSERT_THROW(b.setETag(etag), std::invalid_argument); |
654 | + |
655 | + b.setContentType(content_type); |
656 | + ASSERT_THROW(b.setContentType(content_type), std::invalid_argument); |
657 | + |
658 | + // Now see if data survives a round trip. |
659 | + MediaFile mf = b.build(); |
660 | + ASSERT_EQ(mf.getType(), type); |
661 | + ASSERT_EQ(mf.getFileName(), fname); |
662 | + ASSERT_EQ(mf.getTitle(), title); |
663 | + ASSERT_EQ(mf.getDate(), date); |
664 | + ASSERT_EQ(mf.getAuthor(), author); |
665 | + ASSERT_EQ(mf.getAlbum(), album); |
666 | + ASSERT_EQ(mf.getAlbumArtist(), album_artist); |
667 | + ASSERT_EQ(mf.getTrackNumber(), track_number); |
668 | + ASSERT_EQ(mf.getDuration(), duration); |
669 | + ASSERT_EQ(mf.getETag(), etag); |
670 | + ASSERT_EQ(mf.getContentType(), content_type); |
671 | + |
672 | + MediaFileBuilder mfb2(mf); |
673 | + MediaFile mf2 = mfb2.build(); |
674 | + ASSERT_EQ(mf, mf2); |
675 | +} |
676 | + |
677 | +int main(int argc, char **argv) { |
678 | + ::testing::InitGoogleTest(&argc, argv); |
679 | + return RUN_ALL_TESTS(); |
680 | +} |
PASSED: Continuous integration, rev:190 jenkins. qa.ubuntu. com/job/ mediascanner- v2-ci/5/ jenkins. qa.ubuntu. com/job/ mediascanner- v2-trusty- amd64-ci/ 5 jenkins. qa.ubuntu. com/job/ mediascanner- v2-trusty- armhf-ci/ 5 jenkins. qa.ubuntu. com/job/ mediascanner- v2-trusty- armhf-ci/ 5/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mediascanner- v2-trusty- i386-ci/ 5
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 r-v2-ci/ 5/rebuild
http://