Updated French translation
[profile/ivi/rygel.git] / tests / rygel-media-engine-test.vala
1 /*
2  * Copyright (C) 2012 Intel Corporation.
3  *
4  * Author: Jens Georg <jensg@openismus.com>
5  *
6  * This file is part of Rygel.
7  *
8  * Rygel is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Rygel is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22
23 [CCode (cname="TEST_DATA_FOLDER")]
24 extern const string TEST_DATA_FOLDER;
25
26 // Always test locally built engines
27 [CCode (cname="TEST_ENGINE_PATH")]
28 extern const string TEST_ENGINE_PATH;
29
30 [CCode (cname="BUILT_ENGINES")]
31 extern const string BUILT_ENGINES;
32
33 /**
34  * Helper class to convince the engine loader to load the media engine we want
35  * to test.
36  */
37 internal class Rygel.DataSourceTestConfig : Rygel.BaseConfiguration {
38     private string engine;
39     private string path;
40
41     public DataSourceTestConfig (string? path = null,
42                                  string? engine = null) {
43         this.engine = engine;
44         this.path = path;
45     }
46
47     public override string get_media_engine () throws Error {
48         if (this.engine != null) {
49             return this.engine;
50         }
51
52         // Throw error
53         return base.get_media_engine ();
54     }
55
56     public override string get_engine_path () throws Error {
57         if (this.path != null) {
58             return this.path;
59         }
60
61         return TEST_ENGINE_PATH;
62     }
63
64     public string to_string () {
65         return "Path: %s, Engine: %s".printf (this.path, this.engine);
66     }
67
68     public void clear () {
69         this.engine = null;
70         this.path = null;
71     }
72 }
73
74 /**
75  * Stub implementation of Rygel.HTTPSeek
76  */
77 internal class Rygel.ByteSeek : Rygel.HTTPSeek {
78     public ByteSeek (int64 first, int64 last, int64 length) {
79         var msg = new Soup.Message ("GET", "http://example.com/");
80
81         try {
82             base (msg, first, last, 1, length);
83         } catch (HTTPSeekError error) {
84             assert_not_reached ();
85         }
86
87         this.seek_type = HTTPSeekType.BYTE;
88     }
89
90     public override void add_response_headers () {}
91 }
92
93 /**
94  * Wrapper class arount uint8[] arrays to help stuff those buffers into a
95  * Gee.ArrayList
96  */
97 class DataBlock {
98     public uint8[] data;
99
100     public DataBlock (uint8[] data) {
101         this.data = data;
102     }
103 }
104
105 /**
106  * Helper class to collect a number of byte buffers
107  */
108 internal class Rygel.DataPool : Gee.ArrayList<DataBlock> {
109     public uint8[] flatten () {
110         var size = this.total_size ();
111         var result = new uint8[size];
112         var offset = 0;
113
114         foreach (var data in this) {
115             Memory.copy (result + offset, (void *) data.data, data.data.length);
116             offset += data.data.length;
117         }
118
119         this.clear ();
120         this.add (new DataBlock (result));
121
122         return result;
123     }
124
125     public uint64 total_size () {
126         uint64 total = 0;
127         foreach (var data in this) {
128             total += data.data.length;
129         }
130
131         return total;
132     }
133 }
134
135 /**
136  * Test a DataSource implementation against the expectations of the interface
137  *
138  * It is run as part of the test suite but can be used to check arbitrary
139  * media engines as well:
140  *
141  * rygel-media-engine-test /path/to/my/first/custom-rygel-engine.so \
142  *                         /path/to/my/second/custom-rygel-engine.so ...
143  */
144 public class Rygel.DataSourceTest : Object {
145     private File test_data_file;
146     private MappedFile test_data_mapped;
147
148     public DataSourceTest () {
149         var path = Path.build_filename (TEST_DATA_FOLDER, "test-data.dat");
150         this.test_data_file = File.new_for_path (path);
151         try {
152             this.test_data_mapped = new MappedFile (path, false);
153         } catch (Error error) {
154             warning ("Error: Could not map file: %s", error.message);
155             assert_not_reached ();
156         }
157     }
158
159     /// Get the whole file
160     private void test_simple_streaming () {
161         debug ("test_simple_streaming");
162         var source = MediaEngine.get_default ().create_data_source
163                                         (this.test_data_file.get_uri ());
164         // Sources should support file:// urls
165         assert (source != null);
166
167         uint64 received_bytes = 0;
168         var loop = new MainLoop (null, false);
169         source.data_available.connect ( (data) => {
170             received_bytes += data.length;
171         });
172         source.done.connect ( (data) => {
173             loop.quit ();
174         });
175
176         
177         Idle.add ( () => {
178             try {
179                 source.start (null);
180                 return false;
181             } catch (GLib.Error error) {
182                 assert_not_reached ();
183             }
184         });
185
186         loop.run ();
187         assert (received_bytes == this.test_data_mapped.get_length ());
188         source.stop ();
189         source = null;
190     }
191
192     /// Simple byte range request tests
193     private void test_byte_range_request () {
194         debug ("test_byte_range_request");
195         var source = MediaEngine.get_default ().create_data_source
196                                         (this.test_data_file.get_uri ());
197         // Sources should support file:// urls
198         assert (source != null);
199
200         try {
201             // Get the first 10 bytes
202             var seek = new ByteSeek (0, 9, this.test_data_mapped.get_length ());
203
204             var received_data = new DataPool ();
205             var loop = new MainLoop (null, false);
206             source.data_available.connect ( (data) => {
207                 received_data.add (new DataBlock (data));
208             });
209             source.done.connect ( (data) => {
210                 loop.quit ();
211             });
212             source.error.connect ( () => { assert_not_reached (); });
213             source.start (seek);
214             loop.run ();
215             assert (received_data.total_size () == 10);
216             Memory.cmp (this.test_data_mapped.get_contents (),
217                         received_data.flatten (),
218                         (size_t) received_data.total_size ());
219
220             // Get last 10 bytes
221             seek = new ByteSeek (this.test_data_mapped.get_length () - 10,
222                                  this.test_data_mapped.get_length () - 1,
223                                  this.test_data_mapped.get_length ());
224
225             received_data = new DataPool ();
226             loop = new MainLoop (null, false);
227
228             source = MediaEngine.get_default ().create_data_source
229                                         (this.test_data_file.get_uri ());
230             source.data_available.connect ( (data) => {
231                 received_data.add (new DataBlock (data));
232             });
233             source.done.connect ( (data) => {
234                 loop.quit ();
235             });
236             source.error.connect ( () => { assert_not_reached (); });
237             source.start (seek);
238             loop.run ();
239
240             assert (received_data.total_size () == 10);
241             Memory.cmp (this.test_data_mapped.get_contents () +
242                         (this.test_data_mapped.get_length () - 10),
243                         received_data.flatten (),
244                         (size_t) received_data.total_size ());
245
246             // Get something from the middle
247             seek = new ByteSeek (this.test_data_mapped.get_length () / 2,
248                                  (this.test_data_mapped.get_length () / 2) + 9,
249                                  this.test_data_mapped.get_length ());
250
251             received_data = new DataPool ();
252             loop = new MainLoop (null, false);
253
254             source = MediaEngine.get_default ().create_data_source
255                                         (this.test_data_file.get_uri ());
256             source.data_available.connect ( (data) => {
257                 received_data.add (new DataBlock (data));
258             });
259
260             source.done.connect ( (data) => {
261                 loop.quit ();
262             });
263             source.error.connect ( () => { assert_not_reached (); });
264             source.start (seek);
265             loop.run ();
266
267             assert (received_data.total_size () == 10);
268             Memory.cmp (this.test_data_mapped.get_contents () +
269                         (this.test_data_mapped.get_length () / 2),
270                         received_data.flatten (),
271                         (size_t) received_data.total_size ());
272             source.stop ();
273             source = null;
274         } catch (DataSourceError.SEEK_FAILED seek_error) {
275             debug ("Skipping seek test");
276         } catch (Error error) {
277             warning ("Failed to test: %s", error.message);
278             assert_not_reached ();
279         }
280     }
281
282     // Check that calling start() after stop() starts at the beginning of the
283     // data
284     private void test_stop_start () {
285         debug ("test_stop_start");
286         var source = MediaEngine.get_default ().create_data_source
287                                         (this.test_data_file.get_uri ());
288         // Sources should support file:// urls
289         assert (source != null);
290
291         var pool = new DataPool ();
292         var loop = new MainLoop (null, false);
293         source.data_available.connect ( (data) => {
294             pool.add (new DataBlock (data));
295             source.stop ();
296         });
297         source.done.connect ( (data) => {
298             loop.quit ();
299         });
300
301         Idle.add ( () => {
302             try {
303                 source.start (null);
304                 return false;
305             } catch (GLib.Error error) {
306                 assert_not_reached ();
307             }
308         });
309
310
311         loop.run ();
312         pool.clear ();
313
314         Idle.add ( () => {
315             try {
316                 source.start (null);
317                 return false;
318             } catch (GLib.Error error) {
319                 assert_not_reached ();
320             }
321         });
322
323         loop.run ();
324         Memory.cmp (this.test_data_mapped.get_contents (),
325                     pool.flatten (),
326                     (size_t) pool.total_size ());
327
328         source.stop ();
329         source = null;
330     }
331
332     // Check that it is possible to call stop() when the source is frozen and
333     // still get a done() signal
334     private void test_freeze_stop () {
335         debug ("test_freeze_stop");
336         var source = MediaEngine.get_default ().create_data_source
337                                         (this.test_data_file.get_uri ());
338         // Sources should support file:// urls
339         assert (source != null);
340
341         try {
342             source.start (null);
343         } catch (GLib.Error error) {
344             assert_not_reached ();
345         }
346
347         source.freeze ();
348         var loop = new MainLoop (null, false);
349         source.done.connect ( () => {
350             loop.quit ();
351         });
352         var id = Timeout.add_seconds ( 5, () => {
353             assert_not_reached ();
354         });
355         Idle.add ( () => { source.stop (); return false; });
356         loop.run ();
357         Source.remove (id);
358         source.stop ();
359         source = null;
360     }
361
362     // Check that it is possible to stream to two targets in parallel
363     public void test_parallel_streaming () {
364         debug ("test_parallel_streaming");
365         var source1 = MediaEngine.get_default ().create_data_source
366                                         (this.test_data_file.get_uri ());
367         assert (source1 != null);
368         // Sources should support file:// urls
369         var source2 = MediaEngine.get_default ().create_data_source
370                                         (this.test_data_file.get_uri ());
371         assert (source2 != null);
372
373         try {
374             source1.start (null);
375         } catch (GLib.Error error) {
376             assert_not_reached ();
377         }
378
379         var seek = new ByteSeek (0,
380                                  (this.test_data_mapped.get_length () / 2),
381                                  this.test_data_mapped.get_length ());
382         assert (seek != null);
383
384         try {
385             source2.start (null);
386         } catch (GLib.Error error) {
387             assert_not_reached ();
388         }
389
390         var loop = new MainLoop (null, false);
391         var quit = false;
392         source1.done.connect ( () => {
393             if (quit) {
394                 loop.quit ();
395             } else {
396                 quit = true;
397             }
398         });
399
400         source2.done.connect ( () => {
401             if (quit) {
402                 loop.quit ();
403             } else {
404                 quit = true;
405             }
406         });
407         loop.run ();
408     }
409
410     public int run () {
411         this.test_simple_streaming ();
412         this.test_byte_range_request ();
413         this.test_stop_start ();
414         this.test_freeze_stop ();
415         this.test_parallel_streaming ();
416
417         return 0;
418     }
419
420     public static int main (string[] args) {
421         var configs = new Gee.ArrayList<DataSourceTestConfig> ();
422
423         if (args.length > 1) {
424             foreach (var arg in args) {
425                 File file;
426                 if (args[1].has_prefix ("~")) {
427                     file = File.parse_name (args[1]);
428                 } else {
429                    file = File.new_for_commandline_arg (args[1]);
430                 }
431                 var path = file.get_parent ().get_path ();
432                 var engine = file.get_basename ();
433
434                 configs.add (new DataSourceTestConfig (path, engine));
435             }
436         } else {
437             foreach (var engine in BUILT_ENGINES.split (";")) {
438                 var name = engine + "." + Module.SUFFIX;
439                 configs.add (new DataSourceTestConfig (null, name));
440             }
441         }
442
443         DataSourceTestConfig previous_config = null;
444         foreach (var config in configs) {
445             // Invalidate previous config so MetaConfig picks up the
446             // current one
447             if (previous_config != null) {
448                 previous_config.clear ();
449             }
450
451             debug ("=> Executing tests for config %s", config.to_string ());
452             MetaConfig.register_configuration (config);
453             previous_config = config;
454
455             try {
456                 MediaEngine.init ();
457             } catch (Error error) {
458                 assert_not_reached ();
459             }
460
461             var test = new DataSourceTest ();
462
463             var result = test.run ();
464             if (result != 0) {
465                 return result;
466             }
467         }
468
469         return 0;
470     }
471 }