check/gst/gstbin.c: Added test to check state change order in bins (can still be...
authorTim-Philipp Müller <tim@centricular.net>
Fri, 23 Sep 2005 17:17:42 +0000 (17:17 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Fri, 23 Sep 2005 17:17:42 +0000 (17:17 +0000)
Original commit message from CVS:
* check/gst/gstbin.c: (test_children_state_change_order_flagged_sink),
(test_children_state_change_order_semi_sink), (gst_bin_suite):
Added test to check state change order in bins (can still be made
to fail here under heavy disk load; bails out with 'Push on pad
fakesink:sink0, but it was not activated in push mode').
* gst/gstbin.c: (gst_bin_class_init), (gst_bin_change_state):
Fix state change order when there is only a semi sink (#316856)
* gst/gstbus.c: (gst_bus_class_init):
Use _class_peek_parent(), not _class_ref(); fix docs to say
'default main context' instead of 'mainloop' where that is
what's meant.
* gst/gstelement.c: (gst_element_commit_state),
(gst_element_set_state):
Fix typos in debug messages

ChangeLog
check/gst/gstbin.c
gst/gstbin.c
gst/gstbus.c
gst/gstelement.c
tests/check/gst/gstbin.c

index 537cf3f..06834da 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2005-09-23  Tim-Philipp Müller  <tim at centricular dot net>
+
+       * check/gst/gstbin.c: (test_children_state_change_order_flagged_sink),
+       (test_children_state_change_order_semi_sink), (gst_bin_suite):
+         Added test to check state change order in bins (can still be made
+         to fail here under heavy disk load; bails out with 'Push on pad
+         fakesink:sink0, but it was not activated in push mode').
+
+       * gst/gstbin.c: (gst_bin_class_init), (gst_bin_change_state):
+         Fix state change order when there is only a semi sink (#316856)
+
+       * gst/gstbus.c: (gst_bus_class_init):
+         Use _class_peek_parent(), not _class_ref(); fix docs to say
+         'default main context' instead of 'mainloop' where that is
+         what's meant.
+
+       * gst/gstelement.c: (gst_element_commit_state),
+       (gst_element_set_state):
+         Fix typos in debug messages
+
 2005-09-23  Thomas Vander Stichele  <thomas at apestaart dot org>
 
        * docs/README:
index 16c6f62..f205bb0 100644 (file)
@@ -392,7 +392,7 @@ GST_START_TEST (test_add_linked)
   src = gst_element_factory_make ("fakesrc", NULL);
   fail_if (src == NULL, "Could not create fakesrc");
   sink = gst_element_factory_make ("fakesink", NULL);
-  fail_if (src == NULL, "Could not create fakesink");
+  fail_if (sink == NULL, "Could not create fakesink");
 
   srcpad = gst_element_get_pad (src, "src");
   fail_unless (srcpad != NULL);
@@ -432,6 +432,183 @@ GST_START_TEST (test_add_linked)
 
 GST_END_TEST;
 
+/* g_print ("%10s: %4d => %4d\n", GST_OBJECT_NAME (msg->src), old, new); */
+
+#define ASSERT_STATE_CHANGE_MSG(bus,element,old_state,new_state,num)          \
+  {                                                                           \
+    GstMessage *msg;                                                          \
+    GstState old = 0, new = 0;                                                \
+    msg = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, GST_SECOND);          \
+    fail_if (msg == NULL, "No state change message within 1 second (#"        \
+        G_STRINGIFY (num) ")");                                               \
+    gst_message_parse_state_changed (msg, &old, &new);                        \
+    fail_if (msg->src != GST_OBJECT (element), G_STRINGIFY(element)           \
+        " should have changed state next (#" G_STRINGIFY (num) ")");          \
+    fail_if (old != old_state || new != new_state, "state change is not "     \
+        G_STRINGIFY (old_state) " => " G_STRINGIFY (new_state));              \
+    gst_message_unref (msg);                                                  \
+  }
+
+GST_START_TEST (test_children_state_change_order_flagged_sink)
+{
+  GstElement *src, *identity, *sink, *pipeline;
+  GstStateChangeReturn ret;
+  GstBus *bus;
+
+  pipeline = gst_pipeline_new (NULL);
+  fail_unless (pipeline != NULL, "Could not create pipeline");
+
+  bus = GST_ELEMENT_BUS (pipeline);
+  fail_unless (bus != NULL, "Pipeline has no bus?!");
+
+  src = gst_element_factory_make ("fakesrc", NULL);
+  fail_if (src == NULL, "Could not create fakesrc");
+
+  identity = gst_element_factory_make ("identity", NULL);
+  fail_if (identity == NULL, "Could not create identity");
+
+  sink = gst_element_factory_make ("fakesink", NULL);
+  fail_if (sink == NULL, "Could not create fakesink");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, identity, sink, NULL);
+
+  fail_unless (gst_element_link (src, identity) == TRUE);
+  fail_unless (gst_element_link (identity, sink) == TRUE);
+
+  /* (1) Test state change with fakesink being a regular sink */
+  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
+
+  /* NULL => READY */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_NULL, GST_STATE_READY, 102);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_NULL, GST_STATE_READY, 103);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_NULL, GST_STATE_READY, 104);
+
+  /* READY => PAUSED */
+  /* because of pre-rolling, sink will return ASYNC on state 
+   * change and change state later when it has a buffer */
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_READY, GST_STATE_PAUSED,
+      105);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_READY, GST_STATE_PAUSED, 106);
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_READY, GST_STATE_PAUSED, 107);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_READY, GST_STATE_PAUSED,
+      108);
+
+  /* PAUSED => PLAYING */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_PAUSED, GST_STATE_PLAYING, 109);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      110);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_PAUSED, GST_STATE_PLAYING, 111);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      112);
+
+  /* don't set to NULL that will set the bus flushing and kill our messages */
+  ret = gst_element_set_state (pipeline, GST_STATE_READY);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
+
+  /* TODO: do we need to check downwards state change order as well? */
+  pop_messages (bus, 4);        /* pop playing => paused messages off the bus */
+  pop_messages (bus, 4);        /* pop paused => ready messages off the bus */
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to NULL failed");
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_children_state_change_order_semi_sink)
+{
+  GstElement *src, *identity, *sink, *pipeline;
+  GstStateChangeReturn ret;
+  GstBus *bus;
+
+  /* (2) Now again, but check other code path where we don't have
+   *     a proper sink correctly flagged as such, but a 'semi-sink' */
+  pipeline = gst_pipeline_new (NULL);
+  fail_unless (pipeline != NULL, "Could not create pipeline");
+
+  bus = GST_ELEMENT_BUS (pipeline);
+  fail_unless (bus != NULL, "Pipeline has no bus?!");
+
+  src = gst_element_factory_make ("fakesrc", NULL);
+  fail_if (src == NULL, "Could not create fakesrc");
+
+  identity = gst_element_factory_make ("identity", NULL);
+  fail_if (identity == NULL, "Could not create identity");
+
+  sink = gst_element_factory_make ("fakesink", NULL);
+  fail_if (sink == NULL, "Could not create fakesink");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, identity, sink, NULL);
+
+  fail_unless (gst_element_link (src, identity) == TRUE);
+  fail_unless (gst_element_link (identity, sink) == TRUE);
+
+  GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK);   /* <======== */
+
+  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
+
+  /* NULL => READY */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 201);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_NULL, GST_STATE_READY, 202);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_NULL, GST_STATE_READY, 203);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_NULL, GST_STATE_READY, 204);
+
+  /* READY => PAUSED */
+  /* because of pre-rolling, sink will return ASYNC on state 
+   * change and change state later when it has a buffer */
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_READY, GST_STATE_PAUSED,
+      205);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_READY, GST_STATE_PAUSED, 206);
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_READY, GST_STATE_PAUSED, 207);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_READY, GST_STATE_PAUSED,
+      208);
+
+  /* PAUSED => PLAYING */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_PAUSED, GST_STATE_PLAYING, 209);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      210);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_PAUSED, GST_STATE_PLAYING, 211);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      212);
+
+  /* don't set to NULL that will set the bus flushing and kill our messages */
+  ret = gst_element_set_state (pipeline, GST_STATE_READY);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
+
+  /* TODO: do we need to check downwards state change order as well? */
+  pop_messages (bus, 4);        /* pop playing => paused messages off the bus */
+  pop_messages (bus, 4);        /* pop paused => ready messages off the bus */
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to NULL failed");
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
 Suite *
 gst_bin_suite (void)
 {
@@ -440,6 +617,8 @@ gst_bin_suite (void)
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_interface);
+  tcase_add_test (tc_chain, test_children_state_change_order_flagged_sink);
+  tcase_add_test (tc_chain, test_children_state_change_order_semi_sink);
   tcase_add_test (tc_chain, test_message_state_changed);
   tcase_add_test (tc_chain, test_message_state_changed_child);
   tcase_add_test (tc_chain, test_message_state_changed_children);
