rtsp: add unit test
authorDavid Svensson Fors <davidsf@axis.com>
Tue, 19 Jun 2012 13:25:36 +0000 (15:25 +0200)
committerWim Taymans <wim.taymans@collabora.co.uk>
Tue, 19 Jun 2012 13:25:36 +0000 (15:25 +0200)
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=678076

configure.ac
tests/Makefile.am
tests/check/Makefile.am [new file with mode: 0644]
tests/check/gst/rtspserver.c [new file with mode: 0644]

index 50fe7a4..c61282f 100644 (file)
@@ -44,6 +44,7 @@ AS_LIBTOOL(GST, 0, 0, 0)
 dnl *** required versions of GStreamer stuff ***
 GST_REQ=0.11.0
 GSTPB_REQ=0.11.0
+GSTPG_REQ=0.11.0
 
 dnl *** autotools stuff ****
 
@@ -142,6 +143,11 @@ GSTPB_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-base-$GST_API_VERSION --variabl
 AC_SUBST(GSTPB_PLUGINS_DIR)
 AC_MSG_NOTICE(Using GStreamer Base Plugins in $GSTPB_PLUGINS_DIR)
 
+AG_GST_CHECK_GST_PLUGINS_GOOD($GST_API_VERSION, [$GSTPG_REQ], [yes])
+GSTPG_PLUGINS_DIR=`$PKG_CONFIG gstreamer-plugins-good-$GST_API_VERSION --variable pluginsdir`
+AC_SUBST(GSTPG_PLUGINS_DIR)
+AC_MSG_NOTICE(Using GStreamer Good Plugins in $GSTPG_PLUGINS_DIR)
+
 AG_GST_CHECK_GST_CHECK($GST_API_VERSION, [$GST_REQ], no)
 
 dnl FIXME: get rid of this by making sure gstreamer-check brings it in
@@ -250,6 +256,7 @@ gst/Makefile
 gst/rtsp-server/Makefile
 examples/Makefile
 tests/Makefile
+tests/check/Makefile
 bindings/Makefile
 bindings/vala/Makefile
 pkgconfig/Makefile
