2 * Copyright (C) 2012 Intel Corporation.
4 * Author: Jens Georg <jensg@openismus.com>
6 * This file is part of Rygel.
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.
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.
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.
23 [CCode (cname="TEST_DATA_FOLDER")]
24 extern const string TEST_DATA_FOLDER;
26 // Always test locally built engines
27 [CCode (cname="TEST_ENGINE_PATH")]
28 extern const string TEST_ENGINE_PATH;
30 [CCode (cname="BUILT_ENGINES")]
31 extern const string BUILT_ENGINES;
34 * Helper class to convince the engine loader to load the media engine we want
37 internal class Rygel.DataSourceTestConfig : Rygel.BaseConfiguration {
38 private string engine;
41 public DataSourceTestConfig (string? path = null,
42 string? engine = null) {
47 public override string get_media_engine () throws Error {
48 if (this.engine != null) {
53 return base.get_media_engine ();
56 public override string get_engine_path () throws Error {
57 if (this.path != null) {
61 return TEST_ENGINE_PATH;
64 public string to_string () {
65 return "Path: %s, Engine: %s".printf (this.path, this.engine);
68 public void clear () {
75 * Stub implementation of Rygel.HTTPSeek
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/");
82 base (msg, first, last, 1, length);
83 } catch (HTTPSeekError error) {
84 assert_not_reached ();
87 this.seek_type = HTTPSeekType.BYTE;
90 public override void add_response_headers () {}
94 * Wrapper class arount uint8[] arrays to help stuff those buffers into a
100 public DataBlock (uint8[] data) {
106 * Helper class to collect a number of byte buffers
108 internal class Rygel.DataPool : Gee.ArrayList<DataBlock> {
109 public uint8[] flatten () {
110 var size = this.total_size ();
111 var result = new uint8[size];
114 foreach (var data in this) {
115 Memory.copy (result + offset, (void *) data.data, data.data.length);
116 offset += data.data.length;
120 this.add (new DataBlock (result));
125 public uint64 total_size () {
127 foreach (var data in this) {
128 total += data.data.length;
136 * Test a DataSource implementation against the expectations of the interface
138 * It is run as part of the test suite but can be used to check arbitrary
139 * media engines as well:
141 * rygel-media-engine-test /path/to/my/first/custom-rygel-engine.so \
142 * /path/to/my/second/custom-rygel-engine.so ...
144 public class Rygel.DataSourceTest : Object {
145 private File test_data_file;
146 private MappedFile test_data_mapped;
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);
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 ();
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);
167 uint64 received_bytes = 0;
168 var loop = new MainLoop (null, false);
169 source.data_available.connect ( (data) => {
170 received_bytes += data.length;
172 source.done.connect ( (data) => {
181 } catch (GLib.Error error) {
182 assert_not_reached ();
187 assert (received_bytes == this.test_data_mapped.get_length ());
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);
201 // Get the first 10 bytes
202 var seek = new ByteSeek (0, 9, this.test_data_mapped.get_length ());
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));
209 source.done.connect ( (data) => {
212 source.error.connect ( () => { assert_not_reached (); });
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 ());
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 ());
225 received_data = new DataPool ();
226 loop = new MainLoop (null, false);
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));
233 source.done.connect ( (data) => {
236 source.error.connect ( () => { assert_not_reached (); });
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 ());
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 ());
251 received_data = new DataPool ();
252 loop = new MainLoop (null, false);
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));
260 source.done.connect ( (data) => {
263 source.error.connect ( () => { assert_not_reached (); });
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 ());
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 ();
282 // Check that calling start() after stop() starts at the beginning of the
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);
291 var pool = new DataPool ();
292 var loop = new MainLoop (null, false);
293 source.data_available.connect ( (data) => {
294 pool.add (new DataBlock (data));
297 source.done.connect ( (data) => {
305 } catch (GLib.Error error) {
306 assert_not_reached ();
318 } catch (GLib.Error error) {
319 assert_not_reached ();
324 Memory.cmp (this.test_data_mapped.get_contents (),
326 (size_t) pool.total_size ());
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);
343 } catch (GLib.Error error) {
344 assert_not_reached ();
348 var loop = new MainLoop (null, false);
349 source.done.connect ( () => {
352 var id = Timeout.add_seconds ( 5, () => {
353 assert_not_reached ();
355 Idle.add ( () => { source.stop (); return false; });
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);
374 source1.start (null);
375 } catch (GLib.Error error) {
376 assert_not_reached ();
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);
385 source2.start (null);
386 } catch (GLib.Error error) {
387 assert_not_reached ();
390 var loop = new MainLoop (null, false);
392 source1.done.connect ( () => {
400 source2.done.connect ( () => {
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 ();
420 public static int main (string[] args) {
421 var configs = new Gee.ArrayList<DataSourceTestConfig> ();
423 if (args.length > 1) {
424 foreach (var arg in args) {
426 if (args[1].has_prefix ("~")) {
427 file = File.parse_name (args[1]);
429 file = File.new_for_commandline_arg (args[1]);
431 var path = file.get_parent ().get_path ();
432 var engine = file.get_basename ();
434 configs.add (new DataSourceTestConfig (path, engine));
437 foreach (var engine in BUILT_ENGINES.split (";")) {
438 var name = engine + "." + Module.SUFFIX;
439 configs.add (new DataSourceTestConfig (null, name));
443 DataSourceTestConfig previous_config = null;
444 foreach (var config in configs) {
445 // Invalidate previous config so MetaConfig picks up the
447 if (previous_config != null) {
448 previous_config.clear ();
451 debug ("=> Executing tests for config %s", config.to_string ());
452 MetaConfig.register_configuration (config);
453 previous_config = config;
457 } catch (Error error) {
458 assert_not_reached ();
461 var test = new DataSourceTest ();
463 var result = test.run ();