object: Add _set_name() test on parented object
[platform/upstream/gstreamer.git] / tests / check / gst / gstobject.c
1 /* GStreamer
2  *
3  * unit test for GstObject
4  *
5  * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gst/check/gstcheck.h>
28
29 /*
30   Create a fake subclass
31  */
32 typedef struct _GstFakeObjectClass GstFakeObjectClass;
33 typedef struct _GstFakeObject GstFakeObject;
34
35 struct _GstFakeObject
36 {
37   GstObject object;
38 };
39
40 struct _GstFakeObjectClass
41 {
42   GstObjectClass parent_class;
43 };
44
45 //static GstObjectClass *parent_class = NULL;
46 //static guint gst_fake_object_signals[LAST_SIGNAL] = { 0 };
47
48 static GType
49 gst_fake_object_get_type (void)
50 {
51   static volatile gsize fake_object_type = 0;
52
53   if (g_once_init_enter (&fake_object_type)) {
54     GType type;
55     static const GTypeInfo fake_object_info = {
56       sizeof (GstFakeObjectClass),
57       NULL,                     //gst_fake_object_base_class_init,
58       NULL,                     //gst_fake_object_base_class_finalize,
59       NULL,                     //(GClassInitFunc) gst_fake_object_class_init,
60       NULL,
61       NULL,
62       sizeof (GstFakeObject),
63       0,
64       NULL,                     //(GInstanceInitFunc) gst_fake_object_init,
65       NULL
66     };
67
68     type = g_type_register_static (GST_TYPE_OBJECT,
69         "GstFakeObject", &fake_object_info, 0);
70     g_once_init_leave (&fake_object_type, type);
71   }
72   return fake_object_type;
73 }
74
75 /* g_object_new on GstFakeObject should succeed */
76 GST_START_TEST (test_fake_object_new)
77 {
78   GstObject *object;
79
80   object = g_object_new (gst_fake_object_get_type (), NULL);
81   fail_if (object == NULL, "Failed to create instance of GstFakeObject");
82   fail_unless (GST_IS_OBJECT (object),
83       "GstFakeObject instance is not a GstObject");
84   gst_object_unref (object);
85 }
86
87 GST_END_TEST;
88
89 static void
90 notify_name (GObject * object, GParamSpec * pspec, gint * out_count)
91 {
92   *out_count += 1;
93 }
94
95 /* GstFakeObject name tests */
96 GST_START_TEST (test_fake_object_name)
97 {
98   GstObject *object, *parent;
99   gint count = 0;
100   gchar *name;
101   gchar *name2;
102
103   object = g_object_new (gst_fake_object_get_type (), NULL);
104   g_signal_connect (object, "notify::name", G_CALLBACK (notify_name), &count);
105
106   name = gst_object_get_name (object);
107   fail_if (name == NULL, "Newly created object has no name");
108   fail_if (strncmp (name, "fakeobject", 10) != 0,
109       "Random name %s does not start with Gst", name);
110   g_free (name);
111
112   /* give a random name by setting with NULL;
113    * GstFakeObject class -> fakeobject%d */
114   fail_unless (gst_object_set_name (object, NULL), "Could not set name");
115   name = gst_object_get_name (object);
116   fail_if (name == NULL, "Random name was not assigned");
117   fail_if (strncmp (name, "fakeobject", 10) != 0,
118       "Random name %s does not start with Gst", name);
119   g_free (name);
120   fail_unless (count == 1, "Name change was not notified");
121
122   /* also test the property code path */
123   g_object_set (object, "name", "fake", NULL);
124   name = gst_object_get_name (object);
125   fail_if (name == NULL, "Failed to get name of GstFakeObject");
126   fail_if (strcmp (name, "fake") != 0, "Name of GstFakeObject is not 'fake'");
127   fail_if (count > 2, "Name change was notified multiple time");
128   fail_unless (count == 2, "Name change was not notified");
129
130   /* change the gotten name to see that it's a copy and not the original */
131   name[0] = 'm';
132   name2 = gst_object_get_name (object);
133   fail_if (strcmp (name2, "fake") != 0,
134       "Copy of object name affected actual object name");
135   g_free (name);
136   g_free (name2);
137
138   /* add a parent and ensure name cannot be changed */
139   parent = g_object_new (gst_fake_object_get_type (), NULL);
140   gst_object_set_parent (object, parent);
141   fail_if (gst_object_set_name (object, "broken"),
142       "Could set name on parented object");
143
144   gst_object_unref (parent);
145 }
146
147 GST_END_TEST;
148
149 /* thread function for threaded name change test */
150 static gpointer
151 thread_name_object (GstObject * object)
152 {
153   gchar *thread_id = g_strdup_printf ("%p", g_thread_self ());
154
155   THREAD_START ();
156
157   /* give main thread a head start */
158   g_usleep (100000);
159
160   /* write our name repeatedly */
161   g_message ("THREAD %s: starting loop", thread_id);
162   while (THREAD_TEST_RUNNING ()) {
163     gst_object_set_name (object, thread_id);
164     /* a minimal sleep invokes a thread switch */
165     THREAD_SWITCH ();
166   }
167
168   /* thread is done, so let's return */
169   g_message ("THREAD %s: set name", thread_id);
170   g_free (thread_id);
171
172   return NULL;
173 }
174
175 #if 0
176 GST_START_TEST (test_fake_object_name_threaded_wrong)
177 {
178   GstObject *object;
179   gchar *name;
180   gint i;
181   gboolean expected_failure = FALSE;
182
183   g_message ("\nTEST: set/get without lock");
184
185   object = g_object_new (gst_fake_object_get_type (), NULL);
186   gst_object_set_name (object, "main");
187
188   MAIN_START_THREADS (5, thread_name_object, object);
189
190   /* start looping and set/get name repeatedly */
191   for (i = 0; i < 1000; ++i) {
192     gst_object_set_name (object, "main");
193     THREAD_SWITCH ();
194     name = gst_object_get_name (object);
195     if (strcmp (name, "main") != 0) {
196       g_message ("MAIN: expected failure during run %d", i);
197       expected_failure = TRUE;
198       g_free (name);
199       break;
200     }
201     g_free (name);
202   }
203   MAIN_STOP_THREADS ();
204
205   gst_object_unref (object);
206
207   fail_unless (expected_failure, "name did not get changed");
208 }
209
210 GST_END_TEST;
211 #endif
212
213 /*
214  * main thread sets and gets name directly on struct inside the object lock
215  * succeed because lock is held during set/get, and threads are locked out
216  */
217 GST_START_TEST (test_fake_object_name_threaded_right)
218 {
219   GstObject *object;
220   gchar *name;
221   gint i;
222
223   g_message ("\nTEST: set/get inside lock");
224
225   object = g_object_new (gst_fake_object_get_type (), NULL);
226   gst_object_set_name (object, "main");
227
228   MAIN_START_THREADS (5, thread_name_object, object);
229
230   /* start looping and set/get name repeatedly */
231   for (i = 0; i < 1000; ++i) {
232     GST_OBJECT_LOCK (object);
233     g_free (GST_OBJECT_NAME (object));
234     GST_OBJECT_NAME (object) = g_strdup ("main");
235     THREAD_SWITCH ();
236     name = g_strdup (GST_OBJECT_NAME (object));
237     GST_OBJECT_UNLOCK (object);
238
239     fail_unless (strcmp (name, "main") == 0,
240         "Name got changed while lock held during run %d", i);
241     g_free (name);
242   }
243   MAIN_STOP_THREADS ();
244   gst_object_unref (object);
245 }
246
247 GST_END_TEST;
248 /*
249  * main thread creates lots of objects
250  * child threads sets default names on objects
251  * then main thread checks uniqueness of object names
252  */
253
254 static GList *object_list = NULL;
255 static gint num_objects = 1000;
256 static gint num_threads = 5;
257
258 /* thread function for threaded default name change test */
259 static gpointer
260 thread_name_object_default (int *i)
261 {
262   int j;
263
264   THREAD_START ();
265
266   for (j = *i; j < num_objects; j += num_threads) {
267     GstObject *o = GST_OBJECT (g_list_nth_data (object_list, j));
268
269     /* g_message ("THREAD %p: setting default name on object %d",
270        g_thread_self (), j); */
271     gst_object_set_name (o, NULL);
272     THREAD_SWITCH ();
273   }
274
275   /* thread is done, so let's return */
276   g_message ("THREAD %p: set name", g_thread_self ());
277   g_free (i);
278
279   return NULL;
280 }
281
282 static gint
283 gst_object_name_compare (GstObject * o, GstObject * p)
284 {
285   gint result;
286
287   GST_OBJECT_LOCK (o);
288   GST_OBJECT_LOCK (p);
289
290   if (o->name == NULL && p->name == NULL) {
291     result = 0;
292   } else if (o->name == NULL) {
293     result = -1;
294   } else if (p->name == NULL) {
295     result = 1;
296   } else {
297     result = strcmp (o->name, p->name);
298   }
299
300   GST_OBJECT_UNLOCK (p);
301   GST_OBJECT_UNLOCK (o);
302
303   return result;
304 }
305
306 GST_START_TEST (test_fake_object_name_threaded_unique)
307 {
308   GstObject *object;
309   gint i;
310   gint *ip;
311   gchar *name1, *name2;
312   GList *l;
313
314   g_message ("\nTEST: uniqueness of default names");
315
316   for (i = 0; i < num_objects; ++i) {
317     object = g_object_new (gst_fake_object_get_type (), NULL);
318     object_list = g_list_append (object_list, object);
319   }
320
321   MAIN_INIT ();
322
323   mark_point ();
324   for (i = 0; i < num_threads; ++i) {
325     ip = g_new (gint, 1);
326     *ip = i;
327     MAIN_START_THREAD_FUNCTION (i, thread_name_object_default, ip);
328   }
329
330   mark_point ();
331   MAIN_SYNCHRONIZE ();
332   mark_point ();
333   MAIN_STOP_THREADS ();
334
335   /* sort GList based on object name */
336   /* FIXME: sort and test */
337   object_list =
338       g_list_sort (object_list, (GCompareFunc) gst_object_name_compare);
339
340   name1 = gst_object_get_name (GST_OBJECT (object_list->data));
341   for (l = object_list->next; l->next; l = l->next) {
342     g_message ("object with name %s", name1);
343     name2 = gst_object_get_name (GST_OBJECT (l->data));
344     fail_if (strcmp (name1, name2) == 0, "Two objects with name %s", name2);
345     g_free (name1);
346     name1 = name2;
347   }
348   g_free (name1);
349
350   /* free stuff */
351   g_list_foreach (object_list, (GFunc) g_object_unref, NULL);
352 }
353
354 GST_END_TEST;
355
356 /* parentage test on GstFakeObject */
357 GST_START_TEST (test_fake_object_parentage)
358 {
359   GstObject *object1, *object2;
360   GstObject *parent;
361   gboolean result;
362
363   /* create new object */
364   object1 = g_object_new (gst_fake_object_get_type (), NULL);
365   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
366   fail_unless (GST_IS_OBJECT (object1),
367       "GstFakeObject instance is not a GstObject");
368   fail_unless (g_object_is_floating (object1),
369       "GstFakeObject instance is not floating");
370
371   /* check the parent */
372   parent = gst_object_get_parent (object1);
373   fail_if (parent != NULL, "GstFakeObject has parent");
374   /* try to set a NULL parent, this should give a warning */
375   ASSERT_CRITICAL (result = gst_object_set_parent (object1, NULL));
376   fail_if (result, "GstFakeObject accepted NULL parent");
377   /* try to set itself as parent, we expect a warning here */
378   ASSERT_CRITICAL (result = gst_object_set_parent (object1, object1));
379   fail_if (result, "GstFakeObject accepted itself as parent");
380
381   /* _has_parent always returns FALSE if there is no parent */
382   fail_if (gst_object_has_as_parent (object1, NULL));
383   fail_if (gst_object_has_as_parent (NULL, object1));
384   fail_if (gst_object_has_as_parent (object1, object1));
385
386   /* should still be floating */
387   fail_unless (g_object_is_floating (object1),
388       "GstFakeObject instance is not floating");
389
390   /* create another object */
391   object2 = g_object_new (gst_fake_object_get_type (), NULL);
392   fail_if (object2 == NULL,
393       "Failed to create another instance of GstFakeObject");
394   fail_unless (GST_IS_OBJECT (object2),
395       "second GstFakeObject instance is not a GstObject");
396   fail_unless (g_object_is_floating (object1),
397       "GstFakeObject instance is not floating");
398
399   result = gst_object_has_as_parent (object1, object2);
400   fail_if (result, "GstFakeObject has a parent");
401
402   /* try to set other object as parent */
403   result = gst_object_set_parent (object1, object2);
404   fail_unless (result, "GstFakeObject could not accept other object as parent");
405
406   /* should not be floating anymore */
407   fail_if (g_object_is_floating (object1),
408       "GstFakeObject instance is still floating");
409   /* parent should still be floating */
410   fail_unless (g_object_is_floating (object2),
411       "GstFakeObject instance is not floating");
412
413   /* check the parent */
414   fail_unless (gst_object_has_as_parent (object1, object2));
415
416   /* any other combination is invalid */
417   fail_if (gst_object_has_as_parent (object2, object1));
418   fail_if (gst_object_has_as_parent (object1, NULL));
419   fail_if (gst_object_has_as_parent (object2, NULL));
420   fail_if (gst_object_has_as_parent (NULL, object1));
421   fail_if (gst_object_has_as_parent (NULL, object2));
422   fail_if (gst_object_has_as_parent (object1, object1));
423   fail_if (gst_object_has_as_parent (object2, object2));
424
425   /* try to set other object as parent again */
426   result = gst_object_set_parent (object1, object2);
427   fail_if (result, "GstFakeObject could set parent twice");
428
429   /* ref before unparenting */
430   gst_object_ref (object1);
431   /* clear parent of object */
432   gst_object_unparent (object1);
433
434   /* check the parent */
435   parent = gst_object_get_parent (object1);
436   fail_if (parent != NULL, "GstFakeObject has parent");
437
438   /* object should not be floating */
439   fail_if (g_object_is_floating (object1),
440       "GstFakeObject instance is floating again");
441
442   gst_object_unref (object1);
443   gst_object_unref (object2);
444 }
445
446 GST_END_TEST;
447
448 /* parentage test dispose on GstFakeObject, since our testcase
449  * does not handle the parent relation completely, the parent does
450  * not hold a ref to the child, we cannot dispose the parent to
451  * dipose the child as well. This test needs to be run with DEBUG
452  * info to check if the finalize methods are called correctly. */
453 GST_START_TEST (test_fake_object_parentage_dispose)
454 {
455   GstObject *object1, *object2;
456   gboolean result;
457
458   object1 = g_object_new (gst_fake_object_get_type (), NULL);
459   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
460
461   object2 = g_object_new (gst_fake_object_get_type (), NULL);
462   fail_if (object2 == NULL, "Failed to create instance of GstFakeObject");
463
464   /* try to set other object as parent */
465   result = gst_object_set_parent (object1, object2);
466   fail_unless (result, "GstFakeObject could not accept other object as parent");
467
468   /* clear parent of object */
469   gst_object_unparent (object1);
470
471   /* now dispose parent */
472   gst_object_unref (object2);
473 }
474
475 GST_END_TEST;
476
477 GST_START_TEST (test_fake_object_has_as_ancestor)
478 {
479   GstObject *object1, *object2, *object3, *object4;
480   gboolean result;
481
482   object1 = g_object_new (gst_fake_object_get_type (), NULL);
483   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
484
485   object2 = g_object_new (gst_fake_object_get_type (), NULL);
486   fail_if (object2 == NULL, "Failed to create instance of GstFakeObject");
487
488   object3 = g_object_new (gst_fake_object_get_type (), NULL);
489   fail_if (object3 == NULL, "Failed to create instance of GstFakeObject");
490
491   object4 = g_object_new (gst_fake_object_get_type (), NULL);
492   fail_if (object4 == NULL, "Failed to create instance of GstFakeObject");
493
494   /* try to set other object as parent */
495   result = gst_object_set_parent (object1, object3);
496   fail_unless (result, "GstFakeObject could not accept other object as parent");
497   result = gst_object_set_parent (object2, object3);
498   fail_unless (result, "GstFakeObject could not accept other object as parent");
499   result = gst_object_set_parent (object3, object4);
500   fail_unless (result, "GstFakeObject could not accept other object as parent");
501
502   /* Hierarchy:
503    *  object4
504    *   `- object3
505    *       |- object2
506    *       `- object1
507    */
508
509   /* An object isn't its own parent, but it is its own ancestor */
510   fail_if (gst_object_has_as_parent (object1, object1));
511   fail_unless (gst_object_has_as_ancestor (object1, object1));
512
513   fail_if (gst_object_has_as_parent (object4, object4));
514   fail_unless (gst_object_has_as_ancestor (object4, object4));
515
516   /* direct parents */
517   fail_unless (gst_object_has_as_parent (object1, object3));
518   fail_unless (gst_object_has_as_ancestor (object1, object3));
519
520   fail_unless (gst_object_has_as_parent (object2, object3));
521   fail_unless (gst_object_has_as_ancestor (object2, object3));
522
523   fail_unless (gst_object_has_as_parent (object3, object4));
524   fail_unless (gst_object_has_as_ancestor (object3, object4));
525
526   /* grandparents */
527   fail_if (gst_object_has_as_parent (object1, object4));
528   fail_unless (gst_object_has_as_ancestor (object1, object4));
529
530   fail_if (gst_object_has_as_parent (object2, object4));
531   fail_unless (gst_object_has_as_ancestor (object2, object4));
532
533   /* not ancestors */
534   fail_if (gst_object_has_as_parent (object1, object2));
535   fail_if (gst_object_has_as_ancestor (object1, object2));
536
537   fail_if (gst_object_has_as_parent (object3, object1));
538   fail_if (gst_object_has_as_ancestor (object3, object1));
539
540   fail_if (gst_object_has_as_parent (object4, object1));
541   fail_if (gst_object_has_as_ancestor (object4, object1));
542
543   fail_if (gst_object_has_as_parent (object4, object3));
544   fail_if (gst_object_has_as_ancestor (object4, object3));
545
546   /* unparent everything */
547   gst_object_unparent (object3);
548   gst_object_unparent (object2);
549   gst_object_unparent (object1);
550
551   /* now dispose objects */
552   gst_object_unref (object4);
553 }
554
555 GST_END_TEST;
556
557 /* test: try renaming a parented object, make sure it fails */
558
559 static Suite *
560 gst_object_suite (void)
561 {
562   Suite *s = suite_create ("GstObject");
563   TCase *tc_chain = tcase_create ("general");
564
565   /* turn off timeout */
566   tcase_set_timeout (tc_chain, 60);
567
568   suite_add_tcase (s, tc_chain);
569   tcase_add_test (tc_chain, test_fake_object_new);
570   tcase_add_test (tc_chain, test_fake_object_name);
571 #if 0
572   tcase_add_test (tc_chain, test_fake_object_name_threaded_wrong);
573 #endif
574   tcase_add_test (tc_chain, test_fake_object_name_threaded_right);
575   tcase_add_test (tc_chain, test_fake_object_name_threaded_unique);
576   tcase_add_test (tc_chain, test_fake_object_parentage);
577   tcase_add_test (tc_chain, test_fake_object_parentage_dispose);
578
579   tcase_add_test (tc_chain, test_fake_object_has_as_ancestor);
580   //tcase_add_checked_fixture (tc_chain, setup, teardown);
581
582   return s;
583 }
584
585 GST_CHECK_MAIN (gst_object);