index ce9caf3..2c84540 100644 (file)
@@ -7,3 +7,14 @@ AM_LDFLAGS = \
        $(GST_LIBS) \
        $(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_API_VERSION@.la
 
+if HAVE_CHECK
+SUBDIRS_CHECK = check
+else
+SUBDIRS_CHECK =
+endif
+
+SUBDIRS =                      \
+       $(SUBDIRS_CHECK)
+
+DIST_SUBDIRS =                         \
+       check
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
new file mode 100644 (file)
index 0000000..af5f6c1
--- /dev/null
@@ -0,0 +1,54 @@
+include $(top_srcdir)/common/check.mak
+
+CHECK_REGISTRY = $(top_builddir)/tests/check/test-registry.reg
+TEST_FILES_DIRECTORY = $(top_srcdir)/tests/files
+
+REGISTRY_ENVIRONMENT = \
+       GST_REGISTRY=$(CHECK_REGISTRY)
+
+TESTS_ENVIRONMENT = \
+        CK_DEFAULT_TIMEOUT=120                                  \
+        GST_STATE_IGNORE_ELEMENTS="$(STATE_IGNORE_ELEMENTS)"   \
+       $(REGISTRY_ENVIRONMENT)                                 \
+       GST_PLUGIN_SYSTEM_PATH=                                 \
+       GST_PLUGIN_PATH=$(GST_PLUGINS_DIR):$(GSTPB_PLUGINS_DIR):$(GSTPG_PLUGINS_DIR)    \
+       GST_PLUGIN_LOADING_WHITELIST="gstreamer:gst-plugins-base:gst-plugins-good"
+
+
+# ths core dumps of some machines have PIDs appended
+CLEANFILES = core.* test-registry.*
+
+clean-local: clean-local-check
+
+$(CHECK_REGISTRY):
+       $(TESTS_ENVIRONMENT)
+
+TESTS = $(check_PROGRAMS)
+
+check_PROGRAMS = \
+       gst/rtspserver
+
+# these tests don't even pass
+noinst_PROGRAMS =
+
+AM_CFLAGS = $(GST_CFLAGS) $(GST_CHECK_CFLAGS) \
+       -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" \
+       -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS
+AM_CXXFLAGS = $(GST_CXXFLAGS) $(GST_CHECK_CFLAGS) \
+       -DGST_TEST_FILES_PATH="\"$(TEST_FILES_DIRECTORY)\"" \
+       -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS
+LDADD = $(GST_LIBS) $(GST_CHECK_LIBS) $(GST_RTSP_SERVER_LIBS)
+
+SUPPRESSIONS = $(top_srcdir)/common/gst.supp
+
+gst_rtspserver_CFLAGS = \
+       $(GST_PLUGINS_GOOD_CFLAGS) \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(GST_BASE_CFLAGS) \
+       $(AM_CFLAGS)
+
+gst_rtspserver_LDADD = \
+       $(top_builddir)/gst/rtsp-server/libgstrtspserver-@GST_API_VERSION@.la \
+       $(GST_PLUGINS_GOOD_LIBS) \
+       $(GST_BASE_LIBS) -lgstrtsp-@GST_API_VERSION@ -lgstsdp-@GST_API_VERSION@ \
+       $(LDADD)
diff --git a/tests/check/gst/rtspserver.c b/tests/check/gst/rtspserver.c
new file mode 100644 (file)
index 0000000..5d1bdb0
--- /dev/null
@@ -0,0 +1,710 @@
+/* GStreamer
+ *
+ * unit test for GstRTSPServer
+ *
+ * Copyright (C) 2012 Axis Communications <dev-gstreamer at axis dot com>
+ * @author David Svensson Fors <davidsf at axis dot com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/rtsp-server/rtsp-server.h>
+#include <gst/sdp/gstsdpmessage.h>
+
+#include <stdio.h>
+#include <netinet/in.h>
+
+#define VIDEO_PIPELINE "videotestsrc ! " \
+  "video/x-raw,width=352,height=288 ! " \
+  "rtpgstpay name=pay0 pt=96"
+#define AUDIO_PIPELINE "audiotestsrc ! " \
+  "audio/x-raw,rate=8000 ! " \
+  "rtpgstpay name=pay1 pt=97"
+
+#define TEST_MOUNT_POINT  "/test"
+#define TEST_PROTO        "RTP/AVP"
+#define TEST_ENCODING     "X-GST"
+#define TEST_CLOCK_RATE   "90000"
+
+/* tested rtsp server */
+static GstRTSPServer *server = NULL;
+
+/* tcp port that the test server listens for rtsp requests on */
+static gint test_port = 0;
+
+/* id of the server's source within the GMainContext */
+static guint source_id;
+
+/* iterate the default main loop until there are no events to dispatch */
+static void
+iterate (void)
+{
+  while (g_main_context_iteration (NULL, FALSE)) {
+    GST_DEBUG ("iteration");
+  }
+}
+
+/* returns an unused port that can be used by the test */
+static int
+get_unused_port (gint type)
+{
+  int sock;
+  struct sockaddr_in addr;
+  socklen_t addr_len;
+  gint port;
+
+  /* create socket */
+  fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
+
+  /* pass port 0 to bind, which will bind to any free port */
+  memset (&addr, 0, sizeof addr);
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = INADDR_ANY;
+  addr.sin_port = htons (0);
+  fail_unless (bind (sock, (struct sockaddr *) &addr, sizeof addr) == 0);
+
+  /* ask what port was bound using getsockname */
+  addr_len = sizeof addr;
+  memset (&addr, 0, addr_len);
+  fail_unless (getsockname (sock, (struct sockaddr *) &addr, &addr_len) == 0);
+  port = ntohs (addr.sin_port);
+
+  /* close the socket so the port gets unbound again (and can be used by the
+   * test) */
+  close (sock);
+
+  return port;
+}
+
+/* returns TRUE if the given port is not currently bound */
+static gboolean
+port_is_unused (gint port, gint type)
+{
+  int sock;
+  struct sockaddr_in addr;
+  gboolean is_bound;
+
+  /* create socket */
+  fail_unless ((sock = socket (AF_INET, type, 0)) > 0);
+
+  /* check if the port is already bound by trying to bind to it (again) */
+  memset (&addr, 0, sizeof addr);
+  addr.sin_family = AF_INET;
+  addr.sin_addr.s_addr = INADDR_ANY;
+  addr.sin_port = htons (port);
+  is_bound = (bind (sock, (struct sockaddr *) &addr, sizeof addr) != 0);
+
+  /* close the socket, which will unbind if bound by our call to bind */
+  close (sock);
+
+  return !is_bound;
+}
+
+/* get a free rtp/rtcp client port pair */
+static void
+get_client_ports (GstRTSPRange * range)
+{
+  gint rtp_port;
+  gint rtcp_port;
+
+  /* get a pair of unused ports, where the rtp port is even */
+  do {
+    rtp_port = get_unused_port (SOCK_DGRAM);
+    rtcp_port = rtp_port + 1;
+  } while (rtp_port % 2 != 0 || !port_is_unused (rtcp_port, SOCK_DGRAM));
+  range->min = rtp_port;
+  range->max = rtcp_port;
+  GST_DEBUG ("client_port=%d-%d", range->min, range->max);
+}
+
+/* start the tested rtsp server */
+static void
+start_server ()
+{
+  GstRTSPMediaMapping *mapping;
+  gchar *service;
+  GstRTSPMediaFactory *factory;
+
+  mapping = gst_rtsp_server_get_media_mapping (server);
+
+  factory = gst_rtsp_media_factory_new ();
+
+  gst_rtsp_media_factory_set_launch (factory,
+      "( " VIDEO_PIPELINE "  " AUDIO_PIPELINE " )");
+
+  gst_rtsp_media_mapping_add_factory (mapping, TEST_MOUNT_POINT, factory);
+  g_object_unref (mapping);
+
+  /* set port */
+  test_port = get_unused_port (SOCK_STREAM);
+  service = g_strdup_printf ("%d", test_port);
+  gst_rtsp_server_set_service (server, service);
+  g_free (service);
+
+  /* attach to default main context */
+  source_id = gst_rtsp_server_attach (server, NULL);
+  fail_if (source_id == 0);
+
+  GST_DEBUG ("rtsp server listening on port %d", test_port);
+}
+
+/* stop the tested rtsp server */
+static void
+stop_server ()
+{
+  g_source_remove (source_id);
+  source_id = 0;
+
+  GST_DEBUG ("rtsp server stopped");
+}
+
+/* create an rtsp connection to the server on test_port */
+static GstRTSPConnection *
+connect_to_server (gint port, const gchar * mount_point)
+{
+  GstRTSPConnection *conn = NULL;
+  gchar *address;
+  gchar *uri_string;
+  GstRTSPUrl *url = NULL;
+
+  address = gst_rtsp_server_get_address (server);
+  uri_string = g_strdup_printf ("rtsp://%s:%d%s", address, port, mount_point);
+  g_free (address);
+  gst_rtsp_url_parse (uri_string, &url);
+  g_free (uri_string);
+
+  fail_unless (gst_rtsp_connection_create (url, &conn) == GST_RTSP_OK);
+  gst_rtsp_url_free (url);
+
+  fail_unless (gst_rtsp_connection_connect (conn, NULL) == GST_RTSP_OK);
+
+  return conn;
+}
+
+/* create an rtsp request */
+static GstRTSPMessage *
+create_request (GstRTSPConnection * conn, GstRTSPMethod method,
+    const gchar * control)
+{
+  GstRTSPMessage *request = NULL;
+  gchar *base_uri;
+  gchar *full_uri;
+
+  base_uri = gst_rtsp_url_get_request_uri (gst_rtsp_connection_get_url (conn));
+  full_uri = g_strdup_printf ("%s/%s", base_uri, control ? control : "");
+  g_free (base_uri);
+  if (gst_rtsp_message_new_request (&request, method, full_uri) != GST_RTSP_OK) {
+    GST_DEBUG ("failed to create request object");
+    g_free (full_uri);
+    return NULL;
+  }
+  g_free (full_uri);
+  return request;
+}
+
+/* send an rtsp request */
+static gboolean
+send_request (GstRTSPConnection * conn, GstRTSPMessage * request)
+{
+  if (gst_rtsp_connection_send (conn, request, NULL) != GST_RTSP_OK) {
+    GST_DEBUG ("failed to send request");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/* read rtsp response. response must be freed by the caller */
+static GstRTSPMessage *
+read_response (GstRTSPConnection * conn)
+{
+  GstRTSPMessage *response = NULL;
+
+  if (gst_rtsp_message_new (&response) != GST_RTSP_OK) {
+    GST_DEBUG ("failed to create response object");
+    return NULL;
+  }
+  if (gst_rtsp_connection_receive (conn, response, NULL) != GST_RTSP_OK) {
+    GST_DEBUG ("failed to read response");
+    gst_rtsp_message_free (response);
+    return NULL;
+  }
+  fail_unless (gst_rtsp_message_get_type (response) ==
+      GST_RTSP_MESSAGE_RESPONSE);
+  return response;
+}
+
+/* send an rtsp request and receive response. gchar** parameters are out
+ * parameters that have to be freed by the caller */
+static GstRTSPStatusCode
+do_request (GstRTSPConnection * conn, GstRTSPMethod method,
+    const gchar * control, const gchar * session_in, const gchar * transport_in,
+    gchar ** content_type, gchar ** content_base, gchar ** body,
+    gchar ** session_out, gchar ** transport_out)
+{
+  GstRTSPMessage *request;
+  GstRTSPMessage *response;
+  GstRTSPStatusCode code;
+  gchar *value;
+
+  /* create request */
+  request = create_request (conn, method, control);
+
+  /* add headers */
+  if (session_in) {
+    gst_rtsp_message_add_header (request, GST_RTSP_HDR_SESSION, session_in);
+  }
+  if (transport_in) {
+    gst_rtsp_message_add_header (request, GST_RTSP_HDR_TRANSPORT, transport_in);
+  }
+
+  /* send request */
+  fail_unless (send_request (conn, request));
+  gst_rtsp_message_free (request);
+
+  iterate ();
+
+  /* read response */
+  response = read_response (conn);
+
+  /* check status line */
+  gst_rtsp_message_parse_response (response, &code, NULL, NULL);
+  if (code != GST_RTSP_STS_OK) {
+    gst_rtsp_message_free (response);
+    return code;
+  }
+
+  /* get information from response */
+  if (content_type) {
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_TYPE,
+        &value, 0);
+    *content_type = g_strdup (value);
+  }
+  if (content_base) {
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_CONTENT_BASE,
+        &value, 0);
+    *content_base = g_strdup (value);
+  }
+  if (body) {
+    *body = g_malloc (response->body_size + 1);
+    strncpy (*body, (gchar *) response->body, response->body_size);
+  }
+  if (session_out) {
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_SESSION, &value, 0);
+    if (session_in) {
+      /* check that we got the same session back */
+      fail_unless (!g_strcmp0 (value, session_in));
+    }
+    *session_out = g_strdup (value);
+  }
+  if (transport_out) {
+    gst_rtsp_message_get_header (response, GST_RTSP_HDR_TRANSPORT, &value, 0);
+    *transport_out = g_strdup (value);
+  }
+
+  gst_rtsp_message_free (response);
+  return code;
+}
+
+/* send an rtsp request with a method and a session, and receive response */
+static GstRTSPStatusCode
+do_simple_request (GstRTSPConnection * conn, GstRTSPMethod method,
+    const gchar * session)
+{
+  return do_request (conn, method, NULL, session, NULL, NULL, NULL,
+      NULL, NULL, NULL);
+}
+
+/* send a DESCRIBE request and receive response. returns a received
+ * GstSDPMessage that must be freed by the caller */
+static GstSDPMessage *
+do_describe (GstRTSPConnection * conn, const gchar * mount_point)
+{
+  GstSDPMessage *sdp_message;
+  gchar *content_type;
+  gchar *content_base;
+  gchar *body;
+  gchar *address;
+  gchar *expected_content_base;
+
+  /* send DESCRIBE request */
+  fail_unless (do_request (conn, GST_RTSP_DESCRIBE, NULL, NULL, NULL,
+          &content_type, &content_base, &body, NULL, NULL) == GST_RTSP_STS_OK);
+
+  /* check response values */
+  fail_unless (!g_strcmp0 (content_type, "application/sdp"));
+  address = gst_rtsp_server_get_address (server);
+  expected_content_base =
+      g_strdup_printf ("rtsp://%s:%d%s/", address, test_port, mount_point);
+  fail_unless (!g_strcmp0 (content_base, expected_content_base));
+
+  /* create sdp message */
+  fail_unless (gst_sdp_message_new (&sdp_message) == GST_SDP_OK);
+  fail_unless (gst_sdp_message_parse_buffer ((guint8 *) body,
+          strlen (body), sdp_message) == GST_SDP_OK);
+
+  /* clean up */
+  g_free (content_type);
+  g_free (content_base);
+  g_free (body);
+  g_free (address);
+  g_free (expected_content_base);
+
+  return sdp_message;
+}
+
+/* send a SETUP request and receive response. if *session is not NULL,
+ * it is used in the request. otherwise, *session is set to a returned
+ * session string that must be freed by the caller. the returned
+ * transport must be freed by the caller. */
+static GstRTSPStatusCode
+do_setup (GstRTSPConnection * conn, const gchar * control,
+    const GstRTSPRange * client_ports, gchar ** session,
+    GstRTSPTransport ** transport)
+{
+  GstRTSPStatusCode code;
+  gchar *session_in = NULL;
+  gchar *transport_string_in = NULL;
+  gchar **session_out = NULL;
+  gchar *transport_string_out = NULL;
+
+  /* prepare and send SETUP request */
+  if (session) {
+    if (*session) {
+      session_in = *session;
+    } else {
+      session_out = session;
+    }
+  }
+  transport_string_in =
+      g_strdup_printf (TEST_PROTO ";unicast;client_port=%d-%d",
+      client_ports->min, client_ports->max);
+  code =
+      do_request (conn, GST_RTSP_SETUP, control, session_in,
+      transport_string_in, NULL, NULL, NULL, session_out,
+      &transport_string_out);
+  g_free (transport_string_in);
+
+  if (transport_string_out) {
+    /* create transport */
+    fail_unless (gst_rtsp_transport_new (transport) == GST_RTSP_OK);
+    fail_unless (gst_rtsp_transport_parse (transport_string_out,
+            *transport) == GST_RTSP_OK);
+    g_free (transport_string_out);
+  }
+
+  return code;
+}
+
+/* fixture setup function */
+static void
+setup (void)
+{
+  server = gst_rtsp_server_new ();
+}
+
+/* fixture clean-up function */
+static void
+teardown (void)
+{
+  if (server) {
+    g_object_unref (server);
+    server = NULL;
+  }
+  test_port = 0;
+}
+
+GST_START_TEST (test_connect)
+{
+  GstRTSPConnection *conn;
+
+  start_server ();
+
+  /* connect to server */
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* clean up */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+
+  /* iterate so the clean-up can finish */
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_describe)
+{
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message = NULL;
+  const GstSDPMedia *sdp_media;
+  gint32 format;
+  gchar *expected_rtpmap;
+  const gchar *rtpmap;
+  const gchar *control_video;
+  const gchar *control_audio;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* send DESCRIBE request */
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
+
+  /* check video sdp */
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO));
+  fail_unless (gst_sdp_media_formats_len (sdp_media) == 1);
+  sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT,
+      &format);
+  expected_rtpmap =
+      g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format);
+  rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap");
+  fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap));
+  g_free (expected_rtpmap);
+  control_video = gst_sdp_media_get_attribute_val (sdp_media, "control");
+  fail_unless (!g_strcmp0 (control_video, "stream=0"));
+
+  /* check audio sdp */
+  sdp_media = gst_sdp_message_get_media (sdp_message, 1);
+  fail_unless (!g_strcmp0 (gst_sdp_media_get_proto (sdp_media), TEST_PROTO));
+  fail_unless (gst_sdp_media_formats_len (sdp_media) == 1);
+  sscanf (gst_sdp_media_get_format (sdp_media, 0), "%" G_GINT32_FORMAT,
+      &format);
+  expected_rtpmap =
+      g_strdup_printf ("%d " TEST_ENCODING "/" TEST_CLOCK_RATE, format);
+  rtpmap = gst_sdp_media_get_attribute_val (sdp_media, "rtpmap");
+  fail_unless (!g_strcmp0 (rtpmap, expected_rtpmap));
+  g_free (expected_rtpmap);
+  control_audio = gst_sdp_media_get_attribute_val (sdp_media, "control");
+  fail_unless (!g_strcmp0 (control_audio, "stream=1"));
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_describe_non_existing_mount_point)
+{
+  GstRTSPConnection *conn;
+
+  start_server ();
+
+  /* send DESCRIBE request for a non-existing mount point
+   * and check that we get a 404 Not Found */
+  conn = connect_to_server (test_port, "/non-existing");
+  fail_unless (do_simple_request (conn, GST_RTSP_DESCRIBE, NULL)
+      == GST_RTSP_STS_NOT_FOUND);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup)
+{
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message = NULL;
+  const GstSDPMedia *sdp_media;
+  const gchar *video_control;
+  const gchar *audio_control;
+  GstRTSPRange client_ports;
+  gchar *session = NULL;
+  GstRTSPTransport *video_transport = NULL;
+  GstRTSPTransport *audio_transport = NULL;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  /* get control strings from DESCRIBE response */
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+  sdp_media = gst_sdp_message_get_media (sdp_message, 1);
+  audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+  get_client_ports (&client_ports);
+
+  /* send SETUP request for video */
+  fail_unless (do_setup (conn, video_control, &client_ports, &session,
+          &video_transport) == GST_RTSP_STS_OK);
+  GST_DEBUG ("set up video %s, got session '%s'", video_control, session);
+
+  /* check response from SETUP */
+  fail_unless (video_transport->trans == GST_RTSP_TRANS_RTP);
+  fail_unless (video_transport->profile == GST_RTSP_PROFILE_AVP);
+  fail_unless (video_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (video_transport->mode_play);
+  gst_rtsp_transport_free (video_transport);
+
+  /* send SETUP request for audio */
+  fail_unless (do_setup (conn, audio_control, &client_ports, &session,
+          &audio_transport) == GST_RTSP_STS_OK);
+  GST_DEBUG ("set up audio %s with session '%s'", audio_control, session);
+
+  /* check response from SETUP */
+  fail_unless (audio_transport->trans == GST_RTSP_TRANS_RTP);
+  fail_unless (audio_transport->profile == GST_RTSP_PROFILE_AVP);
+  fail_unless (audio_transport->lower_transport == GST_RTSP_LOWER_TRANS_UDP);
+  fail_unless (audio_transport->mode_play);
+  gst_rtsp_transport_free (audio_transport);
+
+  /* clean up and iterate so the clean-up can finish */
+  g_free (session);
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_setup_non_existing_stream)
+{
+  GstRTSPConnection *conn;
+  GstRTSPRange client_ports;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  get_client_ports (&client_ports);
+
+  /* send SETUP request with a non-existing stream and check that we get a
+   * 404 Not Found */
+  fail_unless (do_setup (conn, "stream=7", &client_ports, NULL,
+          NULL) == GST_RTSP_STS_NOT_FOUND);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+
+  /* need to unref the server here, otherwise threads will remain
+   * and teardown won't be run */
+  g_object_unref (server);
+  server = NULL;
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_play)
+{
+  GstRTSPConnection *conn;
+  GstSDPMessage *sdp_message = NULL;
+  const GstSDPMedia *sdp_media;
+  const gchar *video_control;
+  const gchar *audio_control;
+  GstRTSPRange client_port;
+  gchar *session = NULL;
+  GstRTSPTransport *video_transport = NULL;
+  GstRTSPTransport *audio_transport = NULL;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  sdp_message = do_describe (conn, TEST_MOUNT_POINT);
+
+  /* get control strings from DESCRIBE response */
+  fail_unless (gst_sdp_message_medias_len (sdp_message) == 2);
+  sdp_media = gst_sdp_message_get_media (sdp_message, 0);
+  video_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+  sdp_media = gst_sdp_message_get_media (sdp_message, 1);
+  audio_control = gst_sdp_media_get_attribute_val (sdp_media, "control");
+
+  get_client_ports (&client_port);
+
+  /* do SETUP for video and audio */
+  fail_unless (do_setup (conn, video_control, &client_port, &session,
+          &video_transport) == GST_RTSP_STS_OK);
+  fail_unless (do_setup (conn, audio_control, &client_port, &session,
+          &audio_transport) == GST_RTSP_STS_OK);
+
+  /* send PLAY request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
+          session) == GST_RTSP_STS_OK);
+
+  /* send TEARDOWN request and check that we get 200 OK */
+  fail_unless (do_simple_request (conn, GST_RTSP_TEARDOWN,
+          session) == GST_RTSP_STS_OK);
+
+  /* clean up and iterate so the clean-up can finish */
+  g_free (session);
+  gst_rtsp_transport_free (video_transport);
+  gst_rtsp_transport_free (audio_transport);
+  gst_sdp_message_free (sdp_message);
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_play_without_session)
+{
+  GstRTSPConnection *conn;
+
+  start_server ();
+
+  conn = connect_to_server (test_port, TEST_MOUNT_POINT);
+
+  /* send PLAY request without a session and check that we get a
+   * 454 Session Not Found */
+  fail_unless (do_simple_request (conn, GST_RTSP_PLAY,
+          NULL) == GST_RTSP_STS_SESSION_NOT_FOUND);
+
+  /* clean up and iterate so the clean-up can finish */
+  gst_rtsp_connection_free (conn);
+  stop_server ();
+  iterate ();
+}
+
+GST_END_TEST;
+
+static Suite *
+rtspserver_suite (void)
+{
+  Suite *s = suite_create ("rtspserver");
+  TCase *tc = tcase_create ("general");
+
+  suite_add_tcase (s, tc);
+  tcase_add_checked_fixture (tc, setup, teardown);
+  tcase_set_timeout (tc, 20);
+  tcase_add_test (tc, test_connect);
+  tcase_add_test (tc, test_describe);
+  tcase_add_test (tc, test_describe_non_existing_mount_point);
+  tcase_add_test (tc, test_setup);
+  tcase_add_test (tc, test_setup_non_existing_stream);
+  tcase_add_test (tc, test_play);
+  tcase_add_test (tc, test_play_without_session);
+
+  return s;
+}
+
+GST_CHECK_MAIN (rtspserver);