rtspconnection: New unit test
authorOgnyan Tonchev <ognyan@axis.com>
Wed, 19 Feb 2014 12:54:17 +0000 (13:54 +0100)
committerWim Taymans <wtaymans@redhat.com>
Fri, 21 Feb 2014 15:21:45 +0000 (16:21 +0100)
See https://bugzilla.gnome.org/show_bug.cgi?id=724720

tests/check/Makefile.am
tests/check/libs/rtspconnection.c [new file with mode: 0644]

index 3d41b21..d7ca820 100644 (file)
@@ -197,6 +197,7 @@ check_PROGRAMS = \
        libs/rtp \
        libs/rtp-basepayloading \
        libs/rtsp \
+       libs/rtspconnection \
        libs/sdp \
        libs/tag \
        libs/video \
@@ -376,6 +377,14 @@ libs_rtsp_LDADD = \
        $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_API_VERSION@.la \
        $(GST_BASE_LIBS) $(LDADD)
 
+libs_rtspconnection_CFLAGS = \
+       $(GST_PLUGINS_BASE_CFLAGS) \
+       $(GIO_CFLAGS) \
+       $(AM_CFLAGS)
+libs_rtspconnection_LDADD = \
+       $(top_builddir)/gst-libs/gst/rtsp/libgstrtsp-@GST_API_VERSION@.la \
+       $(GST_BASE_LIBS) $(GIO_LIBS) $(LDADD)
+
 libs_tag_CFLAGS = \
        $(GST_PLUGINS_BASE_CFLAGS) \
        $(GST_BASE_CFLAGS) \