index 74b34a0..9818104 100644 (file)
@@ -223,7 +223,7 @@ gst_bin_class_init (GstBinClass * klass)
   gstobject_class = (GstObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  parent_class = g_type_class_peek_parent (klass);
 
   /**
    * GstBin::element-added:
@@ -1359,7 +1359,7 @@ restart:
 
               /* was reffed before pushing on the queue by the
                * gst_object_get_parent() call we used to get the element. */
-              g_queue_push_tail (elem_queue, peer_parent);
+              g_queue_push_head (elem_queue, peer_parent);
               /* so that we don't unref it */
               peer_parent = NULL;
             } else {
index cb4c59f..da464a6 100644 (file)
@@ -43,7 +43,7 @@
  * is posted on the bus. The application can then _pop() the messages from the
  * bus to handle them.
  * Alternatively the application can register an asynchronous bus function using
- * gst_bus_add_watch_full() orgst_bus_add_watch(). This function will receive
+ * gst_bus_add_watch_full() or gst_bus_add_watch(). This function will receive
  * messages a short while after they have been posted.
  * 
  * It is also possible to get messages from the bus without any thread 
 
 #include "gstbus.h"
 
-enum
-{
-  ARG_0,
-};
 
 static void gst_bus_class_init (GstBusClass * klass);
 static void gst_bus_init (GstBus * bus);
@@ -116,7 +112,7 @@ gst_bus_class_init (GstBusClass * klass)
   gobject_class = (GObjectClass *) klass;
   gstobject_class = (GstObjectClass *) klass;
 
-  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
+  parent_class = g_type_class_peek_parent (klass);
 
   if (!g_thread_supported ())
     g_thread_init (NULL);
@@ -590,11 +586,11 @@ gst_bus_create_watch (GstBus * bus, GstMessageType events)
  * @user_data: user data passed to @func.
  * @notify: the function to call when the source is removed.
  *
- * Adds the bus to the mainloop with the given priority. If the func returns
- * FALSE, the func will be removed. 
+ * Adds a bus watch to the default main context with the given priority. 
+ * If the func returns FALSE, the source will be removed. 
  *
  * When the func is called, the message belongs to the caller; if you want to 
- * keep a copy of it, call gst_message_ref before leaving the func.
+ * keep a copy of it, call gst_message_ref() before leaving the func.
  *
  * Returns: The event source id.
  *
@@ -631,7 +627,7 @@ gst_bus_add_watch_full (GstBus * bus, gint priority, GstMessageType events,
  * @func: A function to call when a message is received.
  * @user_data: user data passed to @func.
  *
- * Adds the bus to the mainloop with the default priority.
+ * Adds a bus watch to the default main context with the default priority.
  *
  * Returns: The event source id.
  *
index 0f64186..a478a7d 100644 (file)
@@ -1737,7 +1737,8 @@ gst_element_commit_state (GstElement * element)
     GstState old_state = GST_STATE (element);
 
     GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
-        "commiting state from %s to %s", gst_element_state_get_name (old_state),
+        "committing state from %s to %s",
+        gst_element_state_get_name (old_state),
         gst_element_state_get_name (pending));
 
     GST_STATE (element) = pending;
@@ -1892,7 +1893,7 @@ gst_element_set_state (GstElement * element, GstState state)
             "element changed state successfully");
         /* we can commit the state now and proceed to the next state */
         gst_element_commit_state (element);
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "commited state");
+        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state");
         break;
       case GST_STATE_CHANGE_NO_PREROLL:
         GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
