Merge lp:~jpakkane/mediascanner/immutable-mediafile into lp:mediascanner/v2

Proposed by Jussi Pakkanen
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
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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
191. By Jussi Pakkanen

Merged trunk.

192. By Jussi Pakkanen

Update tests.

193. By Jussi Pakkanen

Capitalisation fix.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
James Henstridge (jamesh) wrote :

Is there any reason why you removed MediaFile.addUri()? I added it because I was using it in the scope.

review: Needs Information
Revision history for this message
James Henstridge (jamesh) wrote :

Also, as far as API designs go, what do you think of the one described here?

http://www.parashift.com/c++-faq/named-parameter-idiom.html

194. By Jussi Pakkanen

Removed unnecessary includes.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.make_media() and (b) tests, which could both be converted to MediaFileBuilder. This would also mean those pieces of code wouldn't immediately need updating whenever a new data member is added to MediaFile.

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.

Revision history for this message
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.

Revision history for this message
James Henstridge (jamesh) wrote :

Looks good. After discussing in person, I think we can clean up the other improvement issues post-merge.

review: Approve
198. By Jussi Pakkanen

Fixed initialisation order.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches