3 * unit test for GstPromise
5 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
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.
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.
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.
23 #include <gst/check/gstcheck.h>
30 GMainContext *main_context;
36 _unlock_thread (GMutex * lock)
38 g_mutex_unlock (lock);
39 return G_SOURCE_REMOVE;
43 _promise_thread (struct event_queue *q)
45 g_mutex_lock (&q->lock);
46 q->main_context = g_main_context_new ();
47 q->main_loop = g_main_loop_new (q->main_context, FALSE);
49 g_cond_broadcast (&q->cond);
50 g_main_context_invoke (q->main_context, (GSourceFunc) _unlock_thread,
53 g_main_loop_run (q->main_loop);
55 g_mutex_lock (&q->lock);
56 g_main_context_unref (q->main_context);
57 q->main_context = NULL;
58 g_main_loop_unref (q->main_loop);
60 g_cond_broadcast (&q->cond);
61 g_mutex_unlock (&q->lock);
67 event_queue_start (struct event_queue *q)
69 g_mutex_lock (&q->lock);
70 q->thread = g_thread_new ("promise-thread", (GThreadFunc) _promise_thread, q);
73 g_cond_wait (&q->cond, &q->lock);
74 g_mutex_unlock (&q->lock);
78 event_queue_stop (struct event_queue *q)
80 g_mutex_lock (&q->lock);
82 g_main_loop_quit (q->main_loop);
83 g_mutex_unlock (&q->lock);
87 event_queue_stop_wait (struct event_queue *q)
89 g_mutex_lock (&q->lock);
90 while (q->main_loop) {
91 g_main_loop_quit (q->main_loop);
92 g_cond_wait (&q->cond, &q->lock);
94 g_mutex_unlock (&q->lock);
96 g_thread_unref (q->thread);
99 static struct event_queue *
100 event_queue_new (void)
102 struct event_queue *q = g_new0 (struct event_queue, 1);
104 GST_LOG ("starting event queue %p", q);
106 g_mutex_init (&q->lock);
107 g_cond_init (&q->cond);
108 event_queue_start (q);
114 event_queue_free (struct event_queue *q)
116 event_queue_stop_wait (q);
118 g_mutex_clear (&q->lock);
119 g_cond_clear (&q->cond);
121 GST_LOG ("stopped event queue %p", q);
127 _enqueue_task (struct event_queue *q, GSourceFunc func, gpointer data,
128 GDestroyNotify notify)
132 source = g_idle_source_new ();
133 g_source_set_priority (source, G_PRIORITY_DEFAULT);
134 g_source_set_callback (source, (GSourceFunc) func, data, notify);
135 g_source_attach (source, q->main_context);
136 g_source_unref (source);
139 GST_START_TEST (test_reply)
143 r = gst_promise_new ();
145 gst_promise_reply (r, NULL);
146 fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
148 gst_promise_unref (r);
153 GST_START_TEST (test_reply_data)
157 const GstStructure *ret;
159 r = gst_promise_new ();
161 s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
162 gst_promise_reply (r, s);
163 fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
164 ret = gst_promise_get_reply (r);
165 fail_unless (gst_structure_is_equal (ret, s));
167 gst_promise_unref (r);
172 GST_START_TEST (test_reply_immutable)
175 GstStructure *s, *ret;
177 r = gst_promise_new ();
179 s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
180 gst_promise_reply (r, s);
181 ret = (GstStructure *) gst_promise_get_reply (r);
183 /* immutable result must not be able to modify the reply */
184 ASSERT_CRITICAL (gst_structure_set (ret, "foo", G_TYPE_STRING, "bar", NULL));
185 fail_unless (gst_structure_get_string (ret, "foo") == NULL);
187 gst_promise_unref (r);
192 GST_START_TEST (test_interrupt)
196 r = gst_promise_new ();
198 gst_promise_interrupt (r);
199 fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_INTERRUPTED);
201 gst_promise_unref (r);
206 GST_START_TEST (test_expire)
210 r = gst_promise_new ();
212 gst_promise_expire (r);
213 fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_EXPIRED);
215 gst_promise_unref (r);
223 GstPromiseResult result;
227 on_change (GstPromise * promise, gpointer user_data)
229 struct change_data *res = user_data;
231 res->result = gst_promise_wait (promise);
232 res->change_count += 1;
235 GST_START_TEST (test_change_func)
238 struct change_data data = { 0, };
240 r = gst_promise_new_with_change_func (on_change, &data, NULL);
241 gst_promise_reply (r, NULL);
242 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
243 fail_unless (data.change_count == 1);
245 gst_promise_unref (r);
250 GST_START_TEST (test_reply_expire)
253 struct change_data data = { 0, };
255 r = gst_promise_new_with_change_func (on_change, &data, NULL);
256 gst_promise_reply (r, NULL);
257 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
258 fail_unless (data.change_count == 1);
259 gst_promise_expire (r);
260 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
261 fail_unless (data.change_count == 1);
263 gst_promise_unref (r);
268 GST_START_TEST (test_reply_discard)
272 /* NULL promise => discard reply */
275 /* no-op, we don't want a reply */
276 gst_promise_reply (r, NULL);
279 gst_promise_unref (r);
284 GST_START_TEST (test_reply_interrupt)
287 struct change_data data = { 0, };
289 r = gst_promise_new_with_change_func (on_change, &data, NULL);
290 gst_promise_reply (r, NULL);
291 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
292 fail_unless (data.change_count == 1);
293 gst_promise_interrupt (r);
294 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
295 fail_unless (data.change_count == 1);
297 gst_promise_unref (r);
302 GST_START_TEST (test_reply_reply)
306 struct change_data data = { 0, };
307 const GstStructure *ret;
309 r = gst_promise_new_with_change_func (on_change, &data, NULL);
310 s = gst_structure_new ("promise", "test", G_TYPE_INT, 1, NULL);
311 gst_promise_reply (r, s);
312 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
313 fail_unless (data.change_count == 1);
314 ASSERT_CRITICAL (gst_promise_reply (r, NULL));
315 fail_unless (gst_promise_wait (r) == GST_PROMISE_RESULT_REPLIED);
316 ret = gst_promise_get_reply (r);
317 fail_unless (gst_structure_is_equal (ret, s));
318 fail_unless (data.result == GST_PROMISE_RESULT_REPLIED);
319 fail_unless (data.change_count == 1);
321 gst_promise_unref (r);
326 GST_START_TEST (test_interrupt_expire)
329 struct change_data data = { 0, };
331 r = gst_promise_new_with_change_func (on_change, &data, NULL);
332 gst_promise_interrupt (r);
333 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
334 fail_unless (data.change_count == 1);
335 gst_promise_expire (r);
336 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
337 fail_unless (data.change_count == 1);
339 gst_promise_unref (r);
344 GST_START_TEST (test_interrupt_reply)
347 struct change_data data = { 0, };
349 r = gst_promise_new_with_change_func (on_change, &data, NULL);
350 gst_promise_interrupt (r);
351 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
352 fail_unless (data.change_count == 1);
353 gst_promise_reply (r, NULL);
354 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
355 fail_unless (data.change_count == 1);
357 gst_promise_unref (r);
362 GST_START_TEST (test_interrupt_interrupt)
365 struct change_data data = { 0, };
367 r = gst_promise_new_with_change_func (on_change, &data, NULL);
368 gst_promise_interrupt (r);
369 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
370 fail_unless (data.change_count == 1);
371 ASSERT_CRITICAL (gst_promise_interrupt (r));
372 fail_unless (data.result == GST_PROMISE_RESULT_INTERRUPTED);
373 fail_unless (data.change_count == 1);
375 gst_promise_unref (r);
380 GST_START_TEST (test_expire_expire)
383 struct change_data data = { 0, };
385 r = gst_promise_new_with_change_func (on_change, &data, NULL);
386 gst_promise_expire (r);
387 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
388 fail_unless (data.change_count == 1);
389 gst_promise_expire (r);
390 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
391 fail_unless (data.change_count == 1);
393 gst_promise_unref (r);
398 GST_START_TEST (test_expire_interrupt)
401 struct change_data data = { 0, };
403 r = gst_promise_new_with_change_func (on_change, &data, NULL);
404 gst_promise_expire (r);
405 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
406 fail_unless (data.change_count == 1);
407 ASSERT_CRITICAL (gst_promise_interrupt (r));
408 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
409 fail_unless (data.change_count == 1);
411 gst_promise_unref (r);
416 GST_START_TEST (test_expire_reply)
419 struct change_data data = { 0, };
421 r = gst_promise_new_with_change_func (on_change, &data, NULL);
422 gst_promise_expire (r);
423 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
424 fail_unless (data.change_count == 1);
425 ASSERT_CRITICAL (gst_promise_reply (r, NULL));
426 fail_unless (data.result == GST_PROMISE_RESULT_EXPIRED);
427 fail_unless (data.change_count == 1);
429 gst_promise_unref (r);
436 struct event_queue *q;
438 GstPromiseResult result;
442 stress_reply (struct stress_item *item)
444 switch (item->result) {
445 case GST_PROMISE_RESULT_REPLIED:
446 gst_promise_reply (item->promise, NULL);
448 case GST_PROMISE_RESULT_INTERRUPTED:
449 gst_promise_interrupt (item->promise);
451 case GST_PROMISE_RESULT_EXPIRED:
452 gst_promise_expire (item->promise);
455 g_assert_not_reached ();
461 GAsyncQueue *push_queue;
462 GAsyncQueue *wait_queue;
467 _push_random_promise (struct event_queue *q)
469 struct stress_queues *s_q = q->user_data;
470 struct stress_item *item;
472 item = g_new0 (struct stress_item, 1);
473 item->promise = gst_promise_new ();
474 while (item->result == GST_PROMISE_RESULT_PENDING)
475 item->result = g_random_int () % 4;
477 g_async_queue_push (s_q->wait_queue, item);
478 g_async_queue_push (s_q->push_queue, item);
482 return G_SOURCE_CONTINUE;
486 _push_stop_promise (struct event_queue *q)
488 struct stress_queues *s_q = q->user_data;
489 gpointer item = GINT_TO_POINTER (1);
491 g_async_queue_push (s_q->wait_queue, item);
492 g_async_queue_push (s_q->push_queue, item);
496 _pop_promise (struct event_queue *q)
498 struct stress_queues *s_q = q->user_data;
499 struct stress_item *item;
501 item = g_async_queue_pop (s_q->push_queue);
503 if (item == (void *) 1)
504 return G_SOURCE_REMOVE;
508 return G_SOURCE_CONTINUE;
512 _wait_promise (struct event_queue *q)
514 struct stress_queues *s_q = q->user_data;
515 struct stress_item *item;
517 item = g_async_queue_pop (s_q->wait_queue);
519 if (item == (void *) 1)
520 return G_SOURCE_REMOVE;
522 fail_unless (gst_promise_wait (item->promise) == item->result);
524 gst_promise_unref (item->promise);
527 return G_SOURCE_CONTINUE;
530 GST_START_TEST (test_stress)
533 struct event_queue *pushers[N_QUEUES];
534 struct event_queue *poppers[N_QUEUES];
535 struct event_queue *waiters[N_QUEUES];
536 struct stress_queues s_q = { 0, };
539 s_q.push_queue = g_async_queue_new ();
540 s_q.wait_queue = g_async_queue_new ();
542 for (i = 0; i < N_QUEUES; i++) {
543 pushers[i] = event_queue_new ();
544 pushers[i]->user_data = &s_q;
545 _enqueue_task (pushers[i], (GSourceFunc) _push_random_promise, pushers[i],
547 waiters[i] = event_queue_new ();
548 waiters[i]->user_data = &s_q;
549 _enqueue_task (waiters[i], (GSourceFunc) _wait_promise, waiters[i], NULL);
550 poppers[i] = event_queue_new ();
551 poppers[i]->user_data = &s_q;
552 _enqueue_task (poppers[i], (GSourceFunc) _pop_promise, poppers[i], NULL);
555 GST_INFO ("all set up, waiting.");
557 GST_INFO ("wait done, cleaning up the test.");
560 struct stress_item *item;
563 for (i = 0; i < N_QUEUES; i++) {
564 event_queue_stop (pushers[i]);
565 event_queue_stop (poppers[i]);
566 event_queue_stop (waiters[i]);
567 _push_stop_promise (pushers[i]);
570 for (i = 0; i < N_QUEUES; i++) {
571 event_queue_free (pushers[i]);
572 event_queue_free (poppers[i]);
575 push_size = g_async_queue_length (s_q.push_queue);
577 /* push through all the promises so all the waits will complete */
578 while ((item = g_async_queue_try_pop (s_q.push_queue))) {
579 if (item == (void *) 1)
584 for (i = 0; i < N_QUEUES; i++)
585 event_queue_free (waiters[i]);
587 GST_INFO ("pushed %" G_GUINT64_FORMAT ", %d leftover in push queue, "
588 "%d leftover in wait queue", s_q.push_count, push_size,
589 g_async_queue_length (s_q.wait_queue));
591 while ((item = g_async_queue_try_pop (s_q.wait_queue))) {
592 if (item == (void *) 1)
595 fail_unless (gst_promise_wait (item->promise) == item->result);
597 gst_promise_unref (item->promise);
602 g_async_queue_unref (s_q.push_queue);
603 g_async_queue_unref (s_q.wait_queue);
609 gst_promise_suite (void)
611 Suite *s = suite_create ("GstPromise");
612 TCase *tc_chain = tcase_create ("general");
614 suite_add_tcase (s, tc_chain);
615 tcase_add_test (tc_chain, test_reply);
616 tcase_add_test (tc_chain, test_reply_data);
617 tcase_add_test (tc_chain, test_reply_immutable);
618 tcase_add_test (tc_chain, test_interrupt);
619 tcase_add_test (tc_chain, test_expire);
620 tcase_add_test (tc_chain, test_change_func);
621 tcase_add_test (tc_chain, test_reply_expire);
622 tcase_add_test (tc_chain, test_reply_discard);
623 tcase_add_test (tc_chain, test_reply_interrupt);
624 tcase_add_test (tc_chain, test_reply_reply);
625 tcase_add_test (tc_chain, test_interrupt_reply);
626 tcase_add_test (tc_chain, test_interrupt_expire);
627 tcase_add_test (tc_chain, test_interrupt_interrupt);
628 tcase_add_test (tc_chain, test_expire_expire);
629 tcase_add_test (tc_chain, test_expire_interrupt);
630 tcase_add_test (tc_chain, test_expire_reply);
631 tcase_add_test (tc_chain, test_stress);
636 GST_CHECK_MAIN (gst_promise);