4f166afebe8e6646449bb01607aadc72f1efba46
[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;
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   gst_object_unref (object);
139 }
140
141 GST_END_TEST;
142
143 /* thread function for threaded name change test */
144 static gpointer
145 thread_name_object (GstObject * object)
146 {
147   gchar *thread_id = g_strdup_printf ("%p", g_thread_self ());
148
149   THREAD_START ();
150
151   /* give main thread a head start */
152   g_usleep (100000);
153
154   /* write our name repeatedly */
155   g_message ("THREAD %s: starting loop", thread_id);
156   while (THREAD_TEST_RUNNING ()) {
157     gst_object_set_name (object, thread_id);
158     /* a minimal sleep invokes a thread switch */
159     THREAD_SWITCH ();
160   }
161
162   /* thread is done, so let's return */
163   g_message ("THREAD %s: set name", thread_id);
164   g_free (thread_id);
165
166   return NULL;
167 }
168
169 #if 0
170 GST_START_TEST (test_fake_object_name_threaded_wrong)
171 {
172   GstObject *object;
173   gchar *name;
174   gint i;
175   gboolean expected_failure = FALSE;
176
177   g_message ("\nTEST: set/get without lock");
178
179   object = g_object_new (gst_fake_object_get_type (), NULL);
180   gst_object_set_name (object, "main");
181
182   MAIN_START_THREADS (5, thread_name_object, object);
183
184   /* start looping and set/get name repeatedly */
185   for (i = 0; i < 1000; ++i) {
186     gst_object_set_name (object, "main");
187     THREAD_SWITCH ();
188     name = gst_object_get_name (object);
189     if (strcmp (name, "main") != 0) {
190       g_message ("MAIN: expected failure during run %d", i);
191       expected_failure = TRUE;
192       g_free (name);
193       break;
194     }
195     g_free (name);
196   }
197   MAIN_STOP_THREADS ();
198
199   gst_object_unref (object);
200
201   fail_unless (expected_failure, "name did not get changed");
202 }
203
204 GST_END_TEST;
205 #endif
206
207 /*
208  * main thread sets and gets name directly on struct inside the object lock
209  * succeed because lock is held during set/get, and threads are locked out
210  */
211 GST_START_TEST (test_fake_object_name_threaded_right)
212 {
213   GstObject *object;
214   gchar *name;
215   gint i;
216
217   g_message ("\nTEST: set/get inside lock");
218
219   object = g_object_new (gst_fake_object_get_type (), NULL);
220   gst_object_set_name (object, "main");
221
222   MAIN_START_THREADS (5, thread_name_object, object);
223
224   /* start looping and set/get name repeatedly */
225   for (i = 0; i < 1000; ++i) {
226     GST_OBJECT_LOCK (object);
227     g_free (GST_OBJECT_NAME (object));
228     GST_OBJECT_NAME (object) = g_strdup ("main");
229     THREAD_SWITCH ();
230     name = g_strdup (GST_OBJECT_NAME (object));
231     GST_OBJECT_UNLOCK (object);
232
233     fail_unless (strcmp (name, "main") == 0,
234         "Name got changed while lock held during run %d", i);
235     g_free (name);
236   }
237   MAIN_STOP_THREADS ();
238   gst_object_unref (object);
239 }
240
241 GST_END_TEST;
242 /*
243  * main thread creates lots of objects
244  * child threads sets default names on objects
245  * then main thread checks uniqueness of object names
246  */
247
248 static GList *object_list = NULL;
249 static gint num_objects = 1000;
250 static gint num_threads = 5;
251
252 /* thread function for threaded default name change test */
253 static gpointer
254 thread_name_object_default (int *i)
255 {
256   int j;
257
258   THREAD_START ();
259
260   for (j = *i; j < num_objects; j += num_threads) {
261     GstObject *o = GST_OBJECT (g_list_nth_data (object_list, j));
262
263     /* g_message ("THREAD %p: setting default name on object %d",
264        g_thread_self (), j); */
265     gst_object_set_name (o, NULL);
266     THREAD_SWITCH ();
267   }
268
269   /* thread is done, so let's return */
270   g_message ("THREAD %p: set name", g_thread_self ());
271   g_free (i);
272
273   return NULL;
274 }
275
276 static gint
277 gst_object_name_compare (GstObject * o, GstObject * p)
278 {
279   gint result;
280
281   GST_OBJECT_LOCK (o);
282   GST_OBJECT_LOCK (p);
283
284   if (o->name == NULL && p->name == NULL) {
285     result = 0;
286   } else if (o->name == NULL) {
287     result = -1;
288   } else if (p->name == NULL) {
289     result = 1;
290   } else {
291     result = strcmp (o->name, p->name);
292   }
293
294   GST_OBJECT_UNLOCK (p);
295   GST_OBJECT_UNLOCK (o);
296
297   return result;
298 }
299
300 GST_START_TEST (test_fake_object_name_threaded_unique)
301 {
302   GstObject *object;
303   gint i;
304   gint *ip;
305   gchar *name1, *name2;
306   GList *l;
307
308   g_message ("\nTEST: uniqueness of default names");
309
310   for (i = 0; i < num_objects; ++i) {
311     object = g_object_new (gst_fake_object_get_type (), NULL);
312     object_list = g_list_append (object_list, object);
313   }
314
315   MAIN_INIT ();
316
317   mark_point ();
318   for (i = 0; i < num_threads; ++i) {
319     ip = g_new (gint, 1);
320     *ip = i;
321     MAIN_START_THREAD_FUNCTION (i, thread_name_object_default, ip);
322   }
323
324   mark_point ();
325   MAIN_SYNCHRONIZE ();
326   mark_point ();
327   MAIN_STOP_THREADS ();
328
329   /* sort GList based on object name */
330   /* FIXME: sort and test */
331   object_list =
332       g_list_sort (object_list, (GCompareFunc) gst_object_name_compare);
333
334   name1 = gst_object_get_name (GST_OBJECT (object_list->data));
335   for (l = object_list->next; l->next; l = l->next) {
336     g_message ("object with name %s", name1);
337     name2 = gst_object_get_name (GST_OBJECT (l->data));
338     fail_if (strcmp (name1, name2) == 0, "Two objects with name %s", name2);
339     g_free (name1);
340     name1 = name2;
341   }
342   g_free (name1);
343
344   /* free stuff */
345   g_list_foreach (object_list, (GFunc) g_object_unref, NULL);
346 }
347
348 GST_END_TEST;
349
350 /* parentage test on GstFakeObject */
351 GST_START_TEST (test_fake_object_parentage)
352 {
353   GstObject *object1, *object2;
354   GstObject *parent;
355   gboolean result;
356
357   /* create new object */
358   object1 = g_object_new (gst_fake_object_get_type (), NULL);
359   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
360   fail_unless (GST_IS_OBJECT (object1),
361       "GstFakeObject instance is not a GstObject");
362   fail_unless (g_object_is_floating (object1),
363       "GstFakeObject instance is not floating");
364
365   /* check the parent */
366   parent = gst_object_get_parent (object1);
367   fail_if (parent != NULL, "GstFakeObject has parent");
368   /* try to set a NULL parent, this should give a warning */
369   ASSERT_CRITICAL (result = gst_object_set_parent (object1, NULL));
370   fail_if (result, "GstFakeObject accepted NULL parent");
371   /* try to set itself as parent, we expect a warning here */
372   ASSERT_CRITICAL (result = gst_object_set_parent (object1, object1));
373   fail_if (result, "GstFakeObject accepted itself as parent");
374
375   /* _has_parent always returns FALSE if there is no parent */
376   fail_if (gst_object_has_as_parent (object1, NULL));
377   fail_if (gst_object_has_as_parent (NULL, object1));
378   fail_if (gst_object_has_as_parent (object1, object1));
379
380   /* should still be floating */
381   fail_unless (g_object_is_floating (object1),
382       "GstFakeObject instance is not floating");
383
384   /* create another object */
385   object2 = g_object_new (gst_fake_object_get_type (), NULL);
386   fail_if (object2 == NULL,
387       "Failed to create another instance of GstFakeObject");
388   fail_unless (GST_IS_OBJECT (object2),
389       "second GstFakeObject instance is not a GstObject");
390   fail_unless (g_object_is_floating (object1),
391       "GstFakeObject instance is not floating");
392
393   result = gst_object_has_as_parent (object1, object2);
394   fail_if (result, "GstFakeObject has a parent");
395
396   /* try to set other object as parent */
397   result = gst_object_set_parent (object1, object2);
398   fail_unless (result, "GstFakeObject could not accept other object as parent");
399
400   /* should not be floating anymore */
401   fail_if (g_object_is_floating (object1),
402       "GstFakeObject instance is still floating");
403   /* parent should still be floating */
404   fail_unless (g_object_is_floating (object2),
405       "GstFakeObject instance is not floating");
406
407   /* check the parent */
408   fail_unless (gst_object_has_as_parent (object1, object2));
409
410   /* any other combination is invalid */
411   fail_if (gst_object_has_as_parent (object2, object1));
412   fail_if (gst_object_has_as_parent (object1, NULL));
413   fail_if (gst_object_has_as_parent (object2, NULL));
414   fail_if (gst_object_has_as_parent (NULL, object1));
415   fail_if (gst_object_has_as_parent (NULL, object2));
416   fail_if (gst_object_has_as_parent (object1, object1));
417   fail_if (gst_object_has_as_parent (object2, object2));
418
419   /* try to set other object as parent again */
420   result = gst_object_set_parent (object1, object2);
421   fail_if (result, "GstFakeObject could set parent twice");
422
423   /* ref before unparenting */
424   gst_object_ref (object1);
425   /* clear parent of object */
426   gst_object_unparent (object1);
427
428   /* check the parent */
429   parent = gst_object_get_parent (object1);
430   fail_if (parent != NULL, "GstFakeObject has parent");
431
432   /* object should not be floating */
433   fail_if (g_object_is_floating (object1),
434       "GstFakeObject instance is floating again");
435
436   gst_object_unref (object1);
437   gst_object_unref (object2);
438 }
439
440 GST_END_TEST;
441
442 /* parentage test dispose on GstFakeObject, since our testcase
443  * does not handle the parent relation completely, the parent does
444  * not hold a ref to the child, we cannot dispose the parent to
445  * dipose the child as well. This test needs to be run with DEBUG
446  * info to check if the finalize methods are called correctly. */
447 GST_START_TEST (test_fake_object_parentage_dispose)
448 {
449   GstObject *object1, *object2;
450   gboolean result;
451
452   object1 = g_object_new (gst_fake_object_get_type (), NULL);
453   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
454
455   object2 = g_object_new (gst_fake_object_get_type (), NULL);
456   fail_if (object2 == NULL, "Failed to create instance of GstFakeObject");
457
458   /* try to set other object as parent */
459   result = gst_object_set_parent (object1, object2);
460   fail_unless (result, "GstFakeObject could not accept other object as parent");
461
462   /* clear parent of object */
463   gst_object_unparent (object1);
464
465   /* now dispose parent */
466   gst_object_unref (object2);
467 }
468
469 GST_END_TEST;
470
471 GST_START_TEST (test_fake_object_has_as_ancestor)
472 {
473   GstObject *object1, *object2, *object3, *object4;
474   gboolean result;
475
476   object1 = g_object_new (gst_fake_object_get_type (), NULL);
477   fail_if (object1 == NULL, "Failed to create instance of GstFakeObject");
478
479   object2 = g_object_new (gst_fake_object_get_type (), NULL);
480   fail_if (object2 == NULL, "Failed to create instance of GstFakeObject");
481
482   object3 = g_object_new (gst_fake_object_get_type (), NULL);
483   fail_if (object3 == NULL, "Failed to create instance of GstFakeObject");
484
485   object4 = g_object_new (gst_fake_object_get_type (), NULL);
486   fail_if (object4 == NULL, "Failed to create instance of GstFakeObject");
487
488   /* try to set other object as parent */
489   result = gst_object_set_parent (object1, object3);
490   fail_unless (result, "GstFakeObject could not accept other object as parent");
491   result = gst_object_set_parent (object2, object3);
492   fail_unless (result, "GstFakeObject could not accept other object as parent");
493   result = gst_object_set_parent (object3, object4);
494   fail_unless (result, "GstFakeObject could not accept other object as parent");
495
496   /* Hierarchy:
497    *  object4
498    *   `- object3
499    *       |- object2
500    *       `- object1
501    */
502
503   /* An object isn't its own parent, but it is its own ancestor */
504   fail_if (gst_object_has_as_parent (object1, object1));
505   fail_unless (gst_object_has_as_ancestor (object1, object1));
506
507   fail_if (gst_object_has_as_parent (object4, object4));
508   fail_unless (gst_object_has_as_ancestor (object4, object4));
509
510   /* direct parents */
511   fail_unless (gst_object_has_as_parent (object1, object3));
512   fail_unless (gst_object_has_as_ancestor (object1, object3));
513
514   fail_unless (gst_object_has_as_parent (object2, object3));
515   fail_unless (gst_object_has_as_ancestor (object2, object3));
516
517   fail_unless (gst_object_has_as_parent (object3, object4));
518   fail_unless (gst_object_has_as_ancestor (object3, object4));
519
520   /* grandparents */
521   fail_if (gst_object_has_as_parent (object1, object4));
522   fail_unless (gst_object_has_as_ancestor (object1, object4));
523
524   fail_if (gst_object_has_as_parent (object2, object4));
525   fail_unless (gst_object_has_as_ancestor (object2, object4));
526
527   /* not ancestors */
528   fail_if (gst_object_has_as_parent (object1, object2));
529   fail_if (gst_object_has_as_ancestor (object1, object2));
530
531   fail_if (gst_object_has_as_parent (object3, object1));
532   fail_if (gst_object_has_as_ancestor (object3, object1));
533
534   fail_if (gst_object_has_as_parent (object4, object1));
535   fail_if (gst_object_has_as_ancestor (object4, object1));
536
537   fail_if (gst_object_has_as_parent (object4, object3));
538   fail_if (gst_object_has_as_ancestor (object4, object3));
539
540   /* unparent everything */
541   gst_object_unparent (object3);
542   gst_object_unparent (object2);
543   gst_object_unparent (object1);
544
545   /* now dispose objects */
546   gst_object_unref (object4);
547 }
548
549 GST_END_TEST;
550
551 /* test: try renaming a parented object, make sure it fails */
552
553 static Suite *
554 gst_object_suite (void)
555 {
556   Suite *s = suite_create ("GstObject");
557   TCase *tc_chain = tcase_create ("general");
558
559   /* turn off timeout */
560   tcase_set_timeout (tc_chain, 60);
561
562   suite_add_tcase (s, tc_chain);
563   tcase_add_test (tc_chain, test_fake_object_new);
564   tcase_add_test (tc_chain, test_fake_object_name);
565 #if 0
566   tcase_add_test (tc_chain, test_fake_object_name_threaded_wrong);
567 #endif
568   tcase_add_test (tc_chain, test_fake_object_name_threaded_right);
569   tcase_add_test (tc_chain, test_fake_object_name_threaded_unique);
570   tcase_add_test (tc_chain, test_fake_object_parentage);
571   tcase_add_test (tc_chain, test_fake_object_parentage_dispose);
572
573   tcase_add_test (tc_chain, test_fake_object_has_as_ancestor);
574   //tcase_add_checked_fixture (tc_chain, setup, teardown);
575
576   return s;
577 }
578
579 GST_CHECK_MAIN (gst_object);