diff --git a/tests/check/libs/rtspconnection.c b/tests/check/libs/rtspconnection.c
new file mode 100644 (file)
index 0000000..f41277e
--- /dev/null
@@ -0,0 +1,370 @@
+/* GStreamer unit tests for the GstRTSPConnection API (RTSP support
+ * library)
+ *
+ * Copyright (C) 2014 Ognyan Tonchev <ognyan axis 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/rtsp/gstrtspconnection.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+
+
+static const gchar *get_msg =
+    "GET /example/url HTTP/1.0\r\n"
+    "Host: 127.0.0.1\r\n"
+    "x-sessioncookie: 805849328\r\n"
+    "\r\n";
+static const gchar *post_msg =
+    "POST /example/url HTTP/1.0\r\n"
+    "Host: 127.0.0.1\r\n"
+    "x-sessioncookie: 805849328\r\n"
+    "Content-Length: 0\r\n"
+    "Content-Type: application/x-rtsp-tunnelled\r\n"
+    "\r\n";
+
+static guint tunnel_start_count;
+static guint tunnel_complete_count;
+static guint tunnel_lost_count;
+static guint closed_count;
+
+typedef struct
+{
+  GMainLoop *loop;
+  guint16 port;
+  GSocketConnection *conn;
+  GMutex mutex;
+  GCond cond;
+  gboolean started;
+} ServiceData;
+
+static gboolean
+incoming_callback (GSocketService *service, GSocketConnection *connection,
+    GObject *source_object, gpointer user_data)
+{
+  ServiceData *data = user_data;
+
+  GST_DEBUG ("new incoming connection");
+  data->conn = g_object_ref (connection);
+  g_main_loop_quit (data->loop);
+  return FALSE;
+}
+
+static gpointer
+service_thread_func (gpointer user_data)
+{
+  ServiceData *data = user_data;
+  GMainContext *service_context;
+  GSocketService *service;
+
+  service_context = g_main_context_new ();
+  g_main_context_push_thread_default (service_context);
+
+  data->loop = g_main_loop_new (service_context, FALSE);
+
+  /* find available port and start service */
+  service = g_socket_service_new ();
+  data->port = g_socket_listener_add_any_inet_port ((GSocketListener *)service,
+      NULL, NULL);
+  fail_unless (data->port != 0);
+
+  /* get notified upon new connection */
+  g_signal_connect (service, "incoming", G_CALLBACK (incoming_callback), data);
+
+  g_socket_service_start (service);
+
+  /* service is started */
+  g_mutex_lock (&data->mutex);
+  data->started = TRUE;
+  g_cond_signal (&data->cond);
+  g_mutex_unlock (&data->mutex);
+
+  /* our service will run in the main context of this main loop */
+  g_main_loop_run (data->loop);
+
+  g_main_context_pop_thread_default (service_context);
+
+  g_main_loop_unref (data->loop);
+  data->loop = NULL;
+
+  return NULL;
+}
+
+static void
+create_connection (GSocketConnection **client_conn,
+    GSocketConnection **server_conn)
+{
+  ServiceData *data;
+  GThread *service_thread;
+  GSocketClient * client = g_socket_client_new ();
+
+  data =  g_new0 (ServiceData, 1);
+  g_mutex_init (&data->mutex);
+  g_cond_init (&data->cond);
+
+  service_thread = g_thread_new ("service thread", service_thread_func, data);
+  fail_unless (service_thread != NULL);
+
+  /* wait for the service to start */
+  g_mutex_lock (&data->mutex);
+  while (!data->started) {
+    g_cond_wait (&data->cond, &data->mutex);
+  }
+  g_mutex_unlock (&data->mutex);
+
+  /* create the tcp link */
+  *client_conn = g_socket_client_connect_to_host (client, (gchar *)"localhost",
+      data->port, NULL, NULL);
+  fail_unless (*client_conn != NULL);
+  fail_unless (g_socket_connection_is_connected (*client_conn));
+
+  g_thread_join (service_thread);
+  *server_conn = data->conn;
+  data->conn = NULL;
+  fail_unless (g_socket_connection_is_connected (*server_conn));
+
+  g_mutex_clear (&data->mutex);
+  g_cond_clear (&data->cond);
+  g_free (data);
+  g_object_unref (client);
+}
+
+static GstRTSPStatusCode
+tunnel_start (GstRTSPWatch *watch, gpointer user_data)
+{
+  tunnel_start_count++;
+  return GST_RTSP_STS_OK;
+}
+
+static GstRTSPResult
+tunnel_complete (GstRTSPWatch *watch, gpointer user_data)
+{
+  tunnel_complete_count++;
+  return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+tunnel_lost (GstRTSPWatch *watch, gpointer user_data)
+{
+  tunnel_lost_count++;
+  return GST_RTSP_OK;
+}
+
+static GstRTSPResult
+closed (GstRTSPWatch *watch, gpointer user_data)
+{
+  closed_count++;
+  return GST_RTSP_OK;
+}
+
+static GstRTSPWatchFuncs watch_funcs = {
+  NULL,
+  NULL,
+  closed,
+  NULL,
+  tunnel_start,
+  tunnel_complete,
+  NULL,
+  tunnel_lost
+};
+
+/* setts up a new tunnel, then disconnects the read connection and creates it
+ * again */
+GST_START_TEST (test_rtspconnection_tunnel_setup)
+{
+  GstRTSPConnection *rtsp_conn1 = NULL;
+  GstRTSPConnection *rtsp_conn2 = NULL;
+  GstRTSPWatch *watch1;
+  GstRTSPWatch *watch2;
+  GstRTSPResult res;
+  GSocketConnection *client_get = NULL;
+  GSocketConnection *server_get = NULL;
+  GSocketConnection *client_post = NULL;
+  GSocketConnection *server_post = NULL;
+  GSocket *server_sock;
+  GOutputStream *ostream_get;
+  GInputStream *istream_get;
+  GOutputStream *ostream_post;
+  gsize size = 0;
+  gchar buffer[1024];
+
+  /* create GET connection */
+  create_connection (&client_get, &server_get);
+  server_sock = g_socket_connection_get_socket (server_get);
+  fail_unless (server_sock != NULL);
+
+  res = gst_rtsp_connection_create_from_socket (server_sock, "127.0.0.1", 4444,
+      NULL, &rtsp_conn1);
+  fail_unless (res == GST_RTSP_OK);
+  fail_unless (rtsp_conn1 != NULL);
+
+  watch1 = gst_rtsp_watch_new (rtsp_conn1, &watch_funcs, NULL, NULL);
+  fail_unless (watch1 != NULL);
+  fail_unless (gst_rtsp_watch_attach (watch1, NULL) > 0);
+
+  ostream_get = g_io_stream_get_output_stream (G_IO_STREAM (client_get));
+  fail_unless (ostream_get != NULL);
+
+  istream_get = g_io_stream_get_input_stream (G_IO_STREAM (client_get));
+  fail_unless (istream_get != NULL);
+
+  /* initiate the tunnel by sending HTTP GET */
+  fail_unless (g_output_stream_write_all (ostream_get, get_msg,
+      strlen (get_msg), &size, NULL, NULL));
+  fail_unless (size == strlen (get_msg));
+
+  while (!g_main_context_iteration (NULL, TRUE));
+  fail_unless (tunnel_start_count == 1);
+  fail_unless (tunnel_complete_count == 0);
+  fail_unless (tunnel_lost_count == 0);
+  fail_unless (closed_count == 0);
+
+  /* read the HTTP GET response */
+  size = g_input_stream_read (istream_get, buffer, 1024, NULL, NULL);
+  fail_unless (size > 0);
+  buffer[size] = 0;
+  fail_unless (g_strrstr (buffer, "HTTP/1.0 200 OK") != NULL);
+
+  /* create POST channel */
+  create_connection (&client_post, &server_post);
+  server_sock = g_socket_connection_get_socket (server_post);
+  fail_unless (server_sock != NULL);
+
+  res = gst_rtsp_connection_create_from_socket (server_sock, "127.0.0.1", 4444,
+      NULL, &rtsp_conn2);
+  fail_unless (res == GST_RTSP_OK);
+  fail_unless (rtsp_conn2 != NULL);
+
+  watch2 = gst_rtsp_watch_new (rtsp_conn2, &watch_funcs, NULL, NULL);
+  fail_unless (watch2 != NULL);
+  fail_unless (gst_rtsp_watch_attach (watch2, NULL) > 0);
+
+  ostream_post = g_io_stream_get_output_stream (G_IO_STREAM (client_post));
+  fail_unless (ostream_post != NULL);
+
+  /* complete the tunnel by sending HTTP POST */
+  fail_unless (g_output_stream_write_all (ostream_post, post_msg,
+      strlen (post_msg), &size, NULL, NULL));
+  fail_unless (size == strlen (post_msg));
+
+  while (!g_main_context_iteration (NULL, TRUE));
+  fail_unless (tunnel_start_count == 1);
+  fail_unless (tunnel_complete_count == 1);
+  fail_unless (tunnel_lost_count == 0);
+  fail_unless (closed_count == 0);
+
+  /* merge the two connections together */
+  fail_unless (gst_rtsp_connection_do_tunnel (rtsp_conn1, rtsp_conn2) ==
+      GST_RTSP_OK);
+  gst_rtsp_watch_reset (watch1);
+  g_source_destroy ((GSource *)watch2);
+  gst_rtsp_connection_free (rtsp_conn2);
+  rtsp_conn2 = NULL;
+
+  /* it must be possible to reconnect the POST channel */
+  g_object_unref (client_post);
+  while (!g_main_context_iteration (NULL, TRUE));
+  fail_unless (tunnel_start_count == 1);
+  fail_unless (tunnel_complete_count == 1);
+  fail_unless (tunnel_lost_count == 1);
+  fail_unless (closed_count == 0);
+  g_object_unref (server_post);
+
+  /* no other source should get dispatched */
+  fail_if (g_main_context_iteration (NULL, FALSE));
+
+  /* create new POST connection */
+  create_connection (&client_post, &server_post);
+  server_sock = g_socket_connection_get_socket (server_post);
+  fail_unless (server_sock != NULL);
+
+  res = gst_rtsp_connection_create_from_socket (server_sock, "127.0.0.1", 4444,
+      NULL, &rtsp_conn2);
+  fail_unless (res == GST_RTSP_OK);
+  fail_unless (rtsp_conn2 != NULL);
+
+  watch2 = gst_rtsp_watch_new (rtsp_conn2, &watch_funcs, NULL, NULL);
+  fail_unless (watch2 != NULL);
+  fail_unless (gst_rtsp_watch_attach (watch2, NULL) > 0);
+
+  ostream_post = g_io_stream_get_output_stream (G_IO_STREAM (client_post));
+  fail_unless (ostream_post != NULL);
+
+  /* complete the tunnel by sending HTTP POST */
+  fail_unless (g_output_stream_write_all (ostream_post, post_msg,
+      strlen (post_msg), &size, NULL, NULL));
+  fail_unless (size == strlen (post_msg));
+
+  while (!g_main_context_iteration (NULL, TRUE));
+  fail_unless (tunnel_start_count == 1);
+  fail_unless (tunnel_complete_count == 2);
+  fail_unless (tunnel_lost_count == 1);
+  fail_unless (closed_count == 0);
+
+  /* merge the two connections together */
+  fail_unless (gst_rtsp_connection_do_tunnel (rtsp_conn1, rtsp_conn2) ==
+      GST_RTSP_OK);
+  gst_rtsp_watch_reset (watch1);
+  g_source_destroy ((GSource *)watch2);
+  gst_rtsp_connection_free (rtsp_conn2);
+  rtsp_conn2 = NULL;
+
+  fail_unless (gst_rtsp_connection_close (rtsp_conn1) == GST_RTSP_OK);
+  fail_unless (gst_rtsp_connection_free (rtsp_conn1) == GST_RTSP_OK);
+  g_object_unref (client_post);
+  g_object_unref (client_get);
+  g_object_unref (server_post);
+  g_object_unref (server_get);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtspconnection_suite (void)
+{
+  Suite *s = suite_create ("rtsp support library(rtspconnection)");
+  TCase *tc_chain = tcase_create ("general");
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_rtspconnection_tunnel_setup);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = rtspconnection_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}