summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJens Georg <jensg@openismus.com>2012-10-05 09:27:28 +0200
committerJens Georg <jensg@openismus.com>2012-10-05 17:33:28 +0200
commit97b9b6ac76d6891ff469e99d4555959c417b21c8 (patch)
tree7e62aa675abd4f9c42a37257c6fa41284e02d1ab /tests
parentf053f312bd18bc75e20f53af41e650021e5bf0a1 (diff)
downloadrygel-97b9b6ac76d6891ff469e99d4555959c417b21c8.tar.gz
test: Add a test for media engines
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am30
-rw-r--r--tests/data/test-data.datbin0 -> 1048576 bytes
-rw-r--r--tests/rygel-media-engine-test.vala464
3 files changed, 493 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 65ca376a..662724cb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -43,10 +43,13 @@ check_PROGRAMS = rygel-http-item-uri-test \
rygel-searchable-container-test \
rygel-item-creator-test \
rygel-user-config-test \
- rygel-regression
+ rygel-regression \
+ rygel-media-engine-test
TESTS = $(check_PROGRAMS)
+EXTRA_DIST = $(srcdir)/data/test-data.dat
+
rygel_http_item_uri_test_SOURCES = rygel-http-item-uri-test.vala \
rygel-http-item-uri.vala
@@ -131,6 +134,31 @@ rygel_regression_LDADD = \
$(top_builddir)/src/librygel-server/librygel-server-1.0.la \
$(top_builddir)/src/librygel-core/librygel-core-1.0.la
+rygel_media_engine_test_SOURCES = \
+ rygel-media-engine-test.vala
+
+rygel_media_engine_test_VALAFLAGS = \
+ $(AM_VALAFLAGS) \
+ --pkg rygel-server-1.0 \
+ --pkg rygel-core-1.0 \
+ --vapidir $(top_builddir)/src/librygel-server \
+ --vapidir $(top_builddir)/src/librygel-core
+
+rygel_media_engine_test_CFLAGS = \
+ $(AM_CFLAGS) \
+ -DTEST_DATA_FOLDER='"$(abs_srcdir)/data"' \
+ -DTEST_ENGINE_PATH='"$(abs_top_builddir)/src/media-engines"' \
+ -DBUILT_ENGINES='"@BUILT_ENGINES@"' \
+ -I$(top_builddir)/src/librygel-server \
+ -I$(top_srcdir)/src/librygel-server \
+ -I$(top_builddir)/src/librygel-core \
+ -I$(top_srcdir)/src/librygel-core
+
+rygel_media_engine_test_LDADD = \
+ $(LDADD) \
+ $(top_builddir)/src/librygel-server/librygel-server-1.0.la \
+ $(top_builddir)/src/librygel-core/librygel-core-1.0.la
+
if HAVE_GSTREAMER
check_PROGRAMS += \
rygel-playbin-renderer-test \
diff --git a/tests/data/test-data.dat b/tests/data/test-data.dat
new file mode 100644
index 00000000..a3a2a5cf
--- /dev/null
+++ b/tests/data/test-data.dat
Binary files differ
diff --git a/tests/rygel-media-engine-test.vala b/tests/rygel-media-engine-test.vala
new file mode 100644
index 00000000..6345a2a8
--- /dev/null
+++ b/tests/rygel-media-engine-test.vala
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2012 Intel Corporation.
+ *
+ * Author: Jens Georg <jensg@openismus.com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Rygel is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+[CCode (cname="TEST_DATA_FOLDER")]
+extern const string TEST_DATA_FOLDER;
+
+// Always test locally built engines
+[CCode (cname="TEST_ENGINE_PATH")]
+extern const string TEST_ENGINE_PATH;
+
+[CCode (cname="BUILT_ENGINES")]
+extern const string BUILT_ENGINES;
+
+/**
+ * Helper class to convince the engine loader to load the media engine we want
+ * to test.
+ */
+internal class Rygel.DataSourceTestConfig : Rygel.BaseConfiguration {
+ private string engine;
+ private string path;
+
+ public DataSourceTestConfig (string? path = null,
+ string? engine = null) {
+ this.engine = engine;
+ this.path = path;
+ }
+
+ public override string get_media_engine () throws Error {
+ if (this.engine != null) {
+ return this.engine;
+ }
+
+ // Throw error
+ return base.get_media_engine ();
+ }
+
+ public override string get_engine_path () throws Error {
+ if (this.path != null) {
+ return this.path;
+ }
+
+ return TEST_ENGINE_PATH;
+ }
+
+ public string to_string () {
+ return "Path: %s, Engine: %s".printf (this.path, this.engine);
+ }
+
+ public void clear () {
+ this.engine = null;
+ this.path = null;
+ }
+}
+
+/**
+ * Stub implementation of Rygel.HTTPSeek
+ */
+internal class Rygel.ByteSeek : Rygel.HTTPSeek {
+ public ByteSeek (int64 first, int64 last, int64 length) {
+ var msg = new Soup.Message ("GET", "http://example.com/");
+ base (msg, first, last, 1, length);
+ this.seek_type = HTTPSeekType.BYTE;
+ }
+
+ public override void add_response_headers () {}
+}
+
+/**
+ * Wrapper class arount uint8[] arrays to help stuff those buffers into a
+ * Gee.ArrayList
+ */
+class DataBlock {
+ public uint8[] data;
+
+ public DataBlock (uint8[] data) {
+ this.data = data;
+ }
+}
+
+/**
+ * Helper class to collect a number of byte buffers
+ */
+internal class Rygel.DataPool : Gee.ArrayList<DataBlock> {
+ public uint8[] flatten () {
+ var size = this.total_size ();
+ var result = new uint8[size];
+ var offset = 0;
+
+ foreach (var data in this) {
+ Memory.copy (result + offset, (void *) data.data, data.data.length);
+ offset += data.data.length;
+ }
+
+ this.clear ();
+ this.add (new DataBlock (result));
+
+ return result;
+ }
+
+ public uint64 total_size () {
+ uint64 total = 0;
+ foreach (var data in this) {
+ total += data.data.length;
+ }
+
+ return total;
+ }
+}
+
+/**
+ * Test a DataSource implementation against the expectations of the interface
+ *
+ * It is run as part of the test suite but can be used to check arbitrary
+ * media engines as well:
+ *
+ * rygel-media-engine-test /path/to/my/first/custom-rygel-engine.so \
+ * /path/to/my/second/custom-rygel-engine.so ...
+ */
+public class Rygel.DataSourceTest : Object {
+ private File test_data_file;
+ private MappedFile test_data_mapped;
+
+ public DataSourceTest () {
+ var path = Path.build_filename (TEST_DATA_FOLDER, "test-data.dat");
+ this.test_data_file = File.new_for_path (path);
+ try {
+ this.test_data_mapped = new MappedFile (path, false);
+ } catch (Error error) {
+ warning ("Error: Could not map file: %s", error.message);
+ assert_not_reached ();
+ }
+ }
+
+ /// Get the whole file
+ private void test_simple_streaming () {
+ debug ("test_simple_streaming");
+ var source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ // Sources should support file:// urls
+ assert (source != null);
+
+ uint64 received_bytes = 0;
+ var loop = new MainLoop (null, false);
+ source.data_available.connect ( (data) => {
+ received_bytes += data.length;
+ });
+ source.done.connect ( (data) => {
+ loop.quit ();
+ });
+ Idle.add ( () => { source.start (null); return false; });
+ loop.run ();
+ assert (received_bytes == this.test_data_mapped.get_length ());
+ source.stop ();
+ source = null;
+ }
+
+ /// Simple byte range request tests
+ private void test_byte_range_request () {
+ debug ("test_byte_range_request");
+ var source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ // Sources should support file:// urls
+ assert (source != null);
+
+ try {
+ // Get the first 10 bytes
+ var seek = new ByteSeek (0, 9, this.test_data_mapped.get_length ());
+
+ var received_data = new DataPool ();
+ var loop = new MainLoop (null, false);
+ source.data_available.connect ( (data) => {
+ received_data.add (new DataBlock (data));
+ });
+ source.done.connect ( (data) => {
+ loop.quit ();
+ });
+ source.error.connect ( () => { assert_not_reached (); });
+ source.start (seek);
+ loop.run ();
+ assert (received_data.total_size () == 10);
+ Memory.cmp (this.test_data_mapped.get_contents (),
+ received_data.flatten (),
+ (size_t) received_data.total_size ());
+
+ // Get last 10 bytes
+ seek = new ByteSeek (this.test_data_mapped.get_length () - 10,
+ this.test_data_mapped.get_length () - 1,
+ this.test_data_mapped.get_length ());
+
+ received_data = new DataPool ();
+ loop = new MainLoop (null, false);
+
+ source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ source.data_available.connect ( (data) => {
+ received_data.add (new DataBlock (data));
+ });
+ source.done.connect ( (data) => {
+ loop.quit ();
+ });
+ source.error.connect ( () => { assert_not_reached (); });
+ source.start (seek);
+ loop.run ();
+
+ assert (received_data.total_size () == 10);
+ Memory.cmp (this.test_data_mapped.get_contents () +
+ (this.test_data_mapped.get_length () - 10),
+ received_data.flatten (),
+ (size_t) received_data.total_size ());
+
+ // Get something from the middle
+ seek = new ByteSeek (this.test_data_mapped.get_length () / 2,
+ (this.test_data_mapped.get_length () / 2) + 9,
+ this.test_data_mapped.get_length ());
+
+ received_data = new DataPool ();
+ loop = new MainLoop (null, false);
+
+ source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ source.data_available.connect ( (data) => {
+ received_data.add (new DataBlock (data));
+ });
+
+ source.done.connect ( (data) => {
+ loop.quit ();
+ });
+ source.error.connect ( () => { assert_not_reached (); });
+ source.start (seek);
+ loop.run ();
+
+ assert (received_data.total_size () == 10);
+ Memory.cmp (this.test_data_mapped.get_contents () +
+ (this.test_data_mapped.get_length () / 2),
+ received_data.flatten (),
+ (size_t) received_data.total_size ());
+ source.stop ();
+ source = null;
+ } catch (DataSourceError.SEEK_FAILED seek_error) {
+ debug ("Skipping seek test");
+ } catch (Error error) {
+ warning ("Failed to test: %s", error.message);
+ assert_not_reached ();
+ }
+ }
+
+ // Check that calling start() after stop() starts at the beginning of the
+ // data
+ private void test_stop_start () {
+ debug ("test_stop_start");
+ var source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ // Sources should support file:// urls
+ assert (source != null);
+
+ var pool = new DataPool ();
+ var loop = new MainLoop (null, false);
+ source.data_available.connect ( (data) => {
+ pool.add (new DataBlock (data));
+ source.stop ();
+ });
+ source.done.connect ( (data) => {
+ loop.quit ();
+ });
+ Idle.add ( () => { source.start (null); return false; });
+ loop.run ();
+ pool.clear ();
+ Idle.add ( () => { source.start (null); return false; });
+ loop.run ();
+ Memory.cmp (this.test_data_mapped.get_contents (),
+ pool.flatten (),
+ (size_t) pool.total_size ());
+
+ source.stop ();
+ source = null;
+ }
+
+ // Check that calling freeze multiple times only needs one thaw to get the
+ // data again
+ private void test_multiple_freeze () {
+ debug ("test_multiple_freeze");
+ var source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ // Sources should support file:// urls
+ assert (source != null);
+ var available_id = source.data_available.connect ( () => {
+ assert_not_reached ();
+ });
+ source.start (null);
+ source.freeze ();
+ source.freeze ();
+ var loop = new MainLoop (null, false);
+
+ Timeout.add_seconds (5, () => {
+ loop.quit ();
+
+ return false;
+ });
+
+ loop.run ();
+ source.disconnect (available_id);
+ source.data_available.connect ( () => {
+ loop.quit ();
+ });
+
+ var timeout_id = Timeout.add_seconds (5, () => {
+ assert_not_reached ();
+
+ return false;
+ });
+
+ source.thaw ();
+ loop.run ();
+ Source.remove (timeout_id);
+ source.stop ();
+ }
+
+ // Check that it is possible to call stop() when the source is frozen and
+ // still get a done() signal
+ private void test_freeze_stop () {
+ debug ("test_freeze_stop");
+ var source = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ // Sources should support file:// urls
+ assert (source != null);
+
+ source.start (null);
+ source.freeze ();
+ var loop = new MainLoop (null, false);
+ source.done.connect ( () => {
+ loop.quit ();
+ });
+ var id = Timeout.add_seconds ( 5, () => {
+ assert_not_reached ();
+ });
+ Idle.add ( () => { source.stop (); return false; });
+ loop.run ();
+ Source.remove (id);
+ source.stop ();
+ source = null;
+ }
+
+ // Check that it is possible to stream to two targets in parallel
+ public void test_parallel_streaming () {
+ debug ("test_parallel_streaming");
+ var source1 = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ assert (source1 != null);
+ // Sources should support file:// urls
+ var source2 = MediaEngine.get_default ().create_data_source
+ (this.test_data_file.get_uri ());
+ assert (source2 != null);
+
+ source1.start (null);
+
+ var seek = new ByteSeek (0,
+ (this.test_data_mapped.get_length () / 2),
+ this.test_data_mapped.get_length ());
+
+ source2.start (seek);
+ var loop = new MainLoop (null, false);
+ var quit = false;
+ source1.done.connect ( () => {
+ if (quit) {
+ loop.quit ();
+ } else {
+ quit = true;
+ }
+ });
+
+ source2.done.connect ( () => {
+ if (quit) {
+ loop.quit ();
+ } else {
+ quit = true;
+ }
+ });
+ loop.run ();
+ }
+
+ public int run () {
+ this.test_simple_streaming ();
+ this.test_byte_range_request ();
+ this.test_stop_start ();
+ this.test_multiple_freeze ();
+ this.test_freeze_stop ();
+ this.test_parallel_streaming ();
+
+ return 0;
+ }
+
+ public static int main (string[] args) {
+ string[] engines;
+
+ var configs = new Gee.ArrayList<DataSourceTestConfig> ();
+
+ if (args.length > 1) {
+ foreach (var arg in args) {
+ File file;
+ if (args[1].has_prefix ("~")) {
+ file = File.parse_name (args[1]);
+ } else {
+ file = File.new_for_commandline_arg (args[1]);
+ }
+ var path = file.get_parent ().get_path ();
+ var engine = file.get_basename ();
+
+ configs.add (new DataSourceTestConfig (path, engine));
+ }
+ } else {
+ foreach (var engine in BUILT_ENGINES.split (";")) {
+ var name = engine + "." + Module.SUFFIX;
+ configs.add (new DataSourceTestConfig (null, name));
+ }
+ }
+
+ DataSourceTestConfig previous_config = null;
+ foreach (var config in configs) {
+ // Invalidate previous config so MetaConfig picks up the
+ // current one
+ if (previous_config != null) {
+ previous_config.clear ();
+ }
+
+ debug ("=> Executing tests for config %s", config.to_string ());
+ MetaConfig.register_configuration (config);
+ previous_config = config;
+
+ try {
+ MediaEngine.init ();
+ } catch (Error error) {
+ assert_not_reached ();
+ }
+
+ var test = new DataSourceTest ();
+
+ var result = test.run ();
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ return 0;
+ }
+}