gstreamer/tests/check/gst/gstbufferlist.c
Miguel Paris 2ef0fd1862 bufferlist: foreach: always remove as parent if buffer is changed
In case the buffer is not writable, the parent (the BufferList) is not
removed before calling func. So if it is changed, the parent (the BufferList)
of the previous buffer should be removed after calling func.
2020-03-18 14:43:08 +01:00

586 lines
17 KiB
C

/* GStreamer
*
* unit test for GstBufferList
*
* Copyright (C) 2009 Axis Communications <dev-gstreamer at axis dot com>
* @author Jonas Holmberg <jonas dot holmberg at axis dot com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/check/gstcheck.h>
#include <gst/gstbufferlist.h>
#include <string.h>
static GstBufferList *list;
static void
setup (void)
{
list = gst_buffer_list_new ();
}
static void
cleanup (void)
{
gst_buffer_list_unref (list);
list = NULL;
}
GST_START_TEST (test_add_and_iterate)
{
GstBuffer *buf1;
GstBuffer *buf2;
/* buffer list is initially empty */
fail_unless (gst_buffer_list_length (list) == 0);
ASSERT_CRITICAL (gst_buffer_list_insert (list, 0, NULL));
ASSERT_CRITICAL (gst_buffer_list_insert (NULL, 0, NULL));
buf1 = gst_buffer_new ();
/* add a group of 2 buffers */
fail_unless (gst_buffer_list_length (list) == 0);
ASSERT_CRITICAL (gst_buffer_list_insert (list, -1, NULL));
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 1);
gst_buffer_list_add (list, buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 1); /* list takes ownership */
fail_unless (gst_buffer_list_length (list) == 1);
buf2 = gst_buffer_new ();
gst_buffer_list_add (list, buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 1);
fail_unless (gst_buffer_list_length (list) == 2);
}
GST_END_TEST;
GST_START_TEST (test_remove)
{
GstBuffer *buf;
/* buffer list is initially empty */
fail_unless (gst_buffer_list_length (list) == 0);
buf = gst_buffer_new ();
/* add our own ref so it stays alive after removal from the list */
buf = gst_buffer_ref (buf);
/* add a buffer */
fail_unless (gst_buffer_list_length (list) == 0);
ASSERT_CRITICAL (gst_buffer_list_insert (list, -1, NULL));
ASSERT_BUFFER_REFCOUNT (buf, "buf", 2);
gst_buffer_list_add (list, buf);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 2); /* list takes ownership */
fail_unless (gst_buffer_list_length (list) == 1);
gst_buffer_list_remove (list, 0, 1);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
gst_buffer_unref (buf);
fail_unless (gst_buffer_list_length (list) == 0);
}
GST_END_TEST;
GST_START_TEST (test_make_writable)
{
GstBufferList *wlist;
GstBuffer *buf1;
GstBuffer *buf2;
GstBuffer *buf3;
GstBuffer *buf;
/* add buffers to list */
buf1 = gst_buffer_new_allocate (NULL, 1, NULL);
gst_buffer_list_add (list, buf1);
buf2 = gst_buffer_new_allocate (NULL, 2, NULL);
buf3 = gst_buffer_new_allocate (NULL, 3, NULL);
gst_buffer_list_add (list, gst_buffer_append (buf2, buf3));
/* making it writable with refcount 1 returns the same list */
wlist = gst_buffer_list_make_writable (list);
fail_unless (wlist == list);
fail_unless_equals_int (gst_buffer_list_length (list), 2);
buf = gst_buffer_list_get (list, 0);
fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 1);
fail_unless_equals_int (gst_buffer_get_size (buf), 1);
buf = gst_buffer_list_get (list, 1);
fail_unless (buf == buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 1);
fail_unless_equals_int (gst_buffer_n_memory (buf), 2);
/* making it writable with refcount 2 returns a copy of the list with
* increased refcount on the buffers in the list */
gst_buffer_list_ref (list);
fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (list), 2);
wlist = gst_buffer_list_make_writable (list);
fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (list), 1);
fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (wlist), 1);
fail_unless (wlist != list);
/* check list */
fail_unless_equals_int (gst_buffer_list_length (list), 2);
buf = gst_buffer_list_get (list, 0);
fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2);
fail_unless_equals_int (gst_buffer_get_size (buf), 1);
buf = gst_buffer_list_get (list, 1);
fail_unless (buf == buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 2);
fail_unless_equals_int (gst_buffer_n_memory (buf), 2);
/* check wlist */
fail_unless_equals_int (gst_buffer_list_length (wlist), 2);
buf = gst_buffer_list_get (wlist, 0);
fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2);
fail_unless_equals_int (gst_buffer_get_size (buf), 1);
buf = gst_buffer_list_get (wlist, 1);
fail_unless (buf == buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 2);
fail_unless_equals_int (gst_buffer_n_memory (buf), 2);
gst_buffer_list_unref (wlist);
/* list will be unrefed in cleanup hook */
}
GST_END_TEST;
GST_START_TEST (test_copy)
{
GstBufferList *list_copy;
GstBuffer *buf1;
GstBuffer *buf2;
GstBuffer *buf3;
GstBuffer *buf;
/* add buffers to the list */
buf1 = gst_buffer_new_allocate (NULL, 1, NULL);
gst_buffer_list_add (list, buf1);
buf2 = gst_buffer_new_allocate (NULL, 2, NULL);
buf3 = gst_buffer_new_allocate (NULL, 3, NULL);
gst_buffer_list_add (list, gst_buffer_append (buf2, buf3));
/* make a copy */
list_copy = gst_buffer_list_copy (list);
fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (list) == 1);
fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (list_copy) == 1);
fail_unless (list_copy != list);
fail_unless_equals_int (gst_buffer_list_length (list_copy), 2);
buf = gst_buffer_list_get (list_copy, 0);
fail_unless (buf == buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 2);
fail_unless_equals_int (gst_buffer_get_size (buf1), 1);
buf = gst_buffer_list_get (list_copy, 1);
fail_unless (buf == buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 2);
fail_unless_equals_int (gst_buffer_get_size (buf2), 5);
fail_unless_equals_int (gst_buffer_n_memory (buf2), 2);
gst_buffer_list_unref (list_copy);
}
GST_END_TEST;
GST_START_TEST (test_copy_deep)
{
GstBufferList *list_copy;
GstMapInfo info, sinfo;
GstBuffer *buf1;
GstBuffer *buf2;
GstBuffer *buf_copy;
/* add buffers to the list */
buf1 = gst_buffer_new_allocate (NULL, 1, NULL);
gst_buffer_list_add (list, buf1);
buf2 = gst_buffer_new_allocate (NULL, 2, NULL);
gst_buffer_list_add (list, buf2);
/* make a copy */
list_copy = gst_buffer_list_copy_deep (list);
fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (list) == 1);
fail_unless (GST_MINI_OBJECT_REFCOUNT_VALUE (list_copy) == 1);
fail_unless (list_copy != list);
fail_unless_equals_int (gst_buffer_list_length (list_copy), 2);
buf_copy = gst_buffer_list_get (list_copy, 0);
/* each buffer in the list is copied and must point to different memory */
fail_unless (buf_copy != buf1);
ASSERT_BUFFER_REFCOUNT (buf1, "buf1", 1);
fail_unless_equals_int (gst_buffer_get_size (buf1), 1);
buf_copy = gst_buffer_list_get (list_copy, 1);
fail_unless (buf_copy != buf2);
ASSERT_BUFFER_REFCOUNT (buf2, "buf2", 1);
fail_unless_equals_int (gst_buffer_get_size (buf2), 2);
fail_unless (gst_buffer_map (buf2, &info, GST_MAP_READ));
fail_unless (gst_buffer_map (buf_copy, &sinfo, GST_MAP_READ));
/* NOTE that data is refcounted */
fail_unless (info.size == sinfo.size);
/* copy_deep() forces new GstMemory to be used */
fail_unless (info.data != sinfo.data);
gst_buffer_unmap (buf_copy, &sinfo);
gst_buffer_unmap (buf2, &info);
gst_buffer_list_unref (list_copy);
}
GST_END_TEST;
typedef struct
{
GstBuffer *buf[2];
guint iter;
} ForeachData;
static gboolean
foreach_func1 (GstBuffer ** buffer, guint idx, ForeachData * data)
{
fail_unless (buffer != NULL);
fail_unless (GST_IS_BUFFER (*buffer));
fail_unless (*buffer == data->buf[idx]);
data->iter++;
return TRUE;
}
static gboolean
foreach_func3 (GstBuffer ** buffer, guint idx, ForeachData * data)
{
fail_unless (idx == 0);
fail_unless (buffer != NULL);
fail_unless (GST_IS_BUFFER (*buffer));
fail_unless (*buffer == data->buf[idx]);
data->iter++;
return FALSE;
}
static gboolean
foreach_func4 (GstBuffer ** buffer, guint idx, ForeachData * data)
{
fail_unless (idx == 0);
fail_unless (buffer != NULL);
fail_unless (GST_IS_BUFFER (*buffer));
fail_unless (*buffer == data->buf[data->iter]);
/* remove first */
if (*buffer == data->buf[0]) {
gst_buffer_unref (*buffer);
*buffer = NULL;
}
data->iter++;
return TRUE;
}
static gboolean
foreach_func5 (GstBuffer ** buffer, guint idx, ForeachData * data)
{
fail_unless (buffer != NULL);
fail_unless (GST_IS_BUFFER (*buffer));
data->iter++;
return TRUE;
}
GST_START_TEST (test_foreach)
{
GstBuffer *buf2, *buf3;
ForeachData data;
/* add buffers to the list */
data.buf[0] = gst_buffer_new_allocate (NULL, 1, NULL);
gst_buffer_list_add (list, data.buf[0]);
buf2 = gst_buffer_new_allocate (NULL, 2, NULL);
buf3 = gst_buffer_new_allocate (NULL, 3, NULL);
data.buf[1] = gst_buffer_append (buf2, buf3);
gst_buffer_list_add (list, data.buf[1]);
fail_unless (gst_buffer_list_get (list, 0) == data.buf[0]);
fail_unless (gst_buffer_list_get (list, 1) == data.buf[1]);
/* iterate everything */
data.iter = 0;
gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func1, &data);
fail_unless (data.iter == 2);
/* iterate only the first buffer */
data.iter = 0;
gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func3, &data);
fail_unless (data.iter == 1);
/* remove the first buffer */
data.iter = 0;
gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func4, &data);
fail_unless (data.iter == 2);
fail_unless (gst_buffer_list_get (list, 0) == data.buf[1]);
fail_unless_equals_int (gst_buffer_list_length (list), 1);
/* iterate everything, just one more buffer now */
data.iter = 0;
gst_buffer_list_foreach (list, (GstBufferListFunc) foreach_func5, &data);
fail_unless (data.iter == 1);
}
GST_END_TEST;
/* make sure everything is fine if we exceed the pre-allocated size */
GST_START_TEST (test_expand_and_remove)
{
GArray *arr;
GstBuffer *buf;
guint i, idx, num, counter = 0;
gst_buffer_list_unref (list);
arr = g_array_new (FALSE, FALSE, sizeof (guint));
list = gst_buffer_list_new_sized (1);
for (i = 0; i < 250; ++i) {
num = ++counter;
buf = gst_buffer_new_allocate (NULL, num, NULL);
gst_buffer_list_add (list, buf);
g_array_append_val (arr, num);
}
for (i = 0; i < 250; ++i) {
num = ++counter;
buf = gst_buffer_new_allocate (NULL, num, NULL);
idx = g_random_int_range (0, gst_buffer_list_length (list));
gst_buffer_list_insert (list, idx, buf);
g_array_insert_val (arr, idx, num);
}
/* make sure the list looks like it should */
fail_unless_equals_int (arr->len, gst_buffer_list_length (list));
for (i = 0; i < arr->len; ++i) {
buf = gst_buffer_list_get (list, i);
num = gst_buffer_get_size (buf);
fail_unless_equals_int (num, g_array_index (arr, guint, i));
}
for (i = 0; i < 44; ++i) {
num = g_random_int_range (1, 5);
idx = g_random_int_range (0, gst_buffer_list_length (list) - num);
gst_buffer_list_remove (list, idx, num);
g_array_remove_range (arr, idx, num);
}
/* make sure the list still looks like it should */
fail_unless_equals_int (arr->len, gst_buffer_list_length (list));
for (i = 0; i < arr->len; ++i) {
buf = gst_buffer_list_get (list, i);
num = gst_buffer_get_size (buf);
fail_unless_equals_int (num, g_array_index (arr, guint, i));
}
for (i = 0; i < 500; ++i) {
num = ++counter;
buf = gst_buffer_new_allocate (NULL, num, NULL);
gst_buffer_list_add (list, buf);
g_array_append_val (arr, num);
}
for (i = 0; i < 500; ++i) {
num = ++counter;
buf = gst_buffer_new_allocate (NULL, num, NULL);
idx = g_random_int_range (0, gst_buffer_list_length (list));
gst_buffer_list_insert (list, idx, buf);
g_array_insert_val (arr, idx, num);
}
/* make sure the list still looks like it should */
fail_unless_equals_int (arr->len, gst_buffer_list_length (list));
for (i = 0; i < arr->len; ++i) {
buf = gst_buffer_list_get (list, i);
num = gst_buffer_get_size (buf);
fail_unless_equals_int (num, g_array_index (arr, guint, i));
}
g_array_unref (arr);
}
GST_END_TEST;
GST_START_TEST (test_get_writable)
{
GstBuffer *buf, *writable_buf;
/* buffer list is initially empty */
fail_unless (gst_buffer_list_length (list) == 0);
/* Add 2 buffers */
buf = gst_buffer_new ();
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
gst_buffer_list_add (list, buf);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1); /* list takes ownership */
fail_unless (gst_buffer_list_length (list) == 1);
fail_unless (buf == gst_buffer_list_get_writable (list, 0));
fail_unless (buf == gst_buffer_list_get (list, 0));
/* extra ref to make buffer no longer writable */
gst_buffer_ref (buf);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 2);
fail_unless (buf == gst_buffer_list_get (list, 0));
ASSERT_BUFFER_REFCOUNT (buf, "buf", 2);
/* should make a copy to make it writable */
writable_buf = gst_buffer_list_get_writable (list, 0);
fail_if (buf == writable_buf);
ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
ASSERT_BUFFER_REFCOUNT (writable_buf, "writable_buf", 1);
gst_buffer_unref (buf);
}
GST_END_TEST;
GST_START_TEST (test_calc_size)
{
gst_buffer_list_add (list, gst_buffer_new_wrapped (g_strdup ("Hello"), 5));
gst_buffer_list_add (list, gst_buffer_new_wrapped (g_strdup (", "), 2));
gst_buffer_list_add (list, gst_buffer_new_wrapped (g_strdup ("world!"), 6));
fail_unless_equals_int (5 + 2 + 6, gst_buffer_list_calculate_size (list));
}
GST_END_TEST;
GST_START_TEST (test_new_sized_0)
{
GstBufferList *b = gst_buffer_list_new_sized (0);
gst_buffer_list_unref (b);
}
GST_END_TEST;
GST_START_TEST (test_multiple_mutable_buffer_references)
{
GstBufferList *b = gst_buffer_list_new_sized (1);
GstBuffer *buf1, *buf2;
gst_buffer_list_add (b, gst_buffer_new ());
gst_buffer_list_ref (b);
buf1 = gst_buffer_list_get (b, 0);
buf2 = gst_buffer_list_get (b, 0);
fail_if (gst_buffer_list_is_writable (b));
fail_if (gst_buffer_is_writable (buf1));
fail_if (gst_buffer_is_writable (buf2));
/* Immutable operations that have optimizations for writable buffers
* will cause problems now, e.g. read-mapping the buffer and merging
* together all the memories and replacing them in the buffer */
gst_buffer_list_unref (b);
gst_buffer_list_unref (b);
}
GST_END_TEST;
static gboolean
foreach_replace_buffer (GstBuffer ** buffer, guint idx, gpointer user_data)
{
gst_buffer_unref (*buffer);
*buffer = gst_buffer_new ();
return TRUE;
}
GST_START_TEST (test_foreach_modify_non_writeable_list)
{
GstBufferList *b = gst_buffer_list_new_sized (1);
GstBuffer *buf;
buf = gst_buffer_new ();
gst_buffer_list_add (b, gst_buffer_ref (buf));
gst_buffer_list_ref (b);
fail_if (gst_buffer_list_is_writable (b));
ASSERT_CRITICAL (gst_buffer_list_foreach (b, foreach_replace_buffer, NULL));
fail_unless (gst_buffer_list_get (b, 0) == buf);
gst_buffer_list_unref (b);
gst_buffer_list_unref (b);
gst_buffer_unref (buf);
}
GST_END_TEST;
GST_START_TEST (test_foreach_modify_writeable_list)
{
GstBufferList *b = gst_buffer_list_new_sized (1);
GstBuffer *buf;
buf = gst_buffer_new ();
gst_buffer_list_add (b, gst_buffer_ref (buf));
fail_unless (gst_buffer_list_is_writable (b));
gst_buffer_list_foreach (b, foreach_replace_buffer, NULL);
gst_buffer_list_unref (b);
gst_buffer_unref (buf);
}
GST_END_TEST;
static Suite *
gst_buffer_list_suite (void)
{
Suite *s = suite_create ("GstBufferList");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_checked_fixture (tc_chain, setup, cleanup);
tcase_add_test (tc_chain, test_add_and_iterate);
tcase_add_test (tc_chain, test_remove);
tcase_add_test (tc_chain, test_make_writable);
tcase_add_test (tc_chain, test_copy);
tcase_add_test (tc_chain, test_copy_deep);
tcase_add_test (tc_chain, test_foreach);
tcase_add_test (tc_chain, test_expand_and_remove);
tcase_add_test (tc_chain, test_get_writable);
tcase_add_test (tc_chain, test_calc_size);
tcase_add_test (tc_chain, test_new_sized_0);
tcase_add_test (tc_chain, test_multiple_mutable_buffer_references);
tcase_add_test (tc_chain, test_foreach_modify_non_writeable_list);
tcase_add_test (tc_chain, test_foreach_modify_writeable_list);
return s;
}
GST_CHECK_MAIN (gst_buffer_list);