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/");
80 base (msg, first, last, 1, length);
81 this.seek_type = HTTPSeekType.BYTE;
84 public override void add_response_headers () {}
88 * Wrapper class arount uint8[] arrays to help stuff those buffers into a
94 public DataBlock (uint8[] data) {
100 * Helper class to collect a number of byte buffers
102 internal class Rygel.DataPool : Gee.ArrayList<DataBlock> {
103 public uint8[] flatten () {
104 var size = this.total_size ();
105 var result = new uint8[size];
108 foreach (var data in this) {
109 Memory.copy (result + offset, (void *) data.data, data.data.length);
110 offset += data.data.length;
114 this.add (new DataBlock (result));
119 public uint64 total_size () {
121 foreach (var data in this) {
122 total += data.data.length;
130 * Test a DataSource implementation against the expectations of the interface
132 * It is run as part of the test suite but can be used to check arbitrary
133 * media engines as well:
135 * rygel-media-engine-test /path/to/my/first/custom-rygel-engine.so \
136 * /path/to/my/second/custom-rygel-engine.so ...
138 public class Rygel.DataSourceTest : Object {
139 private File test_data_file;
140 private MappedFile test_data_mapped;
142 public DataSourceTest () {
143 var path = Path.build_filename (TEST_DATA_FOLDER, "test-data.dat");
144 this.test_data_file = File.new_for_path (path);
146 this.test_data_mapped = new MappedFile (path, false);
147 } catch (Error error) {
148 warning ("Error: Could not map file: %s", error.message);
149 assert_not_reached ();
153 /// Get the whole file
154 private void test_simple_streaming () {
155 debug ("test_simple_streaming");
156 var source = MediaEngine.get_default ().create_data_source
157 (this.test_data_file.get_uri ());
158 // Sources should support file:// urls
159 assert (source != null);
161 uint64 received_bytes = 0;
162 var loop = new MainLoop (null, false);
163 source.data_available.connect ( (data) => {
164 received_bytes += data.length;
166 source.done.connect ( (data) => {
169 Idle.add ( () => { source.start (null); return false; });
171 assert (received_bytes == this.test_data_mapped.get_length ());
176 /// Simple byte range request tests
177 private void test_byte_range_request () {
178 debug ("test_byte_range_request");
179 var source = MediaEngine.get_default ().create_data_source
180 (this.test_data_file.get_uri ());
181 // Sources should support file:// urls
182 assert (source != null);
185 // Get the first 10 bytes
186 var seek = new ByteSeek (0, 9, this.test_data_mapped.get_length ());
188 var received_data = new DataPool ();
189 var loop = new MainLoop (null, false);
190 source.data_available.connect ( (data) => {
191 received_data.add (new DataBlock (data));
193 source.done.connect ( (data) => {
196 source.error.connect ( () => { assert_not_reached (); });
199 assert (received_data.total_size () == 10);
200 Memory.cmp (this.test_data_mapped.get_contents (),
201 received_data.flatten (),
202 (size_t) received_data.total_size ());
205 seek = new ByteSeek (this.test_data_mapped.get_length () - 10,
206 this.test_data_mapped.get_length () - 1,
207 this.test_data_mapped.get_length ());
209 received_data = new DataPool ();
210 loop = new MainLoop (null, false);
212 source = MediaEngine.get_default ().create_data_source
213 (this.test_data_file.get_uri ());
214 source.data_available.connect ( (data) => {
215 received_data.add (new DataBlock (data));
217 source.done.connect ( (data) => {
220 source.error.connect ( () => { assert_not_reached (); });
224 assert (received_data.total_size () == 10);
225 Memory.cmp (this.test_data_mapped.get_contents () +
226 (this.test_data_mapped.get_length () - 10),
227 received_data.flatten (),
228 (size_t) received_data.total_size ());
230 // Get something from the middle
231 seek = new ByteSeek (this.test_data_mapped.get_length () / 2,
232 (this.test_data_mapped.get_length () / 2) + 9,
233 this.test_data_mapped.get_length ());
235 received_data = new DataPool ();
236 loop = new MainLoop (null, false);
238 source = MediaEngine.get_default ().create_data_source
239 (this.test_data_file.get_uri ());
240 source.data_available.connect ( (data) => {
241 received_data.add (new DataBlock (data));
244 source.done.connect ( (data) => {
247 source.error.connect ( () => { assert_not_reached (); });
251 assert (received_data.total_size () == 10);
252 Memory.cmp (this.test_data_mapped.get_contents () +
253 (this.test_data_mapped.get_length () / 2),
254 received_data.flatten (),
255 (size_t) received_data.total_size ());
258 } catch (DataSourceError.SEEK_FAILED seek_error) {
259 debug ("Skipping seek test");
260 } catch (Error error) {
261 warning ("Failed to test: %s", error.message);
262 assert_not_reached ();
266 // Check that calling start() after stop() starts at the beginning of the
268 private void test_stop_start () {
269 debug ("test_stop_start");
270 var source = MediaEngine.get_default ().create_data_source
271 (this.test_data_file.get_uri ());
272 // Sources should support file:// urls
273 assert (source != null);
275 var pool = new DataPool ();
276 var loop = new MainLoop (null, false);
277 source.data_available.connect ( (data) => {
278 pool.add (new DataBlock (data));
281 source.done.connect ( (data) => {
284 Idle.add ( () => { source.start (null); return false; });
287 Idle.add ( () => { source.start (null); return false; });
289 Memory.cmp (this.test_data_mapped.get_contents (),
291 (size_t) pool.total_size ());
297 // Check that calling freeze multiple times only needs one thaw to get the
299 private void test_multiple_freeze () {
300 debug ("test_multiple_freeze");
301 var source = MediaEngine.get_default ().create_data_source
302 (this.test_data_file.get_uri ());
303 // Sources should support file:// urls
304 assert (source != null);
305 var available_id = source.data_available.connect ( () => {
306 assert_not_reached ();
311 var loop = new MainLoop (null, false);
313 Timeout.add_seconds (5, () => {
320 source.disconnect (available_id);
321 source.data_available.connect ( () => {
325 var timeout_id = Timeout.add_seconds (5, () => {
326 assert_not_reached ();
333 Source.remove (timeout_id);
337 // Check that it is possible to call stop() when the source is frozen and
338 // still get a done() signal
339 private void test_freeze_stop () {
340 debug ("test_freeze_stop");
341 var source = MediaEngine.get_default ().create_data_source
342 (this.test_data_file.get_uri ());
343 // Sources should support file:// urls
344 assert (source != null);
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);
373 source1.start (null);
375 var seek = new ByteSeek (0,
376 (this.test_data_mapped.get_length () / 2),
377 this.test_data_mapped.get_length ());
379 source2.start (seek);
380 var loop = new MainLoop (null, false);
382 source1.done.connect ( () => {
390 source2.done.connect ( () => {
401 this.test_simple_streaming ();
402 this.test_byte_range_request ();
403 this.test_stop_start ();
404 this.test_multiple_freeze ();
405 this.test_freeze_stop ();
406 this.test_parallel_streaming ();
411 public static int main (string[] args) {
414 var configs = new Gee.ArrayList<DataSourceTestConfig> ();
416 if (args.length > 1) {
417 foreach (var arg in args) {
419 if (args[1].has_prefix ("~")) {
420 file = File.parse_name (args[1]);
422 file = File.new_for_commandline_arg (args[1]);
424 var path = file.get_parent ().get_path ();
425 var engine = file.get_basename ();
427 configs.add (new DataSourceTestConfig (path, engine));
430 foreach (var engine in BUILT_ENGINES.split (";")) {
431 var name = engine + "." + Module.SUFFIX;
432 configs.add (new DataSourceTestConfig (null, name));
436 DataSourceTestConfig previous_config = null;
437 foreach (var config in configs) {
438 // Invalidate previous config so MetaConfig picks up the
440 if (previous_config != null) {
441 previous_config.clear ();
444 debug ("=> Executing tests for config %s", config.to_string ());
445 MetaConfig.register_configuration (config);
446 previous_config = config;
450 } catch (Error error) {
451 assert_not_reached ();
454 var test = new DataSourceTest ();
456 var result = test.run ();