From: Wim Taymans Date: Mon, 7 Mar 2005 18:33:37 +0000 (+0000) Subject: check/: Added checks. X-Git-Tag: RELEASE-0_9_2~606 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f3aa2d7c52ff76742b4f88608b95f90e3ada2e82;p=platform%2Fupstream%2Fgstreamer.git check/: Added checks. Original commit message from CVS: * check/.cvsignore: * check/Makefile.am: * check/gst-libs/.cvsignore: * check/gst-libs/gdp.c: (START_TEST), (gst_object_suite), (main): * check/gst/.cvsignore: * check/gst/gstbus.c: (pound_bus_with_messages), (pull_messages), (START_TEST), (gstbus_suite), (main): * check/gst/gstcaps.c: (START_TEST), (gst_caps_suite), (main): * check/gst/gstdata.c: (START_TEST), (thread_ref), (thread_unref), (gst_data_suite), (main): * check/gst/gstiterator.c: (make_list_of_ints), (START_TEST), (add_fold_func), (gstiterator_suite), (main): * check/gst/gstobject.c: (gst_fake_object_get_type), (START_TEST), (thread_name_object), (thread_name_object_default), (gst_object_name_compare), (gst_object_suite), (main): * check/gst/gstpad.c: (START_TEST), (thread_link_unlink), (gst_pad_suite), (main): * check/gstcheck.c: (gst_check_log_message_func), (gst_check_log_critical_func), (gst_check_init): * check/gstcheck.h: * check/pipelines/simple_launch_lines.c: (setup_pipeline), (run_pipeline), (START_TEST), (simple_launch_lines_suite), (main): Added checks. --- diff --git a/ChangeLog b/ChangeLog index 026736d..313f327 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,31 @@ 2005-03-07 Wim Taymans + * check/.cvsignore: + * check/Makefile.am: + * check/gst-libs/.cvsignore: + * check/gst-libs/gdp.c: (START_TEST), (gst_object_suite), (main): + * check/gst/.cvsignore: + * check/gst/gstbus.c: (pound_bus_with_messages), (pull_messages), + (START_TEST), (gstbus_suite), (main): + * check/gst/gstcaps.c: (START_TEST), (gst_caps_suite), (main): + * check/gst/gstdata.c: (START_TEST), (thread_ref), (thread_unref), + (gst_data_suite), (main): + * check/gst/gstiterator.c: (make_list_of_ints), (START_TEST), + (add_fold_func), (gstiterator_suite), (main): + * check/gst/gstobject.c: (gst_fake_object_get_type), (START_TEST), + (thread_name_object), (thread_name_object_default), + (gst_object_name_compare), (gst_object_suite), (main): + * check/gst/gstpad.c: (START_TEST), (thread_link_unlink), + (gst_pad_suite), (main): + * check/gstcheck.c: (gst_check_log_message_func), + (gst_check_log_critical_func), (gst_check_init): + * check/gstcheck.h: + * check/pipelines/simple_launch_lines.c: (setup_pipeline), + (run_pipeline), (START_TEST), (simple_launch_lines_suite), (main): + Added checks. + +2005-03-07 Wim Taymans + * gst/gstiterator.c: (gst_iterator_init), (gst_iterator_new), (gst_list_iterator_next), (gst_list_iterator_resync), (gst_list_iterator_free), (gst_iterator_new_list), diff --git a/check/.gitignore b/check/.gitignore new file mode 100644 index 0000000..5d45c6c --- /dev/null +++ b/check/.gitignore @@ -0,0 +1 @@ +test-registry.xml diff --git a/check/Makefile.am b/check/Makefile.am new file mode 100644 index 0000000..47208ce --- /dev/null +++ b/check/Makefile.am @@ -0,0 +1,43 @@ +TESTS_ENVIRONMENT=\ + GST_PLUGIN_PATH=$(top_builddir)/gst:$(top_builddir)/check \ + GST_REGISTRY=$(top_builddir)/check/test-registry.xml + +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +# make all tests depend on the versioned gst-register +$(TESTS): $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ + +# rebuild gst-register-@GST_MAJORMINOR@ if needed +$(top_builddir)/tools/gst-register-@GST_MAJORMINOR@: + cd $(top_builddir)/tools && make + +# override to _not_ install the test plugins +install-pluginLTLIBRARIES: + +# This rule is here so make distcheck works on machines where core +# dumps have PIDs appended +CLEANFILES = core.* + +TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \ + gst/gstcaps \ + gst/gstdata \ + gst/gstiterator \ + gst/gstobject \ + gst-libs/gdp + +check_PROGRAMS = $(TESTS) + +noinst_LTLIBRARIES = libgstcheck.la + +libgstcheck_la_SOURCES = gstcheck.c +libgstcheck_la_LIBADD = $(GST_OBJ_LIBS) +noinst_HEADERS = gstcheck.h + +AM_CFLAGS = $(GST_OBJ_CFLAGS) $(CHECK_CFLAGS) +LDADD = $(GST_OBJ_LIBS) $(CHECK_LIBS) libgstcheck.la + +gst_libs_gdp_SOURCES = \ + gst-libs/gdp.c \ + $(top_srcdir)/libs/gst/dataprotocol/dataprotocol.c +# remove GST_ENABLE_NEW when dataprotocol has been declared API-stable +gst_libs_gdp_CFLAGS = $(AM_CFLAGS) -DGST_ENABLE_NEW diff --git a/check/gst-libs/.gitignore b/check/gst-libs/.gitignore new file mode 100644 index 0000000..5ad589e --- /dev/null +++ b/check/gst-libs/.gitignore @@ -0,0 +1 @@ +gdp diff --git a/check/gst-libs/gdp.c b/check/gst-libs/gdp.c new file mode 100644 index 0000000..df79c8d --- /dev/null +++ b/check/gst-libs/gdp.c @@ -0,0 +1,277 @@ +/* GStreamer + * + * unit test for data protocol + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +#include +#include "libs/gst/dataprotocol/dp-private.h" /* private header */ + +/* test our method of reading and writing headers using TO/FROM_BE */ +START_TEST (test_conversion) +{ + guint8 array[9]; + guint8 write_array[9]; + guint16 read_two, expect_two; + guint32 read_four, expect_four; + guint64 read_eight, expect_eight; + int i; + + for (i = 0; i < 9; ++i) { + array[i] = i * 0x10; + } + + /* read 8 16 bits */ + for (i = 0; i < 8; ++i) { + read_two = GST_READ_UINT16_BE (array + i); + expect_two = array[i] * (1 << 8) + array[i + 1]; + fail_unless (read_two == expect_two, + "GST_READ_UINT16_BE %d: read %d != %d\n", i, read_two, expect_two); + } + + /* write 8 16 bits */ + for (i = 0; i < 8; ++i) { + GST_WRITE_UINT16_BE (&write_array[i], read_two); + fail_unless (memcmp (array + 7, write_array + i, 2) == 0, + "GST_WRITE_UINT16_BE %d: memcmp failed", i); + } + + /* read 5 32 bits */ + for (i = 0; i < 5; ++i) { + read_four = GST_READ_UINT32_BE (array + i); + expect_four = array[i] * (1 << 24) + array[i + 1] * (1 << 16) + + array[i + 2] * (1 << 8) + array[i + 3]; + fail_unless (read_four == expect_four, + "GST_READ_UINT32_BE %d: read %d != %d\n", i, read_four, expect_four); + } + + /* read 2 64 bits */ + for (i = 0; i < 2; ++i) { + read_eight = GST_READ_UINT64_BE (array + i); + expect_eight = array[i] * (1LL << 56) + array[i + 1] * (1LL << 48) + + array[i + 2] * (1LL << 40) + array[i + 3] * (1LL << 32) + + array[i + 4] * (1 << 24) + array[i + 5] * (1 << 16) + + array[i + 6] * (1 << 8) + array[i + 7]; + fail_unless (read_eight == expect_eight, + "GST_READ_UINT64_BE %d: read %" G_GUINT64_FORMAT + " != %" G_GUINT64_FORMAT "\n", i, read_eight, expect_eight); + } + + /* write 1 64 bit */ + GST_WRITE_UINT64_BE (&write_array[0], read_eight); + fail_unless (memcmp (array + 1, write_array, 8) == 0, + "GST_WRITE_UINT64_BE: memcmp failed"); +} + +END_TEST +/* test creation of header from buffer and back again */ +START_TEST (test_buffer) +{ + GstBuffer *buffer; + GstBuffer *newbuffer; + + guint header_length; + guint8 *header; + + /* create buffer */ + g_message ("Creating a new 8-byte buffer with ts 0.5 sec, dur 1 sec\n"); + buffer = gst_buffer_new_and_alloc (8); + GST_BUFFER_TIMESTAMP (buffer) = (GstClockTime) (GST_SECOND * 0.5); + GST_BUFFER_DURATION (buffer) = (GstClockTime) GST_SECOND; + GST_BUFFER_OFFSET (buffer) = (guint64) 10; + GST_BUFFER_OFFSET_END (buffer) = (guint64) 19; + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_IN_CAPS); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_SUBBUFFER); + memmove (GST_BUFFER_DATA (buffer), "a buffer", 8); + + /* create a buffer with CRC checking */ + fail_unless (gst_dp_header_from_buffer (buffer, GST_DP_HEADER_FLAG_CRC, + &header_length, &header), "Could not create header from buffer."); + + /* validate the header */ + fail_unless (gst_dp_validate_header (header_length, header), + "Could not validate header"); + /* create a new, empty buffer with the right size */ + newbuffer = gst_dp_buffer_from_header (header_length, header); + fail_unless (newbuffer != NULL, "Could not create a new buffer from header"); + fail_unless (GST_IS_BUFFER (newbuffer), "Created buffer is not a GstBuffer"); + /* read/copy the data */ + memmove (GST_BUFFER_DATA (newbuffer), GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer)); + /* validate the buffer */ + fail_unless (gst_dp_validate_payload (header_length, header, + GST_BUFFER_DATA (newbuffer)), "Could not validate payload"); + + g_message ("new buffer timestamp: %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (newbuffer))); + g_message ("new buffer duration: %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_BUFFER_DURATION (newbuffer))); + g_message ("new buffer offset: %" G_GUINT64_FORMAT "\n", + GST_BUFFER_OFFSET (newbuffer)); + g_message ("new buffer offset_end: %" G_GUINT64_FORMAT "\n", + GST_BUFFER_OFFSET_END (newbuffer)); + fail_unless (GST_BUFFER_TIMESTAMP (newbuffer) == + GST_BUFFER_TIMESTAMP (buffer), "Timestamps don't match !"); + fail_unless (GST_BUFFER_DURATION (newbuffer) == GST_BUFFER_DURATION (buffer), + "Durations don't match !"); + fail_unless (GST_BUFFER_OFFSET (newbuffer) == GST_BUFFER_OFFSET (buffer), + "Offsets don't match !"); + fail_unless (GST_BUFFER_OFFSET_END (newbuffer) == + GST_BUFFER_OFFSET_END (buffer), "Offset ends don't match !"); + fail_if (GST_BUFFER_FLAG_IS_SET (newbuffer, GST_BUFFER_SUBBUFFER), + "GST_BUFFER_SUBBUFFER flag should not have been copied !"); + fail_unless (GST_BUFFER_FLAG_IS_SET (newbuffer, GST_BUFFER_IN_CAPS), + "GST_BUFFER_IN_CAPS flag should have been copied !"); + + g_free (header); +} + +END_TEST +START_TEST (test_caps) +{ + gchar *string, *newstring; + GstCaps *caps, *newcaps; + + guint header_length; + guint8 *header, *payload; + + caps = gst_caps_from_string ("audio/x-raw-float, " + "rate = (int) [ 11025, 48000 ], " + "channels = (int) [ 1, 2 ], " "endianness = (int) BYTE_ORDER, " + "width = (int) 32, " "buffer-frames = (int) 0"); + string = gst_caps_to_string (caps); + g_message ("Created caps: %s\n", string); + fail_unless (gst_dp_packet_from_caps (caps, 0, &header_length, &header, + &payload), "Could not create packet from caps."); + + /* validate the packet */ + fail_unless (gst_dp_validate_packet (header_length, header, payload), + "Could not validate packet"); + newcaps = gst_dp_caps_from_packet (header_length, header, payload); + fail_unless (newcaps != NULL, "Could not create caps from packet"); + //g_return_val_if_fail (GST_IS_CAPS (newcaps), -1); + newstring = gst_caps_to_string (newcaps); + g_message ("Received caps: %s\n", newstring); + fail_unless (strcmp (string, newstring) == 0, + "Created caps do not match original caps"); + g_free (string); + g_free (newstring); +} + +END_TEST +START_TEST (test_event) +{ + GstEvent *send; + GstEvent *receive; + guint header_length; + guint8 *header, *payload; + + g_message ("Testing EOS event at 1s\n"); + send = gst_event_new (GST_EVENT_EOS); + GST_EVENT_TIMESTAMP (send) = GST_SECOND; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from eos event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("EOS, timestamp %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_EOS, + "Received event is not EOS"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND, + "EOS timestamp is not 1.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); + + g_message ("Testing FLUSH event at 2s\n"); + send = gst_event_new (GST_EVENT_FLUSH); + GST_EVENT_TIMESTAMP (send) = GST_SECOND * 2; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from flush event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("Flush, timestamp %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_FLUSH, + "Received event is not flush"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND * 2, + "Flush timestamp is not 2.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); + + g_message ("Testing SEEK event with 1 second at 3 seconds\n"); + send = gst_event_new_seek (GST_FORMAT_TIME, GST_SECOND); + GST_EVENT_TIMESTAMP (send) = GST_SECOND * 3; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from seek event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("Seek, timestamp %" GST_TIME_FORMAT ", to %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive)), + GST_TIME_ARGS (GST_EVENT_SEEK_OFFSET (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_SEEK, + "Returned event is not seek"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND * 3, + "Seek timestamp is not 3.0 sec"); + fail_unless (GST_EVENT_SEEK_FORMAT (receive) == GST_FORMAT_TIME, + "Seek format is not time"); + fail_unless (GST_EVENT_SEEK_OFFSET (receive) == GST_SECOND, + "Seek offset is not 1.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); +} +END_TEST Suite * +gst_object_suite (void) +{ + Suite *s = suite_create ("data protocol"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_conversion); + tcase_add_test (tc_chain, test_buffer); + tcase_add_test (tc_chain, test_caps); + tcase_add_test (tc_chain, test_event); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_object_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + gst_dp_init (); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/check/gst/.gitignore b/check/gst/.gitignore new file mode 100644 index 0000000..4693cc3 --- /dev/null +++ b/check/gst/.gitignore @@ -0,0 +1,3 @@ +gstobject +gstcaps +gstdata diff --git a/check/gst/gstbus.c b/check/gst/gstbus.c new file mode 100644 index 0000000..9d28bc9 --- /dev/null +++ b/check/gst/gstbus.c @@ -0,0 +1,130 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstbus.c: Unit test for the message bus + * + * 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 "../gstcheck.h" + + +static GstBus *test_bus = NULL; + +#define NUM_MESSAGES 1000 +#define NUM_THREADS 10 + +static gpointer +pound_bus_with_messages (gpointer data) +{ + gint thread_id = GPOINTER_TO_INT (data); + gint i; + + for (i = 0; i < NUM_MESSAGES; i++) { + GstMessage *m; + GstStructure *s; + + s = gst_structure_new ("test_message", + "thread_id", G_TYPE_INT, thread_id, "msg_id", G_TYPE_INT, i, NULL); + m = gst_message_new_application (s); + gst_bus_post (test_bus, m); + } + return NULL; +} + +static void +pull_messages () +{ + GstMessage *m; + const GstStructure *s; + guint message_ids[NUM_THREADS]; + gint i; + + for (i = 0; i < NUM_THREADS; i++) + message_ids[i] = 0; + + while (1) { + gint _t, _i; + + m = gst_bus_pop (test_bus); + if (!m) + break; + g_return_if_fail (GST_MESSAGE_TYPE (m) == GST_MESSAGE_APPLICATION); + + s = gst_message_get_structure (m); + if (!gst_structure_get_int (s, "thread_id", &_t)) + g_critical ("Invalid message"); + if (!gst_structure_get_int (s, "msg_id", &_i)) + g_critical ("Invalid message"); + + g_return_if_fail (_t < NUM_THREADS); + g_return_if_fail (_i == message_ids[_t]++); + + gst_message_unref (m); + } + + for (i = 0; i < NUM_THREADS; i++) + g_return_if_fail (message_ids[i] == NUM_MESSAGES); +} + +START_TEST (test_hammer_bus) +{ + GThread *threads[NUM_THREADS]; + gint i; + + test_bus = gst_bus_new (); + + for (i = 0; i < NUM_THREADS; i++) + threads[i] = g_thread_create (pound_bus_with_messages, GINT_TO_POINTER (i), + TRUE, NULL); + + for (i = 0; i < NUM_THREADS; i++) + g_thread_join (threads[i]); + + pull_messages (); + + gst_object_unref ((GstObject *) test_bus); +} +END_TEST Suite * +gstbus_suite (void) +{ + Suite *s = suite_create ("GstBus"); + TCase *tc_chain = tcase_create ("stresstest"); + + tcase_set_timeout (tc_chain, 0); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_hammer_bus); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gstbus_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; +} diff --git a/check/gst/gstcaps.c b/check/gst/gstcaps.c new file mode 100644 index 0000000..4eefe72 --- /dev/null +++ b/check/gst/gstcaps.c @@ -0,0 +1,91 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstcaps.c: Unit test for GstCaps + * + * 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 "../gstcheck.h" + + +START_TEST (test_double_append) +{ + GstStructure *s1; + GstCaps *c1; + + c1 = gst_caps_new_any (); + s1 = gst_structure_from_string ("audio/x-raw-int,rate=44100", NULL); + gst_caps_append_structure (c1, s1); + ASSERT_CRITICAL (gst_caps_append_structure (c1, s1)); +} + +END_TEST +START_TEST (test_mutability) +{ + GstStructure *s1; + GstCaps *c1; + gint ret; + + c1 = gst_caps_new_any (); + s1 = gst_structure_from_string ("audio/x-raw-int,rate=44100", NULL); + gst_structure_set (s1, "rate", G_TYPE_INT, 48000, NULL); + gst_caps_append_structure (c1, s1); + gst_structure_set (s1, "rate", G_TYPE_INT, 22500, NULL); + gst_caps_ref (c1); + ASSERT_CRITICAL (gst_structure_set (s1, "rate", G_TYPE_INT, 11250, NULL)); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 22500); + ASSERT_CRITICAL (gst_caps_set_simple (c1, "rate", G_TYPE_INT, 11250, NULL)); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 22500); + gst_caps_unref (c1); + gst_structure_set (s1, "rate", G_TYPE_INT, 11250, NULL); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 11250); + gst_caps_set_simple (c1, "rate", G_TYPE_INT, 1, NULL); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 1); +} +END_TEST Suite * +gst_caps_suite (void) +{ + Suite *s = suite_create ("GstCaps"); + TCase *tc_chain = tcase_create ("mutability"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_double_append); + tcase_add_test (tc_chain, test_mutability); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_caps_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; +} diff --git a/check/gst/gstdata.c b/check/gst/gstdata.c new file mode 100644 index 0000000..59c931e --- /dev/null +++ b/check/gst/gstdata.c @@ -0,0 +1,196 @@ +/* GStreamer + * + * unit test for GstData + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +START_TEST (test_copy) +{ + GstBuffer *buffer, *copy; + + buffer = gst_buffer_new_and_alloc (4); + + copy = GST_BUFFER (gst_data_copy (GST_DATA (buffer))); + + fail_if (copy == NULL, "Copy of buffer returned NULL"); + fail_unless (GST_BUFFER_SIZE (copy) == 4, + "Copy of buffer has different size"); +} + +END_TEST +START_TEST (test_is_writable) +{ + GstBuffer *buffer; + GstData *data; + + buffer = gst_buffer_new_and_alloc (4); + data = GST_DATA (buffer); + + fail_unless (gst_data_is_writable (data), + "A buffer with one ref should be writable"); + + GST_DATA_FLAG_SET (data, GST_DATA_READONLY); + fail_if (gst_data_is_writable (data), + "A buffer with READONLY set should not be writable"); + GST_DATA_FLAG_UNSET (data, GST_DATA_READONLY); + fail_unless (gst_data_is_writable (data), + "A buffer with one ref and READONLY not set should be writable"); + + fail_if (gst_data_ref (data) == NULL, "Could not ref the data"); + + fail_if (gst_data_is_writable (data), + "A buffer with two refs should not be writable"); +} + +END_TEST +START_TEST (test_copy_on_write) +{ + GstBuffer *buffer; + GstData *data, *data2, *data3; + + buffer = gst_buffer_new_and_alloc (4); + data = GST_DATA (buffer); + + data2 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data2), "copy_on_write did not return a buffer"); + fail_unless (data == data2, + "copy_on_write returned a copy for a buffer with refcount 1"); + + data2 = gst_data_ref (data); + data3 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data3), "copy_on_write did not return a buffer"); + fail_if (data == data3, + "copy_on_write returned same object for a buffer with refcount > 1"); + + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == 1, + "refcount of original data object should be back to 1"); + + data2 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data2), "copy_on_write did not return a buffer"); + fail_unless (data == data2, + "copy_on_write returned a copy for a buffer with refcount 1"); + +} + +END_TEST gint num_threads = 10; +gint refs_per_thread = 10000; + +/* test thread-safe refcounting of GstData */ +void +thread_ref (GstData * data) +{ + int j; + + THREAD_START (); + + for (j = 0; j < refs_per_thread; ++j) { + fail_if (gst_data_ref (data) == NULL, "Could not ref data from thread"); + + if (j % num_threads == 0) + THREAD_SWITCH (); + } +} + +START_TEST (test_ref_threaded) +{ + GstBuffer *buffer; + GstData *data; + gint expected; + + buffer = gst_buffer_new_and_alloc (4); + + data = GST_DATA (buffer); + + MAIN_START_THREADS (num_threads, thread_ref, data); + + MAIN_STOP_THREADS (); + + expected = num_threads * refs_per_thread + 1; + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == expected, + "Refcount of data is %d != %d", GST_DATA_REFCOUNT_VALUE (data), expected); +} +END_TEST void +thread_unref (GstData * data) +{ + int j; + + THREAD_START (); + + for (j = 0; j < refs_per_thread; ++j) { + gst_data_unref (data); + + if (j % num_threads == 0) + THREAD_SWITCH (); + } +} + +START_TEST (test_unref_threaded) +{ + GstBuffer *buffer; + GstData *data; + + buffer = gst_buffer_new_and_alloc (4); + + data = GST_DATA (buffer); + + gst_data_ref_by_count (data, num_threads * refs_per_thread); + + MAIN_START_THREADS (num_threads, thread_unref, data); + + MAIN_STOP_THREADS (); + + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == 1, + "Refcount of data is %d != %d", GST_DATA_REFCOUNT_VALUE (data), 1); + + /* final unref */ + gst_data_unref (data); +} +END_TEST Suite * +gst_data_suite (void) +{ + Suite *s = suite_create ("GstData"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_copy); + tcase_add_test (tc_chain, test_is_writable); + tcase_add_test (tc_chain, test_copy_on_write); + tcase_add_test (tc_chain, test_ref_threaded); + tcase_add_test (tc_chain, test_unref_threaded); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_data_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; +} diff --git a/check/gst/gstiterator.c b/check/gst/gstiterator.c new file mode 100644 index 0000000..aa9a27e --- /dev/null +++ b/check/gst/gstiterator.c @@ -0,0 +1,182 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstiterator.c: Unit test for iterators + * + * 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 "../gstcheck.h" + + +static GList * +make_list_of_ints (gint n) +{ + GList *ret = NULL; + gint i; + + for (i = 0; i < n; i++) + ret = g_list_prepend (ret, GINT_TO_POINTER (i)); + + return g_list_reverse (ret); +} + +#define NUM_ELEMENTS 10 + +START_TEST (test_manual_iteration) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gpointer item; + gint i = 0; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + + g_return_if_fail (iter != NULL); + + while (1) { + res = gst_iterator_next (iter, &item); + if (i < NUM_ELEMENTS) { + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == i); + i++; + continue; + } else { + g_return_if_fail (res == GST_ITERATOR_DONE); + break; + } + } + + gst_iterator_free (iter); +} + +END_TEST +START_TEST (test_resync) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gpointer item; + gint i = 0; + gboolean hacked_list = FALSE; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + + g_return_if_fail (iter != NULL); + + while (1) { + res = gst_iterator_next (iter, &item); + if (i < NUM_ELEMENTS / 2) { + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == i); + i++; + continue; + } else if (!hacked_list) { + /* here's where we test resync */ + g_return_if_fail (res == GST_ITERATOR_OK); + l = g_list_prepend (l, GINT_TO_POINTER (-1)); + cookie++; + hacked_list = TRUE; + continue; + } else { + g_return_if_fail (res == GST_ITERATOR_RESYNC); + gst_iterator_resync (iter); + res = gst_iterator_next (iter, &item); + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == -1); + break; + } + } + + gst_iterator_free (iter); +} +END_TEST static gboolean +add_fold_func (gpointer item, GValue * ret, gpointer user_data) +{ + g_value_set_int (ret, g_value_get_int (ret) + GPOINTER_TO_INT (item)); + return TRUE; +} + +START_TEST (test_fold) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gint i, expected; + GValue ret = { 0, }; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + g_return_if_fail (iter != NULL); + + expected = 0; + for (i = 0; i < NUM_ELEMENTS; i++) + expected += i; + + g_value_init (&ret, G_TYPE_INT); + g_value_set_int (&ret, 0); + + res = gst_iterator_fold (iter, add_fold_func, &ret, NULL); + + g_return_if_fail (res == GST_ITERATOR_DONE); + g_return_if_fail (g_value_get_int (&ret) == expected); +} +END_TEST Suite * +gstiterator_suite (void) +{ + Suite *s = suite_create ("GstIterator"); + TCase *tc_chain = tcase_create ("correctness"); + + tcase_set_timeout (tc_chain, 0); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_manual_iteration); + tcase_add_test (tc_chain, test_resync); + tcase_add_test (tc_chain, test_fold); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gstiterator_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; +} diff --git a/check/gst/gstobject.c b/check/gst/gstobject.c new file mode 100644 index 0000000..52f92e9 --- /dev/null +++ b/check/gst/gstobject.c @@ -0,0 +1,364 @@ +/* GStreamer + * + * unit test for GstObject + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +/* + Create a fake subclass + */ +typedef struct _GstFakeObjectClass GstFakeObjectClass; +typedef struct _GstFakeObject GstFakeObject; + +struct _GstFakeObject +{ + GstObject object; +}; + +struct _GstFakeObjectClass +{ + GstObjectClass parent_class; +}; + +GType _gst_fake_object_type = 0; + +//static GstObjectClass *parent_class = NULL; +//static guint gst_fake_object_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_fake_object_get_type (void) +{ + if (!_gst_fake_object_type) { + static const GTypeInfo fake_object_info = { + sizeof (GstFakeObjectClass), + NULL, //gst_fake_object_base_class_init, + NULL, //gst_fake_object_base_class_finalize, + NULL, //(GClassInitFunc) gst_fake_object_class_init, + NULL, + NULL, + sizeof (GstFakeObject), + 0, + NULL, //(GInstanceInitFunc) gst_fake_object_init, + NULL + }; + + _gst_fake_object_type = g_type_register_static (GST_TYPE_OBJECT, + "GstFakeObject", &fake_object_info, 0); + } + return _gst_fake_object_type; +} + +/* g_object_new on abstract GstObject should fail */ +START_TEST (test_fail_abstract_new) +{ + GstObject *object; + + ASSERT_CRITICAL (object = g_object_new (gst_object_get_type (), NULL)); + fail_unless (object == NULL, "Created an instance of abstract GstObject"); +} + +END_TEST +/* g_object_new on GstFakeObject should succeed */ +START_TEST (test_fake_object_new) +{ + GstObject *object; + + object = g_object_new (gst_fake_object_get_type (), NULL); + fail_if (object == NULL, "Failed to create instance of GstFakeObject"); + fail_unless (GST_IS_OBJECT (object), + "GstFakeObject instance is not a GstObject"); +} + +END_TEST +/* GstFakeObject name tests */ +START_TEST (test_fake_object_name) +{ + GstObject *object; + gchar *name; + gchar *name2; + + object = g_object_new (gst_fake_object_get_type (), NULL); + + name = gst_object_get_name (object); + fail_if (name == NULL, "Newly created object has no name"); + fail_if (strncmp (name, "fakeobject", 10) != 0, + "Random name %s does not start with Gst", name); + + /* give a random name by setting with NULL; + * GstFakeObject class -> fakeobject%d */ + gst_object_set_name (object, NULL); + name = gst_object_get_name (object); + fail_if (name == NULL, "Random name was not assigned"); + fail_if (strncmp (name, "fakeobject", 10) != 0, + "Random name %s does not start with Gst", name); + g_free (name); + + gst_object_set_name (object, "fake"); + name = gst_object_get_name (object); + fail_if (name == NULL, "Failed to get name of GstFakeObject"); + fail_if (strcmp (name, "fake") != 0, "Name of GstFakeObject is not 'fake'"); + + /* change the gotten name to see that it's a copy and not the original */ + name[0] = 'm'; + name2 = gst_object_get_name (object); + fail_if (strcmp (name2, "fake") != 0, + "Copy of object name affected actual object name"); + g_free (name); + g_free (name2); +} + +END_TEST +/* thread function for threaded name change test */ + gpointer thread_name_object (GstObject * object) +{ + gchar *thread_id = g_strdup_printf ("%p", g_thread_self ()); + + THREAD_START (); + + /* give main thread a head start */ + g_usleep (100000); + + /* write our name repeatedly */ + g_message ("THREAD %s: starting loop\n", thread_id); + while (THREAD_TEST_RUNNING ()) { + gst_object_set_name (object, thread_id); + /* a minimal sleep invokes a thread switch */ + THREAD_SWITCH (); + } + + /* thread is done, so let's return */ + g_message ("THREAD %s: set name\n", thread_id); + g_free (thread_id); + + return NULL; +} + +/* + * main thread sets and gets name while other threads set the name + * constantly; fails because lock is released inbetween set and get + */ + +START_TEST (test_fake_object_name_threaded_wrong) +{ + GstObject *object; + gchar *name; + gint i; + gboolean expected_failure = FALSE; + + g_message ("\nTEST: set/get without lock\n"); + + object = g_object_new (gst_fake_object_get_type (), NULL); + gst_object_set_name (object, "main"); + + MAIN_START_THREADS (5, thread_name_object, object); + + /* start looping and set/get name repeatedly */ + for (i = 0; i < 1000; ++i) { + gst_object_set_name (object, "main"); + THREAD_SWITCH (); + name = gst_object_get_name (object); + if (strcmp (name, "main") != 0) { + g_message ("MAIN: expected failure during run %d\n", i); + expected_failure = TRUE; + g_free (name); + break; + } + g_free (name); + } + MAIN_STOP_THREADS (); + + fail_unless (expected_failure, "name did not get changed"); +} + +END_TEST +/* + * main thread sets and gets name directly on struct inside the object lock + * succeed because lock is held during set/get, and threads are locked out + */ +START_TEST (test_fake_object_name_threaded_right) +{ + GstObject *object; + gchar *name; + gint i; + + g_message ("\nTEST: set/get inside lock\n"); + + object = g_object_new (gst_fake_object_get_type (), NULL); + gst_object_set_name (object, "main"); + + MAIN_START_THREADS (5, thread_name_object, object); + + /* start looping and set/get name repeatedly */ + for (i = 0; i < 1000; ++i) { + GST_LOCK (object); + g_free (GST_OBJECT_NAME (object)); + GST_OBJECT_NAME (object) = g_strdup ("main"); + THREAD_SWITCH (); + name = g_strdup (GST_OBJECT_NAME (object)); + GST_UNLOCK (object); + + fail_unless (strcmp (name, "main") == 0, + "Name got changed while lock held during run %d", i); + g_free (name); + } + MAIN_STOP_THREADS (); +} + +END_TEST +/* + * main thread creates lots of objects + * child threads sets default names on objects + * then main thread checks uniqueness of object names + */ + GList * object_list = NULL; +gint num_objects = 1000; +gint num_threads = 5; + +/* thread function for threaded default name change test */ +gpointer +thread_name_object_default (int *i) +{ + int j; + + THREAD_START (); + + for (j = *i; j < num_objects; j += num_threads) { + GstObject *o = GST_OBJECT (g_list_nth_data (object_list, j)); + + /* g_message ("THREAD %p: setting default name on object %d\n", + g_thread_self (), j); */ + gst_object_set_name (o, NULL); + THREAD_SWITCH (); + } + + /* thread is done, so let's return */ + g_message ("THREAD %p: set name\n", g_thread_self ()); + g_free (i); + + return NULL; +} + +static gint +gst_object_name_compare (GstObject * o, GstObject * p) +{ + gint result; + + GST_LOCK (o); + GST_LOCK (p); + + if (o->name == NULL && p->name == NULL) { + result = 0; + } else if (o->name == NULL) { + result = -1; + } else if (p->name == NULL) { + result = 1; + } else { + result = strcmp (o->name, p->name); + } + + GST_UNLOCK (p); + GST_UNLOCK (o); + + return result; +} + +START_TEST (test_fake_object_name_threaded_unique) +{ + GstObject *object; + gint i; + gint *ip; + gchar *name1, *name2; + GList *l; + + g_message ("\nTEST: uniqueness of default names\n"); + + for (i = 0; i < num_objects; ++i) { + object = g_object_new (gst_fake_object_get_type (), NULL); + object_list = g_list_append (object_list, object); + } + + MAIN_INIT (); + + mark_point (); + for (i = 0; i < num_threads; ++i) { + ip = g_new (gint, 1); + *ip = i; + MAIN_START_THREAD_FUNCTION (i, thread_name_object_default, ip); + } + + mark_point (); + MAIN_SYNCHRONIZE (); + mark_point (); + MAIN_STOP_THREADS (); + + /* sort GList based on object name */ + /* FIXME: sort and test */ + g_list_sort (object_list, (GCompareFunc) gst_object_name_compare); + + name1 = gst_object_get_name (GST_OBJECT (object_list->data)); + for (l = object_list->next; l->next; l = l->next) { + g_message ("object with name %s\n", name1); + name2 = gst_object_get_name (GST_OBJECT (l->data)); + fail_if (strcmp (name1, name2) == 0, "Two objects with name %s", name2); + g_free (name1); + name1 = name2; + } + + /* free stuff */ + g_list_foreach (object_list, (GFunc) g_object_unref, NULL); +} + +END_TEST +/* test: try renaming a parented object, make sure it fails */ + Suite * gst_object_suite (void) +{ + Suite *s = suite_create ("GstObject"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_fake_object_new); + tcase_add_test (tc_chain, test_fake_object_name); + tcase_add_test (tc_chain, test_fake_object_name_threaded_wrong); + tcase_add_test (tc_chain, test_fake_object_name_threaded_right); + tcase_add_test (tc_chain, test_fake_object_name_threaded_unique); + //tcase_add_checked_fixture (tc_chain, setup, teardown); + + /* SEGV tests go last so we can debug the others */ + tcase_add_test_raise_signal (tc_chain, test_fail_abstract_new, SIGSEGV); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_object_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; +} diff --git a/check/gst/gstpad.c b/check/gst/gstpad.c new file mode 100644 index 0000000..3875900 --- /dev/null +++ b/check/gst/gstpad.c @@ -0,0 +1,127 @@ +/* GStreamer + * Copyright (C) <2005> Thomas Vander Stichele + * + * gstpad.c: Unit test for GstPad + * + * 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 "../gstcheck.h" + +START_TEST (test_link) +{ + GstPad *src, *sink; + GstPadTemplate *srct; //, *sinkt; + GstCaps *srcc; + + GstPadLinkReturn ret; + gchar *name; + + src = gst_pad_new ("source", GST_PAD_SRC); + fail_if (src == NULL); + + name = gst_pad_get_name (src); + fail_unless (strcmp (name, "source") == 0); + + sink = gst_pad_new ("sink", GST_PAD_SINK); + fail_if (sink == NULL); + + /* linking without templates should fail */ + ret = gst_pad_link (src, sink); + fail_unless (ret == GST_PAD_LINK_NOFORMAT); + + ASSERT_CRITICAL (gst_pad_get_pad_template (NULL)); + + srct = gst_pad_get_pad_template (src); + fail_unless (srct == NULL); + + /* create caps */ + srcc = gst_caps_new_any (); + gst_pad_set_caps (src, srcc); + gst_pad_set_caps (sink, srcc); + + /* linking with any caps should succeed */ + ret = gst_pad_link (src, sink); + fail_unless (ret == GST_PAD_LINK_OK); +} + +END_TEST +/* threaded link/unlink */ +/* use globals */ + GstPad * src, *sink; + +void +thread_link_unlink (gpointer data) +{ + THREAD_START (); + + while (THREAD_TEST_RUNNING ()) { + gst_pad_link (src, sink); + gst_pad_unlink (src, sink); + THREAD_SWITCH (); + } +} + +START_TEST (test_link_unlink_threaded) +{ + GstCaps *caps; + int i; + + src = gst_pad_new ("source", GST_PAD_SRC); + fail_if (src == NULL); + sink = gst_pad_new ("sink", GST_PAD_SINK); + fail_if (sink == NULL); + + caps = gst_caps_new_any (); + gst_pad_set_caps (src, caps); + gst_pad_set_caps (sink, caps); + + MAIN_START_THREADS (5, thread_link_unlink, NULL); + for (i = 0; i < 1000; ++i) { + gst_pad_is_linked (src); + gst_pad_is_linked (sink); + THREAD_SWITCH (); + } + MAIN_STOP_THREADS (); +} +END_TEST Suite * +gst_pad_suite (void) +{ + Suite *s = suite_create ("GstPad"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_link); + tcase_add_test (tc_chain, test_link_unlink_threaded); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_pad_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; +} diff --git a/check/gstcheck.c b/check/gstcheck.c new file mode 100644 index 0000000..53af0a6 --- /dev/null +++ b/check/gstcheck.c @@ -0,0 +1,85 @@ +/* GStreamer + * + * Common code for GStreamer unittests + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "gstcheck.h" + + +/* logging function for tests + * a test uses g_message() to log a debug line + * a gst unit test can be run with GST_TEST_DEBUG env var set to see the + * messages + */ + +gboolean _gst_check_threads_running = FALSE; +GList *thread_list = NULL; +GMutex *mutex; +GCond *start_cond; /* used to notify main thread of thread startups */ +GCond *sync_cond; /* used to synchronize all threads and main thread */ + +gboolean _gst_check_debug = FALSE; +gboolean _gst_check_raised_critical = FALSE; +gboolean _gst_check_expecting_log = FALSE; + +void gst_check_log_message_func + (const gchar * log_domain, GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + if (_gst_check_debug) { + g_print (message); + } +} + +void gst_check_log_critical_func + (const gchar * log_domain, GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + if (!_gst_check_expecting_log) { + g_print ("\n\nUnexpected critical/warning: %s\n", message); + fail ("Unexpected critical/warning: %s", message); + } + + if (_gst_check_debug) { + g_print ("\nExpected critical/warning: %s\n", message); + } + + if (log_level & G_LOG_LEVEL_CRITICAL) + _gst_check_raised_critical = TRUE; +} + +/* initialize GStreamer testing */ +void +gst_check_init (int *argc, char **argv[]) +{ + gst_init (argc, argv); + + if (g_getenv ("GST_TEST_DEBUG")) + _gst_check_debug = TRUE; + + g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, gst_check_log_message_func, + NULL); + g_log_set_handler (NULL, G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); + g_log_set_handler ("GStreamer", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); + g_log_set_handler ("GLib-GObject", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); +} diff --git a/check/gstcheck.h b/check/gstcheck.h new file mode 100644 index 0000000..ce452eb --- /dev/null +++ b/check/gstcheck.h @@ -0,0 +1,150 @@ +/* GStreamer + * + * Common code for GStreamer unittests + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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. + */ + +#ifndef __GST_CHECK_H__ +#define __GST_CHECK_H__ + +#include +#include +#include + +#include + +#include + + +/* logging function for tests + * a test uses g_message() to log a debug line + * a gst unit test can be run with GST_TEST_DEBUG env var set to see the + * messages + */ +extern gboolean _gst_check_threads_running; +extern gboolean _gst_check_raised_critical; +extern gboolean _gst_check_expecting_log; + +void gst_check_init (int *argc, char **argv[]); + +/*** + * thread test macros and variables + */ +extern GList *thread_list; +extern GMutex *mutex; +extern GCond *start_cond; /* used to notify main thread of thread startups */ +extern GCond *sync_cond; /* used to synchronize all threads and main thread */ + +#define MAIN_START_THREADS(count, function, data) \ +MAIN_INIT(); \ +MAIN_START_THREAD_FUNCTIONS(count, function, data); \ +MAIN_SYNCHRONIZE(); + +#define MAIN_INIT() \ +G_STMT_START { \ + _gst_check_threads_running = TRUE; \ + \ + mutex = g_mutex_new (); \ + start_cond = g_cond_new (); \ + sync_cond = g_cond_new (); \ +} G_STMT_END; + +#define MAIN_START_THREAD_FUNCTIONS(count, function, data) \ +G_STMT_START { \ + int i; \ + for (i = 0; i < count; ++i) { \ + MAIN_START_THREAD_FUNCTION (i, function, data); \ + } \ +} G_STMT_END; + +#define MAIN_START_THREAD_FUNCTION(i, function, data) \ +G_STMT_START { \ + GThread *thread = NULL; \ + g_message ("MAIN: creating thread %d\n", i); \ + g_mutex_lock (mutex); \ + thread = g_thread_create ((GThreadFunc) function, data, \ + TRUE, NULL); \ + /* wait for thread to signal us that it's ready */ \ + g_message ("MAIN: waiting for thread %d\n", i); \ + g_cond_wait (start_cond, mutex); \ + g_mutex_unlock (mutex); \ + \ + thread_list = g_list_append (thread_list, thread); \ +} G_STMT_END; + + +#define MAIN_SYNCHRONIZE() \ +G_STMT_START { \ + g_message ("MAIN: synchronizing\n"); \ + g_cond_broadcast (sync_cond); \ + g_message ("MAIN: synchronized\n"); \ +} G_STMT_END; + +#define MAIN_STOP_THREADS() \ +G_STMT_START { \ + _gst_check_threads_running = FALSE; \ + \ + /* join all threads */ \ + g_message ("MAIN: joining\n"); \ + g_list_foreach (thread_list, (GFunc) g_thread_join, NULL); \ + g_message ("MAIN: joined\n"); \ +} G_STMT_END; + +#define THREAD_START() \ +THREAD_STARTED(); \ +THREAD_SYNCHRONIZE(); + +#define THREAD_STARTED() \ +G_STMT_START { \ + /* signal main thread that we started */ \ + g_message ("THREAD %p: started\n", g_thread_self ()); \ + g_mutex_lock (mutex); \ + g_cond_signal (start_cond); \ +} G_STMT_END; + +#define THREAD_SYNCHRONIZE() \ +G_STMT_START { \ + /* synchronize everyone */ \ + g_message ("THREAD %p: syncing\n", g_thread_self ()); \ + g_cond_wait (sync_cond, mutex); \ + g_message ("THREAD %p: synced\n", g_thread_self ()); \ + g_mutex_unlock (mutex); \ +} G_STMT_END; + +#define THREAD_SWITCH() \ +G_STMT_START { \ + /* a minimal sleep is a context switch */ \ + g_usleep (1); \ +} G_STMT_END; + +#define THREAD_TEST_RUNNING() (_gst_check_threads_running == TRUE) + +#define ASSERT_CRITICAL(code) \ +G_STMT_START { \ + _gst_check_expecting_log = TRUE; \ + _gst_check_raised_critical = FALSE; \ + code; \ + _fail_unless (_gst_check_raised_critical, __FILE__, __LINE__, \ + "Expected g_critical, got nothing: '"#code"'"); \ + _gst_check_expecting_log = FALSE; \ +} G_STMT_END + + +#endif /* __GST_CHECK_H__ */ + diff --git a/check/pipelines/simple_launch_lines.c b/check/pipelines/simple_launch_lines.c new file mode 100644 index 0000000..f812392 --- /dev/null +++ b/check/pipelines/simple_launch_lines.c @@ -0,0 +1,127 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * simple_launch_lines.c: Unit test for simple pipelines + * + * 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 "../gstcheck.h" + + +static GstElement * +setup_pipeline (gchar * pipe_descr) +{ + GstElement *pipeline; + + pipeline = gst_parse_launch (pipe_descr, NULL); + g_return_val_if_fail (GST_IS_PIPELINE (pipeline), NULL); + return pipeline; +} + +/* events is a mask of expected events. tevent is the expected terminal event. + the poll call will time out after half a second. + */ +static void +run_pipeline (GstElement * pipe, gchar * descr, + GstMessageType events, GstMessageType tevent) +{ + GstBus *bus; + GstMessageType revent; + + bus = gst_element_get_bus (pipe); + g_assert (bus); + gst_element_set_state (pipe, GST_STATE_PLAYING); + + while (1) { + revent = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2); + + /* always have to pop the message before getting back into poll */ + if (revent != GST_MESSAGE_UNKNOWN) + gst_message_unref (gst_bus_pop (bus)); + + if (revent == tevent) { + break; + } else if (revent == GST_MESSAGE_UNKNOWN) { + g_critical ("Unexpected timeout in gst_bus_poll, looking for %d: %s", + tevent, descr); + break; + } else if (revent & events) { + continue; + } + g_critical ("Unexpected message received of type %d, looking for %d: %s", + revent, tevent, descr); + } + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (pipe)); +} + +START_TEST (test_2_elements) +{ + gchar *s; + + s = "fakesrc has-loop=false ! fakesink has-loop=true"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN); + + s = "fakesrc has-loop=true ! fakesink has-loop=false"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN); + + s = "fakesrc has-loop=false num-buffers=10 ! fakesink has-loop=true"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS); + + s = "fakesrc has-loop=true num-buffers=10 ! fakesink has-loop=false"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS); + + s = "fakesrc has-loop=false ! fakesink has-loop=false"; + ASSERT_CRITICAL (run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN)); +} +END_TEST Suite * +simple_launch_lines_suite (void) +{ + Suite *s = suite_create ("Pipelines"); + TCase *tc_chain = tcase_create ("linear"); + + /* time out after 20s, not the default 3 */ + tcase_set_timeout (tc_chain, 20); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_2_elements); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = simple_launch_lines_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; +} diff --git a/tests/check/.gitignore b/tests/check/.gitignore new file mode 100644 index 0000000..5d45c6c --- /dev/null +++ b/tests/check/.gitignore @@ -0,0 +1 @@ +test-registry.xml diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am new file mode 100644 index 0000000..47208ce --- /dev/null +++ b/tests/check/Makefile.am @@ -0,0 +1,43 @@ +TESTS_ENVIRONMENT=\ + GST_PLUGIN_PATH=$(top_builddir)/gst:$(top_builddir)/check \ + GST_REGISTRY=$(top_builddir)/check/test-registry.xml + +plugindir = $(libdir)/gstreamer-@GST_MAJORMINOR@ + +# make all tests depend on the versioned gst-register +$(TESTS): $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ + +# rebuild gst-register-@GST_MAJORMINOR@ if needed +$(top_builddir)/tools/gst-register-@GST_MAJORMINOR@: + cd $(top_builddir)/tools && make + +# override to _not_ install the test plugins +install-pluginLTLIBRARIES: + +# This rule is here so make distcheck works on machines where core +# dumps have PIDs appended +CLEANFILES = core.* + +TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \ + gst/gstcaps \ + gst/gstdata \ + gst/gstiterator \ + gst/gstobject \ + gst-libs/gdp + +check_PROGRAMS = $(TESTS) + +noinst_LTLIBRARIES = libgstcheck.la + +libgstcheck_la_SOURCES = gstcheck.c +libgstcheck_la_LIBADD = $(GST_OBJ_LIBS) +noinst_HEADERS = gstcheck.h + +AM_CFLAGS = $(GST_OBJ_CFLAGS) $(CHECK_CFLAGS) +LDADD = $(GST_OBJ_LIBS) $(CHECK_LIBS) libgstcheck.la + +gst_libs_gdp_SOURCES = \ + gst-libs/gdp.c \ + $(top_srcdir)/libs/gst/dataprotocol/dataprotocol.c +# remove GST_ENABLE_NEW when dataprotocol has been declared API-stable +gst_libs_gdp_CFLAGS = $(AM_CFLAGS) -DGST_ENABLE_NEW diff --git a/tests/check/gst/.gitignore b/tests/check/gst/.gitignore new file mode 100644 index 0000000..4693cc3 --- /dev/null +++ b/tests/check/gst/.gitignore @@ -0,0 +1,3 @@ +gstobject +gstcaps +gstdata diff --git a/tests/check/gst/gstbus.c b/tests/check/gst/gstbus.c new file mode 100644 index 0000000..9d28bc9 --- /dev/null +++ b/tests/check/gst/gstbus.c @@ -0,0 +1,130 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstbus.c: Unit test for the message bus + * + * 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 "../gstcheck.h" + + +static GstBus *test_bus = NULL; + +#define NUM_MESSAGES 1000 +#define NUM_THREADS 10 + +static gpointer +pound_bus_with_messages (gpointer data) +{ + gint thread_id = GPOINTER_TO_INT (data); + gint i; + + for (i = 0; i < NUM_MESSAGES; i++) { + GstMessage *m; + GstStructure *s; + + s = gst_structure_new ("test_message", + "thread_id", G_TYPE_INT, thread_id, "msg_id", G_TYPE_INT, i, NULL); + m = gst_message_new_application (s); + gst_bus_post (test_bus, m); + } + return NULL; +} + +static void +pull_messages () +{ + GstMessage *m; + const GstStructure *s; + guint message_ids[NUM_THREADS]; + gint i; + + for (i = 0; i < NUM_THREADS; i++) + message_ids[i] = 0; + + while (1) { + gint _t, _i; + + m = gst_bus_pop (test_bus); + if (!m) + break; + g_return_if_fail (GST_MESSAGE_TYPE (m) == GST_MESSAGE_APPLICATION); + + s = gst_message_get_structure (m); + if (!gst_structure_get_int (s, "thread_id", &_t)) + g_critical ("Invalid message"); + if (!gst_structure_get_int (s, "msg_id", &_i)) + g_critical ("Invalid message"); + + g_return_if_fail (_t < NUM_THREADS); + g_return_if_fail (_i == message_ids[_t]++); + + gst_message_unref (m); + } + + for (i = 0; i < NUM_THREADS; i++) + g_return_if_fail (message_ids[i] == NUM_MESSAGES); +} + +START_TEST (test_hammer_bus) +{ + GThread *threads[NUM_THREADS]; + gint i; + + test_bus = gst_bus_new (); + + for (i = 0; i < NUM_THREADS; i++) + threads[i] = g_thread_create (pound_bus_with_messages, GINT_TO_POINTER (i), + TRUE, NULL); + + for (i = 0; i < NUM_THREADS; i++) + g_thread_join (threads[i]); + + pull_messages (); + + gst_object_unref ((GstObject *) test_bus); +} +END_TEST Suite * +gstbus_suite (void) +{ + Suite *s = suite_create ("GstBus"); + TCase *tc_chain = tcase_create ("stresstest"); + + tcase_set_timeout (tc_chain, 0); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_hammer_bus); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gstbus_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; +} diff --git a/tests/check/gst/gstcaps.c b/tests/check/gst/gstcaps.c new file mode 100644 index 0000000..4eefe72 --- /dev/null +++ b/tests/check/gst/gstcaps.c @@ -0,0 +1,91 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstcaps.c: Unit test for GstCaps + * + * 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 "../gstcheck.h" + + +START_TEST (test_double_append) +{ + GstStructure *s1; + GstCaps *c1; + + c1 = gst_caps_new_any (); + s1 = gst_structure_from_string ("audio/x-raw-int,rate=44100", NULL); + gst_caps_append_structure (c1, s1); + ASSERT_CRITICAL (gst_caps_append_structure (c1, s1)); +} + +END_TEST +START_TEST (test_mutability) +{ + GstStructure *s1; + GstCaps *c1; + gint ret; + + c1 = gst_caps_new_any (); + s1 = gst_structure_from_string ("audio/x-raw-int,rate=44100", NULL); + gst_structure_set (s1, "rate", G_TYPE_INT, 48000, NULL); + gst_caps_append_structure (c1, s1); + gst_structure_set (s1, "rate", G_TYPE_INT, 22500, NULL); + gst_caps_ref (c1); + ASSERT_CRITICAL (gst_structure_set (s1, "rate", G_TYPE_INT, 11250, NULL)); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 22500); + ASSERT_CRITICAL (gst_caps_set_simple (c1, "rate", G_TYPE_INT, 11250, NULL)); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 22500); + gst_caps_unref (c1); + gst_structure_set (s1, "rate", G_TYPE_INT, 11250, NULL); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 11250); + gst_caps_set_simple (c1, "rate", G_TYPE_INT, 1, NULL); + fail_unless (gst_structure_get_int (s1, "rate", &ret)); + fail_unless (ret == 1); +} +END_TEST Suite * +gst_caps_suite (void) +{ + Suite *s = suite_create ("GstCaps"); + TCase *tc_chain = tcase_create ("mutability"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_double_append); + tcase_add_test (tc_chain, test_mutability); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_caps_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; +} diff --git a/tests/check/gst/gstdata.c b/tests/check/gst/gstdata.c new file mode 100644 index 0000000..59c931e --- /dev/null +++ b/tests/check/gst/gstdata.c @@ -0,0 +1,196 @@ +/* GStreamer + * + * unit test for GstData + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +START_TEST (test_copy) +{ + GstBuffer *buffer, *copy; + + buffer = gst_buffer_new_and_alloc (4); + + copy = GST_BUFFER (gst_data_copy (GST_DATA (buffer))); + + fail_if (copy == NULL, "Copy of buffer returned NULL"); + fail_unless (GST_BUFFER_SIZE (copy) == 4, + "Copy of buffer has different size"); +} + +END_TEST +START_TEST (test_is_writable) +{ + GstBuffer *buffer; + GstData *data; + + buffer = gst_buffer_new_and_alloc (4); + data = GST_DATA (buffer); + + fail_unless (gst_data_is_writable (data), + "A buffer with one ref should be writable"); + + GST_DATA_FLAG_SET (data, GST_DATA_READONLY); + fail_if (gst_data_is_writable (data), + "A buffer with READONLY set should not be writable"); + GST_DATA_FLAG_UNSET (data, GST_DATA_READONLY); + fail_unless (gst_data_is_writable (data), + "A buffer with one ref and READONLY not set should be writable"); + + fail_if (gst_data_ref (data) == NULL, "Could not ref the data"); + + fail_if (gst_data_is_writable (data), + "A buffer with two refs should not be writable"); +} + +END_TEST +START_TEST (test_copy_on_write) +{ + GstBuffer *buffer; + GstData *data, *data2, *data3; + + buffer = gst_buffer_new_and_alloc (4); + data = GST_DATA (buffer); + + data2 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data2), "copy_on_write did not return a buffer"); + fail_unless (data == data2, + "copy_on_write returned a copy for a buffer with refcount 1"); + + data2 = gst_data_ref (data); + data3 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data3), "copy_on_write did not return a buffer"); + fail_if (data == data3, + "copy_on_write returned same object for a buffer with refcount > 1"); + + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == 1, + "refcount of original data object should be back to 1"); + + data2 = gst_data_copy_on_write (data); + fail_unless (GST_IS_BUFFER (data2), "copy_on_write did not return a buffer"); + fail_unless (data == data2, + "copy_on_write returned a copy for a buffer with refcount 1"); + +} + +END_TEST gint num_threads = 10; +gint refs_per_thread = 10000; + +/* test thread-safe refcounting of GstData */ +void +thread_ref (GstData * data) +{ + int j; + + THREAD_START (); + + for (j = 0; j < refs_per_thread; ++j) { + fail_if (gst_data_ref (data) == NULL, "Could not ref data from thread"); + + if (j % num_threads == 0) + THREAD_SWITCH (); + } +} + +START_TEST (test_ref_threaded) +{ + GstBuffer *buffer; + GstData *data; + gint expected; + + buffer = gst_buffer_new_and_alloc (4); + + data = GST_DATA (buffer); + + MAIN_START_THREADS (num_threads, thread_ref, data); + + MAIN_STOP_THREADS (); + + expected = num_threads * refs_per_thread + 1; + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == expected, + "Refcount of data is %d != %d", GST_DATA_REFCOUNT_VALUE (data), expected); +} +END_TEST void +thread_unref (GstData * data) +{ + int j; + + THREAD_START (); + + for (j = 0; j < refs_per_thread; ++j) { + gst_data_unref (data); + + if (j % num_threads == 0) + THREAD_SWITCH (); + } +} + +START_TEST (test_unref_threaded) +{ + GstBuffer *buffer; + GstData *data; + + buffer = gst_buffer_new_and_alloc (4); + + data = GST_DATA (buffer); + + gst_data_ref_by_count (data, num_threads * refs_per_thread); + + MAIN_START_THREADS (num_threads, thread_unref, data); + + MAIN_STOP_THREADS (); + + fail_unless (GST_DATA_REFCOUNT_VALUE (data) == 1, + "Refcount of data is %d != %d", GST_DATA_REFCOUNT_VALUE (data), 1); + + /* final unref */ + gst_data_unref (data); +} +END_TEST Suite * +gst_data_suite (void) +{ + Suite *s = suite_create ("GstData"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_copy); + tcase_add_test (tc_chain, test_is_writable); + tcase_add_test (tc_chain, test_copy_on_write); + tcase_add_test (tc_chain, test_ref_threaded); + tcase_add_test (tc_chain, test_unref_threaded); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_data_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; +} diff --git a/tests/check/gst/gstiterator.c b/tests/check/gst/gstiterator.c new file mode 100644 index 0000000..aa9a27e --- /dev/null +++ b/tests/check/gst/gstiterator.c @@ -0,0 +1,182 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * gstiterator.c: Unit test for iterators + * + * 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 "../gstcheck.h" + + +static GList * +make_list_of_ints (gint n) +{ + GList *ret = NULL; + gint i; + + for (i = 0; i < n; i++) + ret = g_list_prepend (ret, GINT_TO_POINTER (i)); + + return g_list_reverse (ret); +} + +#define NUM_ELEMENTS 10 + +START_TEST (test_manual_iteration) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gpointer item; + gint i = 0; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + + g_return_if_fail (iter != NULL); + + while (1) { + res = gst_iterator_next (iter, &item); + if (i < NUM_ELEMENTS) { + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == i); + i++; + continue; + } else { + g_return_if_fail (res == GST_ITERATOR_DONE); + break; + } + } + + gst_iterator_free (iter); +} + +END_TEST +START_TEST (test_resync) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gpointer item; + gint i = 0; + gboolean hacked_list = FALSE; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + + g_return_if_fail (iter != NULL); + + while (1) { + res = gst_iterator_next (iter, &item); + if (i < NUM_ELEMENTS / 2) { + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == i); + i++; + continue; + } else if (!hacked_list) { + /* here's where we test resync */ + g_return_if_fail (res == GST_ITERATOR_OK); + l = g_list_prepend (l, GINT_TO_POINTER (-1)); + cookie++; + hacked_list = TRUE; + continue; + } else { + g_return_if_fail (res == GST_ITERATOR_RESYNC); + gst_iterator_resync (iter); + res = gst_iterator_next (iter, &item); + g_return_if_fail (res == GST_ITERATOR_OK); + g_return_if_fail (GPOINTER_TO_INT (item) == -1); + break; + } + } + + gst_iterator_free (iter); +} +END_TEST static gboolean +add_fold_func (gpointer item, GValue * ret, gpointer user_data) +{ + g_value_set_int (ret, g_value_get_int (ret) + GPOINTER_TO_INT (item)); + return TRUE; +} + +START_TEST (test_fold) +{ + GList *l; + guint32 cookie = 0; + GMutex *m; + GstIterator *iter; + GstIteratorResult res; + gint i, expected; + GValue ret = { 0, }; + + l = make_list_of_ints (NUM_ELEMENTS); + m = g_mutex_new (); + iter = gst_iterator_new_list (m, &cookie, &l, NULL, NULL, NULL); + g_return_if_fail (iter != NULL); + + expected = 0; + for (i = 0; i < NUM_ELEMENTS; i++) + expected += i; + + g_value_init (&ret, G_TYPE_INT); + g_value_set_int (&ret, 0); + + res = gst_iterator_fold (iter, add_fold_func, &ret, NULL); + + g_return_if_fail (res == GST_ITERATOR_DONE); + g_return_if_fail (g_value_get_int (&ret) == expected); +} +END_TEST Suite * +gstiterator_suite (void) +{ + Suite *s = suite_create ("GstIterator"); + TCase *tc_chain = tcase_create ("correctness"); + + tcase_set_timeout (tc_chain, 0); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_manual_iteration); + tcase_add_test (tc_chain, test_resync); + tcase_add_test (tc_chain, test_fold); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gstiterator_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; +} diff --git a/tests/check/gst/gstobject.c b/tests/check/gst/gstobject.c new file mode 100644 index 0000000..52f92e9 --- /dev/null +++ b/tests/check/gst/gstobject.c @@ -0,0 +1,364 @@ +/* GStreamer + * + * unit test for GstObject + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +/* + Create a fake subclass + */ +typedef struct _GstFakeObjectClass GstFakeObjectClass; +typedef struct _GstFakeObject GstFakeObject; + +struct _GstFakeObject +{ + GstObject object; +}; + +struct _GstFakeObjectClass +{ + GstObjectClass parent_class; +}; + +GType _gst_fake_object_type = 0; + +//static GstObjectClass *parent_class = NULL; +//static guint gst_fake_object_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_fake_object_get_type (void) +{ + if (!_gst_fake_object_type) { + static const GTypeInfo fake_object_info = { + sizeof (GstFakeObjectClass), + NULL, //gst_fake_object_base_class_init, + NULL, //gst_fake_object_base_class_finalize, + NULL, //(GClassInitFunc) gst_fake_object_class_init, + NULL, + NULL, + sizeof (GstFakeObject), + 0, + NULL, //(GInstanceInitFunc) gst_fake_object_init, + NULL + }; + + _gst_fake_object_type = g_type_register_static (GST_TYPE_OBJECT, + "GstFakeObject", &fake_object_info, 0); + } + return _gst_fake_object_type; +} + +/* g_object_new on abstract GstObject should fail */ +START_TEST (test_fail_abstract_new) +{ + GstObject *object; + + ASSERT_CRITICAL (object = g_object_new (gst_object_get_type (), NULL)); + fail_unless (object == NULL, "Created an instance of abstract GstObject"); +} + +END_TEST +/* g_object_new on GstFakeObject should succeed */ +START_TEST (test_fake_object_new) +{ + GstObject *object; + + object = g_object_new (gst_fake_object_get_type (), NULL); + fail_if (object == NULL, "Failed to create instance of GstFakeObject"); + fail_unless (GST_IS_OBJECT (object), + "GstFakeObject instance is not a GstObject"); +} + +END_TEST +/* GstFakeObject name tests */ +START_TEST (test_fake_object_name) +{ + GstObject *object; + gchar *name; + gchar *name2; + + object = g_object_new (gst_fake_object_get_type (), NULL); + + name = gst_object_get_name (object); + fail_if (name == NULL, "Newly created object has no name"); + fail_if (strncmp (name, "fakeobject", 10) != 0, + "Random name %s does not start with Gst", name); + + /* give a random name by setting with NULL; + * GstFakeObject class -> fakeobject%d */ + gst_object_set_name (object, NULL); + name = gst_object_get_name (object); + fail_if (name == NULL, "Random name was not assigned"); + fail_if (strncmp (name, "fakeobject", 10) != 0, + "Random name %s does not start with Gst", name); + g_free (name); + + gst_object_set_name (object, "fake"); + name = gst_object_get_name (object); + fail_if (name == NULL, "Failed to get name of GstFakeObject"); + fail_if (strcmp (name, "fake") != 0, "Name of GstFakeObject is not 'fake'"); + + /* change the gotten name to see that it's a copy and not the original */ + name[0] = 'm'; + name2 = gst_object_get_name (object); + fail_if (strcmp (name2, "fake") != 0, + "Copy of object name affected actual object name"); + g_free (name); + g_free (name2); +} + +END_TEST +/* thread function for threaded name change test */ + gpointer thread_name_object (GstObject * object) +{ + gchar *thread_id = g_strdup_printf ("%p", g_thread_self ()); + + THREAD_START (); + + /* give main thread a head start */ + g_usleep (100000); + + /* write our name repeatedly */ + g_message ("THREAD %s: starting loop\n", thread_id); + while (THREAD_TEST_RUNNING ()) { + gst_object_set_name (object, thread_id); + /* a minimal sleep invokes a thread switch */ + THREAD_SWITCH (); + } + + /* thread is done, so let's return */ + g_message ("THREAD %s: set name\n", thread_id); + g_free (thread_id); + + return NULL; +} + +/* + * main thread sets and gets name while other threads set the name + * constantly; fails because lock is released inbetween set and get + */ + +START_TEST (test_fake_object_name_threaded_wrong) +{ + GstObject *object; + gchar *name; + gint i; + gboolean expected_failure = FALSE; + + g_message ("\nTEST: set/get without lock\n"); + + object = g_object_new (gst_fake_object_get_type (), NULL); + gst_object_set_name (object, "main"); + + MAIN_START_THREADS (5, thread_name_object, object); + + /* start looping and set/get name repeatedly */ + for (i = 0; i < 1000; ++i) { + gst_object_set_name (object, "main"); + THREAD_SWITCH (); + name = gst_object_get_name (object); + if (strcmp (name, "main") != 0) { + g_message ("MAIN: expected failure during run %d\n", i); + expected_failure = TRUE; + g_free (name); + break; + } + g_free (name); + } + MAIN_STOP_THREADS (); + + fail_unless (expected_failure, "name did not get changed"); +} + +END_TEST +/* + * main thread sets and gets name directly on struct inside the object lock + * succeed because lock is held during set/get, and threads are locked out + */ +START_TEST (test_fake_object_name_threaded_right) +{ + GstObject *object; + gchar *name; + gint i; + + g_message ("\nTEST: set/get inside lock\n"); + + object = g_object_new (gst_fake_object_get_type (), NULL); + gst_object_set_name (object, "main"); + + MAIN_START_THREADS (5, thread_name_object, object); + + /* start looping and set/get name repeatedly */ + for (i = 0; i < 1000; ++i) { + GST_LOCK (object); + g_free (GST_OBJECT_NAME (object)); + GST_OBJECT_NAME (object) = g_strdup ("main"); + THREAD_SWITCH (); + name = g_strdup (GST_OBJECT_NAME (object)); + GST_UNLOCK (object); + + fail_unless (strcmp (name, "main") == 0, + "Name got changed while lock held during run %d", i); + g_free (name); + } + MAIN_STOP_THREADS (); +} + +END_TEST +/* + * main thread creates lots of objects + * child threads sets default names on objects + * then main thread checks uniqueness of object names + */ + GList * object_list = NULL; +gint num_objects = 1000; +gint num_threads = 5; + +/* thread function for threaded default name change test */ +gpointer +thread_name_object_default (int *i) +{ + int j; + + THREAD_START (); + + for (j = *i; j < num_objects; j += num_threads) { + GstObject *o = GST_OBJECT (g_list_nth_data (object_list, j)); + + /* g_message ("THREAD %p: setting default name on object %d\n", + g_thread_self (), j); */ + gst_object_set_name (o, NULL); + THREAD_SWITCH (); + } + + /* thread is done, so let's return */ + g_message ("THREAD %p: set name\n", g_thread_self ()); + g_free (i); + + return NULL; +} + +static gint +gst_object_name_compare (GstObject * o, GstObject * p) +{ + gint result; + + GST_LOCK (o); + GST_LOCK (p); + + if (o->name == NULL && p->name == NULL) { + result = 0; + } else if (o->name == NULL) { + result = -1; + } else if (p->name == NULL) { + result = 1; + } else { + result = strcmp (o->name, p->name); + } + + GST_UNLOCK (p); + GST_UNLOCK (o); + + return result; +} + +START_TEST (test_fake_object_name_threaded_unique) +{ + GstObject *object; + gint i; + gint *ip; + gchar *name1, *name2; + GList *l; + + g_message ("\nTEST: uniqueness of default names\n"); + + for (i = 0; i < num_objects; ++i) { + object = g_object_new (gst_fake_object_get_type (), NULL); + object_list = g_list_append (object_list, object); + } + + MAIN_INIT (); + + mark_point (); + for (i = 0; i < num_threads; ++i) { + ip = g_new (gint, 1); + *ip = i; + MAIN_START_THREAD_FUNCTION (i, thread_name_object_default, ip); + } + + mark_point (); + MAIN_SYNCHRONIZE (); + mark_point (); + MAIN_STOP_THREADS (); + + /* sort GList based on object name */ + /* FIXME: sort and test */ + g_list_sort (object_list, (GCompareFunc) gst_object_name_compare); + + name1 = gst_object_get_name (GST_OBJECT (object_list->data)); + for (l = object_list->next; l->next; l = l->next) { + g_message ("object with name %s\n", name1); + name2 = gst_object_get_name (GST_OBJECT (l->data)); + fail_if (strcmp (name1, name2) == 0, "Two objects with name %s", name2); + g_free (name1); + name1 = name2; + } + + /* free stuff */ + g_list_foreach (object_list, (GFunc) g_object_unref, NULL); +} + +END_TEST +/* test: try renaming a parented object, make sure it fails */ + Suite * gst_object_suite (void) +{ + Suite *s = suite_create ("GstObject"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_fake_object_new); + tcase_add_test (tc_chain, test_fake_object_name); + tcase_add_test (tc_chain, test_fake_object_name_threaded_wrong); + tcase_add_test (tc_chain, test_fake_object_name_threaded_right); + tcase_add_test (tc_chain, test_fake_object_name_threaded_unique); + //tcase_add_checked_fixture (tc_chain, setup, teardown); + + /* SEGV tests go last so we can debug the others */ + tcase_add_test_raise_signal (tc_chain, test_fail_abstract_new, SIGSEGV); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_object_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; +} diff --git a/tests/check/gst/gstpad.c b/tests/check/gst/gstpad.c new file mode 100644 index 0000000..3875900 --- /dev/null +++ b/tests/check/gst/gstpad.c @@ -0,0 +1,127 @@ +/* GStreamer + * Copyright (C) <2005> Thomas Vander Stichele + * + * gstpad.c: Unit test for GstPad + * + * 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 "../gstcheck.h" + +START_TEST (test_link) +{ + GstPad *src, *sink; + GstPadTemplate *srct; //, *sinkt; + GstCaps *srcc; + + GstPadLinkReturn ret; + gchar *name; + + src = gst_pad_new ("source", GST_PAD_SRC); + fail_if (src == NULL); + + name = gst_pad_get_name (src); + fail_unless (strcmp (name, "source") == 0); + + sink = gst_pad_new ("sink", GST_PAD_SINK); + fail_if (sink == NULL); + + /* linking without templates should fail */ + ret = gst_pad_link (src, sink); + fail_unless (ret == GST_PAD_LINK_NOFORMAT); + + ASSERT_CRITICAL (gst_pad_get_pad_template (NULL)); + + srct = gst_pad_get_pad_template (src); + fail_unless (srct == NULL); + + /* create caps */ + srcc = gst_caps_new_any (); + gst_pad_set_caps (src, srcc); + gst_pad_set_caps (sink, srcc); + + /* linking with any caps should succeed */ + ret = gst_pad_link (src, sink); + fail_unless (ret == GST_PAD_LINK_OK); +} + +END_TEST +/* threaded link/unlink */ +/* use globals */ + GstPad * src, *sink; + +void +thread_link_unlink (gpointer data) +{ + THREAD_START (); + + while (THREAD_TEST_RUNNING ()) { + gst_pad_link (src, sink); + gst_pad_unlink (src, sink); + THREAD_SWITCH (); + } +} + +START_TEST (test_link_unlink_threaded) +{ + GstCaps *caps; + int i; + + src = gst_pad_new ("source", GST_PAD_SRC); + fail_if (src == NULL); + sink = gst_pad_new ("sink", GST_PAD_SINK); + fail_if (sink == NULL); + + caps = gst_caps_new_any (); + gst_pad_set_caps (src, caps); + gst_pad_set_caps (sink, caps); + + MAIN_START_THREADS (5, thread_link_unlink, NULL); + for (i = 0; i < 1000; ++i) { + gst_pad_is_linked (src); + gst_pad_is_linked (sink); + THREAD_SWITCH (); + } + MAIN_STOP_THREADS (); +} +END_TEST Suite * +gst_pad_suite (void) +{ + Suite *s = suite_create ("GstPad"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_link); + tcase_add_test (tc_chain, test_link_unlink_threaded); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_pad_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; +} diff --git a/tests/check/gstcheck.c b/tests/check/gstcheck.c new file mode 100644 index 0000000..53af0a6 --- /dev/null +++ b/tests/check/gstcheck.c @@ -0,0 +1,85 @@ +/* GStreamer + * + * Common code for GStreamer unittests + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "gstcheck.h" + + +/* logging function for tests + * a test uses g_message() to log a debug line + * a gst unit test can be run with GST_TEST_DEBUG env var set to see the + * messages + */ + +gboolean _gst_check_threads_running = FALSE; +GList *thread_list = NULL; +GMutex *mutex; +GCond *start_cond; /* used to notify main thread of thread startups */ +GCond *sync_cond; /* used to synchronize all threads and main thread */ + +gboolean _gst_check_debug = FALSE; +gboolean _gst_check_raised_critical = FALSE; +gboolean _gst_check_expecting_log = FALSE; + +void gst_check_log_message_func + (const gchar * log_domain, GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + if (_gst_check_debug) { + g_print (message); + } +} + +void gst_check_log_critical_func + (const gchar * log_domain, GLogLevelFlags log_level, + const gchar * message, gpointer user_data) +{ + if (!_gst_check_expecting_log) { + g_print ("\n\nUnexpected critical/warning: %s\n", message); + fail ("Unexpected critical/warning: %s", message); + } + + if (_gst_check_debug) { + g_print ("\nExpected critical/warning: %s\n", message); + } + + if (log_level & G_LOG_LEVEL_CRITICAL) + _gst_check_raised_critical = TRUE; +} + +/* initialize GStreamer testing */ +void +gst_check_init (int *argc, char **argv[]) +{ + gst_init (argc, argv); + + if (g_getenv ("GST_TEST_DEBUG")) + _gst_check_debug = TRUE; + + g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, gst_check_log_message_func, + NULL); + g_log_set_handler (NULL, G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); + g_log_set_handler ("GStreamer", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); + g_log_set_handler ("GLib-GObject", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + gst_check_log_critical_func, NULL); +} diff --git a/tests/check/gstcheck.h b/tests/check/gstcheck.h new file mode 100644 index 0000000..ce452eb --- /dev/null +++ b/tests/check/gstcheck.h @@ -0,0 +1,150 @@ +/* GStreamer + * + * Common code for GStreamer unittests + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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. + */ + +#ifndef __GST_CHECK_H__ +#define __GST_CHECK_H__ + +#include +#include +#include + +#include + +#include + + +/* logging function for tests + * a test uses g_message() to log a debug line + * a gst unit test can be run with GST_TEST_DEBUG env var set to see the + * messages + */ +extern gboolean _gst_check_threads_running; +extern gboolean _gst_check_raised_critical; +extern gboolean _gst_check_expecting_log; + +void gst_check_init (int *argc, char **argv[]); + +/*** + * thread test macros and variables + */ +extern GList *thread_list; +extern GMutex *mutex; +extern GCond *start_cond; /* used to notify main thread of thread startups */ +extern GCond *sync_cond; /* used to synchronize all threads and main thread */ + +#define MAIN_START_THREADS(count, function, data) \ +MAIN_INIT(); \ +MAIN_START_THREAD_FUNCTIONS(count, function, data); \ +MAIN_SYNCHRONIZE(); + +#define MAIN_INIT() \ +G_STMT_START { \ + _gst_check_threads_running = TRUE; \ + \ + mutex = g_mutex_new (); \ + start_cond = g_cond_new (); \ + sync_cond = g_cond_new (); \ +} G_STMT_END; + +#define MAIN_START_THREAD_FUNCTIONS(count, function, data) \ +G_STMT_START { \ + int i; \ + for (i = 0; i < count; ++i) { \ + MAIN_START_THREAD_FUNCTION (i, function, data); \ + } \ +} G_STMT_END; + +#define MAIN_START_THREAD_FUNCTION(i, function, data) \ +G_STMT_START { \ + GThread *thread = NULL; \ + g_message ("MAIN: creating thread %d\n", i); \ + g_mutex_lock (mutex); \ + thread = g_thread_create ((GThreadFunc) function, data, \ + TRUE, NULL); \ + /* wait for thread to signal us that it's ready */ \ + g_message ("MAIN: waiting for thread %d\n", i); \ + g_cond_wait (start_cond, mutex); \ + g_mutex_unlock (mutex); \ + \ + thread_list = g_list_append (thread_list, thread); \ +} G_STMT_END; + + +#define MAIN_SYNCHRONIZE() \ +G_STMT_START { \ + g_message ("MAIN: synchronizing\n"); \ + g_cond_broadcast (sync_cond); \ + g_message ("MAIN: synchronized\n"); \ +} G_STMT_END; + +#define MAIN_STOP_THREADS() \ +G_STMT_START { \ + _gst_check_threads_running = FALSE; \ + \ + /* join all threads */ \ + g_message ("MAIN: joining\n"); \ + g_list_foreach (thread_list, (GFunc) g_thread_join, NULL); \ + g_message ("MAIN: joined\n"); \ +} G_STMT_END; + +#define THREAD_START() \ +THREAD_STARTED(); \ +THREAD_SYNCHRONIZE(); + +#define THREAD_STARTED() \ +G_STMT_START { \ + /* signal main thread that we started */ \ + g_message ("THREAD %p: started\n", g_thread_self ()); \ + g_mutex_lock (mutex); \ + g_cond_signal (start_cond); \ +} G_STMT_END; + +#define THREAD_SYNCHRONIZE() \ +G_STMT_START { \ + /* synchronize everyone */ \ + g_message ("THREAD %p: syncing\n", g_thread_self ()); \ + g_cond_wait (sync_cond, mutex); \ + g_message ("THREAD %p: synced\n", g_thread_self ()); \ + g_mutex_unlock (mutex); \ +} G_STMT_END; + +#define THREAD_SWITCH() \ +G_STMT_START { \ + /* a minimal sleep is a context switch */ \ + g_usleep (1); \ +} G_STMT_END; + +#define THREAD_TEST_RUNNING() (_gst_check_threads_running == TRUE) + +#define ASSERT_CRITICAL(code) \ +G_STMT_START { \ + _gst_check_expecting_log = TRUE; \ + _gst_check_raised_critical = FALSE; \ + code; \ + _fail_unless (_gst_check_raised_critical, __FILE__, __LINE__, \ + "Expected g_critical, got nothing: '"#code"'"); \ + _gst_check_expecting_log = FALSE; \ +} G_STMT_END + + +#endif /* __GST_CHECK_H__ */ + diff --git a/tests/check/libs/.gitignore b/tests/check/libs/.gitignore new file mode 100644 index 0000000..5ad589e --- /dev/null +++ b/tests/check/libs/.gitignore @@ -0,0 +1 @@ +gdp diff --git a/tests/check/libs/gdp.c b/tests/check/libs/gdp.c new file mode 100644 index 0000000..df79c8d --- /dev/null +++ b/tests/check/libs/gdp.c @@ -0,0 +1,277 @@ +/* GStreamer + * + * unit test for data protocol + * + * Copyright (C) <2004> Thomas Vander Stichele + * + * 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 "../gstcheck.h" + +#include +#include "libs/gst/dataprotocol/dp-private.h" /* private header */ + +/* test our method of reading and writing headers using TO/FROM_BE */ +START_TEST (test_conversion) +{ + guint8 array[9]; + guint8 write_array[9]; + guint16 read_two, expect_two; + guint32 read_four, expect_four; + guint64 read_eight, expect_eight; + int i; + + for (i = 0; i < 9; ++i) { + array[i] = i * 0x10; + } + + /* read 8 16 bits */ + for (i = 0; i < 8; ++i) { + read_two = GST_READ_UINT16_BE (array + i); + expect_two = array[i] * (1 << 8) + array[i + 1]; + fail_unless (read_two == expect_two, + "GST_READ_UINT16_BE %d: read %d != %d\n", i, read_two, expect_two); + } + + /* write 8 16 bits */ + for (i = 0; i < 8; ++i) { + GST_WRITE_UINT16_BE (&write_array[i], read_two); + fail_unless (memcmp (array + 7, write_array + i, 2) == 0, + "GST_WRITE_UINT16_BE %d: memcmp failed", i); + } + + /* read 5 32 bits */ + for (i = 0; i < 5; ++i) { + read_four = GST_READ_UINT32_BE (array + i); + expect_four = array[i] * (1 << 24) + array[i + 1] * (1 << 16) + + array[i + 2] * (1 << 8) + array[i + 3]; + fail_unless (read_four == expect_four, + "GST_READ_UINT32_BE %d: read %d != %d\n", i, read_four, expect_four); + } + + /* read 2 64 bits */ + for (i = 0; i < 2; ++i) { + read_eight = GST_READ_UINT64_BE (array + i); + expect_eight = array[i] * (1LL << 56) + array[i + 1] * (1LL << 48) + + array[i + 2] * (1LL << 40) + array[i + 3] * (1LL << 32) + + array[i + 4] * (1 << 24) + array[i + 5] * (1 << 16) + + array[i + 6] * (1 << 8) + array[i + 7]; + fail_unless (read_eight == expect_eight, + "GST_READ_UINT64_BE %d: read %" G_GUINT64_FORMAT + " != %" G_GUINT64_FORMAT "\n", i, read_eight, expect_eight); + } + + /* write 1 64 bit */ + GST_WRITE_UINT64_BE (&write_array[0], read_eight); + fail_unless (memcmp (array + 1, write_array, 8) == 0, + "GST_WRITE_UINT64_BE: memcmp failed"); +} + +END_TEST +/* test creation of header from buffer and back again */ +START_TEST (test_buffer) +{ + GstBuffer *buffer; + GstBuffer *newbuffer; + + guint header_length; + guint8 *header; + + /* create buffer */ + g_message ("Creating a new 8-byte buffer with ts 0.5 sec, dur 1 sec\n"); + buffer = gst_buffer_new_and_alloc (8); + GST_BUFFER_TIMESTAMP (buffer) = (GstClockTime) (GST_SECOND * 0.5); + GST_BUFFER_DURATION (buffer) = (GstClockTime) GST_SECOND; + GST_BUFFER_OFFSET (buffer) = (guint64) 10; + GST_BUFFER_OFFSET_END (buffer) = (guint64) 19; + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_IN_CAPS); + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_SUBBUFFER); + memmove (GST_BUFFER_DATA (buffer), "a buffer", 8); + + /* create a buffer with CRC checking */ + fail_unless (gst_dp_header_from_buffer (buffer, GST_DP_HEADER_FLAG_CRC, + &header_length, &header), "Could not create header from buffer."); + + /* validate the header */ + fail_unless (gst_dp_validate_header (header_length, header), + "Could not validate header"); + /* create a new, empty buffer with the right size */ + newbuffer = gst_dp_buffer_from_header (header_length, header); + fail_unless (newbuffer != NULL, "Could not create a new buffer from header"); + fail_unless (GST_IS_BUFFER (newbuffer), "Created buffer is not a GstBuffer"); + /* read/copy the data */ + memmove (GST_BUFFER_DATA (newbuffer), GST_BUFFER_DATA (buffer), + GST_BUFFER_SIZE (buffer)); + /* validate the buffer */ + fail_unless (gst_dp_validate_payload (header_length, header, + GST_BUFFER_DATA (newbuffer)), "Could not validate payload"); + + g_message ("new buffer timestamp: %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (newbuffer))); + g_message ("new buffer duration: %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_BUFFER_DURATION (newbuffer))); + g_message ("new buffer offset: %" G_GUINT64_FORMAT "\n", + GST_BUFFER_OFFSET (newbuffer)); + g_message ("new buffer offset_end: %" G_GUINT64_FORMAT "\n", + GST_BUFFER_OFFSET_END (newbuffer)); + fail_unless (GST_BUFFER_TIMESTAMP (newbuffer) == + GST_BUFFER_TIMESTAMP (buffer), "Timestamps don't match !"); + fail_unless (GST_BUFFER_DURATION (newbuffer) == GST_BUFFER_DURATION (buffer), + "Durations don't match !"); + fail_unless (GST_BUFFER_OFFSET (newbuffer) == GST_BUFFER_OFFSET (buffer), + "Offsets don't match !"); + fail_unless (GST_BUFFER_OFFSET_END (newbuffer) == + GST_BUFFER_OFFSET_END (buffer), "Offset ends don't match !"); + fail_if (GST_BUFFER_FLAG_IS_SET (newbuffer, GST_BUFFER_SUBBUFFER), + "GST_BUFFER_SUBBUFFER flag should not have been copied !"); + fail_unless (GST_BUFFER_FLAG_IS_SET (newbuffer, GST_BUFFER_IN_CAPS), + "GST_BUFFER_IN_CAPS flag should have been copied !"); + + g_free (header); +} + +END_TEST +START_TEST (test_caps) +{ + gchar *string, *newstring; + GstCaps *caps, *newcaps; + + guint header_length; + guint8 *header, *payload; + + caps = gst_caps_from_string ("audio/x-raw-float, " + "rate = (int) [ 11025, 48000 ], " + "channels = (int) [ 1, 2 ], " "endianness = (int) BYTE_ORDER, " + "width = (int) 32, " "buffer-frames = (int) 0"); + string = gst_caps_to_string (caps); + g_message ("Created caps: %s\n", string); + fail_unless (gst_dp_packet_from_caps (caps, 0, &header_length, &header, + &payload), "Could not create packet from caps."); + + /* validate the packet */ + fail_unless (gst_dp_validate_packet (header_length, header, payload), + "Could not validate packet"); + newcaps = gst_dp_caps_from_packet (header_length, header, payload); + fail_unless (newcaps != NULL, "Could not create caps from packet"); + //g_return_val_if_fail (GST_IS_CAPS (newcaps), -1); + newstring = gst_caps_to_string (newcaps); + g_message ("Received caps: %s\n", newstring); + fail_unless (strcmp (string, newstring) == 0, + "Created caps do not match original caps"); + g_free (string); + g_free (newstring); +} + +END_TEST +START_TEST (test_event) +{ + GstEvent *send; + GstEvent *receive; + guint header_length; + guint8 *header, *payload; + + g_message ("Testing EOS event at 1s\n"); + send = gst_event_new (GST_EVENT_EOS); + GST_EVENT_TIMESTAMP (send) = GST_SECOND; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from eos event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("EOS, timestamp %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_EOS, + "Received event is not EOS"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND, + "EOS timestamp is not 1.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); + + g_message ("Testing FLUSH event at 2s\n"); + send = gst_event_new (GST_EVENT_FLUSH); + GST_EVENT_TIMESTAMP (send) = GST_SECOND * 2; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from flush event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("Flush, timestamp %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_FLUSH, + "Received event is not flush"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND * 2, + "Flush timestamp is not 2.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); + + g_message ("Testing SEEK event with 1 second at 3 seconds\n"); + send = gst_event_new_seek (GST_FORMAT_TIME, GST_SECOND); + GST_EVENT_TIMESTAMP (send) = GST_SECOND * 3; + fail_unless (gst_dp_packet_from_event (send, GST_DP_HEADER_FLAG_CRC, + &header_length, &header, &payload), + "Could not create packet from seek event"); + + receive = gst_dp_event_from_packet (header_length, header, payload); + + g_message ("Seek, timestamp %" GST_TIME_FORMAT ", to %" GST_TIME_FORMAT "\n", + GST_TIME_ARGS (GST_EVENT_TIMESTAMP (receive)), + GST_TIME_ARGS (GST_EVENT_SEEK_OFFSET (receive))); + fail_unless (GST_EVENT_TYPE (receive) == GST_EVENT_SEEK, + "Returned event is not seek"); + fail_unless (GST_EVENT_TIMESTAMP (receive) == GST_SECOND * 3, + "Seek timestamp is not 3.0 sec"); + fail_unless (GST_EVENT_SEEK_FORMAT (receive) == GST_FORMAT_TIME, + "Seek format is not time"); + fail_unless (GST_EVENT_SEEK_OFFSET (receive) == GST_SECOND, + "Seek offset is not 1.0 sec"); + gst_event_unref (send); + gst_event_unref (receive); +} +END_TEST Suite * +gst_object_suite (void) +{ + Suite *s = suite_create ("data protocol"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_conversion); + tcase_add_test (tc_chain, test_buffer); + tcase_add_test (tc_chain, test_caps); + tcase_add_test (tc_chain, test_event); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = gst_object_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + gst_dp_init (); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/pipelines/simple-launch-lines.c b/tests/check/pipelines/simple-launch-lines.c new file mode 100644 index 0000000..f812392 --- /dev/null +++ b/tests/check/pipelines/simple-launch-lines.c @@ -0,0 +1,127 @@ +/* GStreamer + * Copyright (C) 2005 Andy Wingo + * + * simple_launch_lines.c: Unit test for simple pipelines + * + * 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 "../gstcheck.h" + + +static GstElement * +setup_pipeline (gchar * pipe_descr) +{ + GstElement *pipeline; + + pipeline = gst_parse_launch (pipe_descr, NULL); + g_return_val_if_fail (GST_IS_PIPELINE (pipeline), NULL); + return pipeline; +} + +/* events is a mask of expected events. tevent is the expected terminal event. + the poll call will time out after half a second. + */ +static void +run_pipeline (GstElement * pipe, gchar * descr, + GstMessageType events, GstMessageType tevent) +{ + GstBus *bus; + GstMessageType revent; + + bus = gst_element_get_bus (pipe); + g_assert (bus); + gst_element_set_state (pipe, GST_STATE_PLAYING); + + while (1) { + revent = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 2); + + /* always have to pop the message before getting back into poll */ + if (revent != GST_MESSAGE_UNKNOWN) + gst_message_unref (gst_bus_pop (bus)); + + if (revent == tevent) { + break; + } else if (revent == GST_MESSAGE_UNKNOWN) { + g_critical ("Unexpected timeout in gst_bus_poll, looking for %d: %s", + tevent, descr); + break; + } else if (revent & events) { + continue; + } + g_critical ("Unexpected message received of type %d, looking for %d: %s", + revent, tevent, descr); + } + + gst_element_set_state (pipe, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (pipe)); +} + +START_TEST (test_2_elements) +{ + gchar *s; + + s = "fakesrc has-loop=false ! fakesink has-loop=true"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN); + + s = "fakesrc has-loop=true ! fakesink has-loop=false"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN); + + s = "fakesrc has-loop=false num-buffers=10 ! fakesink has-loop=true"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS); + + s = "fakesrc has-loop=true num-buffers=10 ! fakesink has-loop=false"; + run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_EOS); + + s = "fakesrc has-loop=false ! fakesink has-loop=false"; + ASSERT_CRITICAL (run_pipeline (setup_pipeline (s), s, + GST_MESSAGE_STATE_CHANGED, GST_MESSAGE_UNKNOWN)); +} +END_TEST Suite * +simple_launch_lines_suite (void) +{ + Suite *s = suite_create ("Pipelines"); + TCase *tc_chain = tcase_create ("linear"); + + /* time out after 20s, not the default 3 */ + tcase_set_timeout (tc_chain, 20); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_2_elements); + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = simple_launch_lines_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; +}