@@ -1900,7 +1901,7 @@ gst_element_set_state (GstElement * element, GstState state)
         /* we can commit the state now and proceed to the next state */
         gst_element_commit_state (element);
         GST_STATE_NO_PREROLL (element) = TRUE;
-        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "commited state");
+        GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state");
         break;
       default:
         goto invalid_return;
index 16c6f62..f205bb0 100644 (file)
@@ -392,7 +392,7 @@ GST_START_TEST (test_add_linked)
   src = gst_element_factory_make ("fakesrc", NULL);
   fail_if (src == NULL, "Could not create fakesrc");
   sink = gst_element_factory_make ("fakesink", NULL);
-  fail_if (src == NULL, "Could not create fakesink");
+  fail_if (sink == NULL, "Could not create fakesink");
 
   srcpad = gst_element_get_pad (src, "src");
   fail_unless (srcpad != NULL);
@@ -432,6 +432,183 @@ GST_START_TEST (test_add_linked)
 
 GST_END_TEST;
 
+/* g_print ("%10s: %4d => %4d\n", GST_OBJECT_NAME (msg->src), old, new); */
+
+#define ASSERT_STATE_CHANGE_MSG(bus,element,old_state,new_state,num)          \
+  {                                                                           \
+    GstMessage *msg;                                                          \
+    GstState old = 0, new = 0;                                                \
+    msg = gst_bus_poll (bus, GST_MESSAGE_STATE_CHANGED, GST_SECOND);          \
+    fail_if (msg == NULL, "No state change message within 1 second (#"        \
+        G_STRINGIFY (num) ")");                                               \
+    gst_message_parse_state_changed (msg, &old, &new);                        \
+    fail_if (msg->src != GST_OBJECT (element), G_STRINGIFY(element)           \
+        " should have changed state next (#" G_STRINGIFY (num) ")");          \
+    fail_if (old != old_state || new != new_state, "state change is not "     \
+        G_STRINGIFY (old_state) " => " G_STRINGIFY (new_state));              \
+    gst_message_unref (msg);                                                  \
+  }
+
+GST_START_TEST (test_children_state_change_order_flagged_sink)
+{
+  GstElement *src, *identity, *sink, *pipeline;
+  GstStateChangeReturn ret;
+  GstBus *bus;
+
+  pipeline = gst_pipeline_new (NULL);
+  fail_unless (pipeline != NULL, "Could not create pipeline");
+
+  bus = GST_ELEMENT_BUS (pipeline);
+  fail_unless (bus != NULL, "Pipeline has no bus?!");
+
+  src = gst_element_factory_make ("fakesrc", NULL);
+  fail_if (src == NULL, "Could not create fakesrc");
+
+  identity = gst_element_factory_make ("identity", NULL);
+  fail_if (identity == NULL, "Could not create identity");
+
+  sink = gst_element_factory_make ("fakesink", NULL);
+  fail_if (sink == NULL, "Could not create fakesink");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, identity, sink, NULL);
+
+  fail_unless (gst_element_link (src, identity) == TRUE);
+  fail_unless (gst_element_link (identity, sink) == TRUE);
+
+  /* (1) Test state change with fakesink being a regular sink */
+  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
+
+  /* NULL => READY */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 101);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_NULL, GST_STATE_READY, 102);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_NULL, GST_STATE_READY, 103);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_NULL, GST_STATE_READY, 104);
+
+  /* READY => PAUSED */
+  /* because of pre-rolling, sink will return ASYNC on state 
+   * change and change state later when it has a buffer */
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_READY, GST_STATE_PAUSED,
+      105);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_READY, GST_STATE_PAUSED, 106);
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_READY, GST_STATE_PAUSED, 107);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_READY, GST_STATE_PAUSED,
+      108);
+
+  /* PAUSED => PLAYING */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_PAUSED, GST_STATE_PLAYING, 109);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      110);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_PAUSED, GST_STATE_PLAYING, 111);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      112);
+
+  /* don't set to NULL that will set the bus flushing and kill our messages */
+  ret = gst_element_set_state (pipeline, GST_STATE_READY);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
+
+  /* TODO: do we need to check downwards state change order as well? */
+  pop_messages (bus, 4);        /* pop playing => paused messages off the bus */
+  pop_messages (bus, 4);        /* pop paused => ready messages off the bus */
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to NULL failed");
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_children_state_change_order_semi_sink)
+{
+  GstElement *src, *identity, *sink, *pipeline;
+  GstStateChangeReturn ret;
+  GstBus *bus;
+
+  /* (2) Now again, but check other code path where we don't have
+   *     a proper sink correctly flagged as such, but a 'semi-sink' */
+  pipeline = gst_pipeline_new (NULL);
+  fail_unless (pipeline != NULL, "Could not create pipeline");
+
+  bus = GST_ELEMENT_BUS (pipeline);
+  fail_unless (bus != NULL, "Pipeline has no bus?!");
+
+  src = gst_element_factory_make ("fakesrc", NULL);
+  fail_if (src == NULL, "Could not create fakesrc");
+
+  identity = gst_element_factory_make ("identity", NULL);
+  fail_if (identity == NULL, "Could not create identity");
+
+  sink = gst_element_factory_make ("fakesink", NULL);
+  fail_if (sink == NULL, "Could not create fakesink");
+
+  gst_bin_add_many (GST_BIN (pipeline), src, identity, sink, NULL);
+
+  fail_unless (gst_element_link (src, identity) == TRUE);
+  fail_unless (gst_element_link (identity, sink) == TRUE);
+
+  GST_FLAG_UNSET (sink, GST_ELEMENT_IS_SINK);   /* <======== */
+
+  ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to PLAYING failed");
+
+  /* NULL => READY */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_NULL, GST_STATE_READY, 201);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_NULL, GST_STATE_READY, 202);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_NULL, GST_STATE_READY, 203);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_NULL, GST_STATE_READY, 204);
+
+  /* READY => PAUSED */
+  /* because of pre-rolling, sink will return ASYNC on state 
+   * change and change state later when it has a buffer */
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_READY, GST_STATE_PAUSED,
+      205);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_READY, GST_STATE_PAUSED, 206);
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_READY, GST_STATE_PAUSED, 207);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_READY, GST_STATE_PAUSED,
+      208);
+
+  /* PAUSED => PLAYING */
+  ASSERT_STATE_CHANGE_MSG (bus, sink, GST_STATE_PAUSED, GST_STATE_PLAYING, 209);
+  ASSERT_STATE_CHANGE_MSG (bus, identity, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      210);
+  ASSERT_STATE_CHANGE_MSG (bus, src, GST_STATE_PAUSED, GST_STATE_PLAYING, 211);
+  ASSERT_STATE_CHANGE_MSG (bus, pipeline, GST_STATE_PAUSED, GST_STATE_PLAYING,
+      212);
+
+  /* don't set to NULL that will set the bus flushing and kill our messages */
+  ret = gst_element_set_state (pipeline, GST_STATE_READY);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to READY failed");
+
+  /* TODO: do we need to check downwards state change order as well? */
+  pop_messages (bus, 4);        /* pop playing => paused messages off the bus */
+  pop_messages (bus, 4);        /* pop paused => ready messages off the bus */
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+  fail_if (ret != GST_STATE_CHANGE_SUCCESS, "State change to NULL failed");
+
+  ASSERT_OBJECT_REFCOUNT (src, "src", 1);
+  ASSERT_OBJECT_REFCOUNT (sink, "sink", 1);
+  ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
 Suite *
 gst_bin_suite (void)
 {
@@ -440,6 +617,8 @@ gst_bin_suite (void)
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_interface);
+  tcase_add_test (tc_chain, test_children_state_change_order_flagged_sink);
+  tcase_add_test (tc_chain, test_children_state_change_order_semi_sink);
   tcase_add_test (tc_chain, test_message_state_changed);
   tcase_add_test (tc_chain, test_message_state_changed_child);
   tcase_add_test (tc_chain, test_message_state_changed_children);