tests: Add a testsuite for the new timeline edition API
authorThibault Saunier <thibault.saunier@collabora.com>
Tue, 24 Apr 2012 00:54:15 +0000 (20:54 -0400)
committerThibault Saunier <thibault.saunier@collabora.com>
Tue, 24 Apr 2012 02:03:11 +0000 (22:03 -0400)
tests/check/Makefile.am
tests/check/ges/timelineedition.c [new file with mode: 0644]

index 59105d039bf77d6baab95cfe5105e499b4628867..c458497503f2e1085dd4d69fedfccc294c89f2fe 100644 (file)
@@ -22,6 +22,7 @@ check_PROGRAMS = \
        ges/filesource  \
        ges/simplelayer \
        ges/timelineobject      \
+       ges/timelineedition     \
        ges/titles\
        ges/transition  \
        ges/overlays\
diff --git a/tests/check/ges/timelineedition.c b/tests/check/ges/timelineedition.c
new file mode 100644 (file)
index 0000000..9e1e9c0
--- /dev/null
@@ -0,0 +1,975 @@
+/* GStreamer Editing Services
+ *
+ * Copyright (C) <2012> Thibault Saunier <thibault.saunier@collabora.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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+static gboolean
+my_fill_track_func (GESTimelineObject * object,
+    GESTrackObject * trobject, GstElement * gnlobj, gpointer user_data)
+{
+  GstElement *src;
+
+  GST_DEBUG ("timelineobj:%p, trackobjec:%p, gnlobj:%p",
+      object, trobject, gnlobj);
+
+  /* Let's just put a fakesource in for the time being */
+  src = gst_element_factory_make ("fakesrc", NULL);
+  /* If this fails... that means that there already was something
+   * in it */
+  fail_unless (gst_bin_add (GST_BIN (gnlobj), src));
+
+  return TRUE;
+}
+
+static inline GESTimelineObject *
+create_custom_tlobj (void)
+{
+  return
+      GES_TIMELINE_OBJECT (ges_custom_timeline_source_new (my_fill_track_func,
+          NULL));
+}
+
+#define CHECK_OBJECT_PROPS(obj, start, inpoint, duration) {\
+  assert_equals_uint64 (ges_track_object_get_start (obj), start);\
+  assert_equals_uint64 (ges_track_object_get_inpoint (obj), inpoint);\
+  assert_equals_uint64 (ges_track_object_get_duration (obj), duration);\
+}
+
+GST_START_TEST (test_basic_timeline_edition)
+{
+  GESTrack *track;
+  GESTimeline *timeline;
+  GESTrackObject *tckobj, *tckobj1, *tckobj2;
+  GESTimelineObject *obj, *obj1, *obj2;
+
+  ges_init ();
+
+  track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
+  fail_unless (track != NULL);
+
+  timeline = ges_timeline_new ();
+  fail_unless (timeline != NULL);
+
+  fail_unless (ges_timeline_add_track (timeline, track));
+
+  obj = create_custom_tlobj ();
+  obj1 = create_custom_tlobj ();
+  obj2 = create_custom_tlobj ();
+
+
+  fail_unless (obj && obj1 && obj2);
+
+  /**
+   * Our timeline
+   *
+   * inpoints 0-------   0--------      0-----------
+   *          |  obj  |  |  obj1  |     |     obj2  |
+   * time     0------- 10 --------20    50---------60
+   */
+  g_object_set (obj, "start", (guint64) 0, "duration", (guint64) 10,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj1, "start", (guint64) 10, "duration", (guint64) 10,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj2, "start", (guint64) 50, "duration", (guint64) 60,
+      "in-point", (guint64) 0, NULL);
+
+  tckobj = ges_timeline_object_create_track_object (obj, track);
+  fail_unless (tckobj != NULL);
+  fail_unless (ges_timeline_object_add_track_object (obj, tckobj));
+  fail_unless (ges_track_add_object (track, tckobj));
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj), 10);
+
+  tckobj1 = ges_timeline_object_create_track_object (obj1, track);
+  fail_unless (tckobj1 != NULL);
+  fail_unless (ges_timeline_object_add_track_object (obj1, tckobj1));
+  fail_unless (ges_track_add_object (track, tckobj1));
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 10);
+
+  tckobj2 = ges_timeline_object_create_track_object (obj2, track);
+  fail_unless (ges_timeline_object_add_track_object (obj2, tckobj2));
+  fail_unless (tckobj2 != NULL);
+  fail_unless (ges_track_add_object (track, tckobj2));
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
+
+  /**
+   * Simple rippling obj to: 10
+   *
+   * New timeline:
+   * ------------
+   *
+   * inpoints 0-------   0--------      0-----------
+   *          |  obj  |  |  obj1  |     |   obj2    |
+   * time    10------- 20 --------30    60---------120
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 10) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
+
+
+  /* FIXME find a way to check that we are using the same MovingContext
+   * inside the GESTrack */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 40) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 40, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 80, 0, 60);
+
+  /**
+   * Rippling obj1 back to: 20 (getting to the exact same timeline as before
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 20) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
+
+  /**
+   * Simple move obj to: 27 and obj2 to 35
+   *
+   * New timeline:
+   * ------------
+   *                    0------------
+   * inpoints   0-------|---  obj 0--|----------
+   *            |  obj1 27 -|-----|-37   obj2   |
+   * time      20-----------30   35-------------120
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 27) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 35) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 27, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+
+  /**
+   * Trim start obj to: 32 and obj2 to 35
+   *
+   * New timeline:
+   * ------------
+   *                           5--------
+   * inpoints   0-----------   | obj 0--|----------
+   *            |  obj1     |  32----|-37   obj2   |
+   * time      20-----------30      35-------------120
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_START, 32) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 5);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+
+  /* Ripple end obj to 42
+   * New timeline:
+   * ------------
+   *                           5--------
+   * inpoints   0-----------   | obj 0--|----------
+   *            |  obj1     |  32----|-42   obj2   |
+   * time      20-----------30      35-------------120
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 42) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+
+  /**
+   * New timeline:
+   * ------------
+   * inpoints 0-------     5-------- 0-----------
+   *          |  obj1 |    |  obj1  ||  obj2    |
+   * time    20-------30  32--------52 ---------112
+   */
+  fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 42) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 52) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 20);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 52, 0, 60);
+
+  /**
+   * New timeline:
+   * ------------
+   * inpoints 0-------     5-------- 0------------
+   *          |  obj1 |    |  obj   ||    obj2    |
+   * time    20-------40  42--------62 ---------122
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 40) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 42, 5, 20);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /**
+   * New timeline:
+   * ------------
+   * inpoints 0------- 0-------- 0-----------
+   *          |  obj1 ||   obj  ||  obj2     |
+   * time    20------ 25 ------ 62 ---------122
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_START, 40) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_START, 25) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Make sure that not doing anything when not able to roll */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_START, 65) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 65) == TRUE, 0);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  g_object_unref (timeline);
+  g_object_unref (obj);
+  g_object_unref (obj1);
+  g_object_unref (obj2);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_snapping)
+{
+  GESTrack *track;
+  GESTimeline *timeline;
+  GESTrackObject *tckobj, *tckobj1, *tckobj2;
+  GESTimelineObject *obj, *obj1, *obj2;
+  GESTimelineLayer *layer;
+  GList *tckobjs;
+
+  ges_init ();
+
+  track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
+  fail_unless (track != NULL);
+
+  timeline = ges_timeline_new ();
+  fail_unless (timeline != NULL);
+
+  fail_unless (ges_timeline_add_track (timeline, track));
+
+  obj = create_custom_tlobj ();
+  obj1 = create_custom_tlobj ();
+  obj2 = create_custom_tlobj ();
+
+  fail_unless (obj && obj1 && obj2);
+
+  /**
+   * Our timeline
+   * ------------
+   * inpoints 0------- 0-------- 0-----------
+   *          |  obj1 ||   obj  ||  obj2     |
+   * time    20------ 25 ------ 62 ---------122
+   */
+  g_object_set (obj, "start", (guint64) 25, "duration", (guint64) 37,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj1, "start", (guint64) 20, "duration", (guint64) 15,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj2, "start", (guint64) 62, "duration", (guint64) 60,
+      "in-point", (guint64) 0, NULL);
+
+  fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
+
+
+  fail_unless (ges_timeline_layer_add_object (layer, obj));
+  fail_unless ((tckobjs = ges_timeline_object_get_track_objects (obj)) != NULL);
+  fail_unless ((tckobj = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj), 37);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  /* We have 3 references to tckobj from:
+   *  track + timeline + obj */
+  ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
+  /* We have 1 ref to obj1:
+   * + layer */
+  ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
+
+  fail_unless (ges_timeline_layer_add_object (layer, obj1));
+  fail_unless ((tckobjs =
+          ges_timeline_object_get_track_objects (obj1)) != NULL);
+  fail_unless ((tckobj1 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj1) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 15);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  /* Same ref logic */
+  ASSERT_OBJECT_REFCOUNT (tckobj1, "First tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (obj1, "First tlobj", 1);
+
+  fail_unless (ges_timeline_layer_add_object (layer, obj2));
+  fail_unless ((tckobjs =
+          ges_timeline_object_get_track_objects (obj2)) != NULL);
+  fail_unless ((tckobj2 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj2) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  /* Same ref logic */
+  ASSERT_OBJECT_REFCOUNT (tckobj2, "First tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (obj2, "First tlobj", 1);
+
+  /* Snaping to edge, so no move */
+  g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_END, 27) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Snaping to edge, so no move */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_END, 27) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /**
+   * New timeline:
+   * ------------
+   *                    0----------- 0-------------
+   * inpoints   0-------|--   obj   ||   obj2      |
+   *            |  obj1 25-|------- 62 -----------122
+   * time      20----------30
+   */
+  g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
+  ges_timeline_object_set_duration (obj1, 10);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /**
+   * New timeline(the "layers" are just to help reading diagram, nothing else):
+   * ------------
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints   0----------------------- 10--------
+   *            |       obj1            ||  obj2   |
+   * time      20---------------------- 72 --------122
+   */
+  /* Rolling involves only neighbour that are currently snapping */
+  fail_unless (ges_timeline_object_roll_end (obj1, 62));
+  fail_unless (ges_timeline_object_roll_end (obj1, 72) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 52);
+  CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
+
+  /**
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints           5--------------- 10--------
+   *                    |     obj1      ||  obj2   |
+   * time               25------------- 72 --------122
+   */
+  g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
+  fail_unless (ges_timeline_object_trim_start (obj1, 28) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 25, 5, 47);
+  CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
+
+  /**
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints           5---------- 0---------
+   *                    |  obj1    ||  obj2   |
+   * time               25-------- 62 --------122
+   */
+  fail_unless (ges_timeline_object_roll_start (obj2, 59) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 25, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+   /**
+   * inpoints           0----------5---------- 0----------
+   *                    |   obj    ||  obj1    ||  obj2   |
+   * time               25---------62-------- 99 --------170
+   */
+  fail_unless (ges_timeline_object_ripple (obj1, 58) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 99, 0, 60);
+
+  /**
+   * inpoints     0----------5----------     0----------
+   *              |   obj    ||  obj1    |   |  obj2    |
+   * time         25---------62-------- 99  110--------170
+   */
+  ges_timeline_object_set_start (obj2, 110);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
+
+  /**
+   * inpoints     0----------5    5 --------- 0----------
+   *              |   obj    |    |  obj1    ||  obj2    |
+   * time         25---------62   73---------110--------170
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 72) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 73, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
+
+  /**
+   * inpoints     0----------5----------     0----------
+   *              |   obj    ||  obj1    |   |  obj2    |
+   * time         25---------62-------- 99  110--------170
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 58) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 62, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
+
+
+  /**
+   * inpoints     0----------5---------- 0----------
+   *              |   obj    ||  obj1   ||  obj2    |
+   * time         25---------62--------110--------170
+   */
+  g_object_set (obj1, "duration", 46, NULL);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 62, 5, 48);
+  CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
+
+  /**
+   * inpoints     5----------- 0--------- 0----------
+   *              |   obj1    ||  obj2   ||  obj     |
+   * time         62---------110--------170--------207
+   */
+  g_object_set (obj, "start", 168, NULL);
+  CHECK_OBJECT_PROPS (tckobj, 170, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 62, 5, 48);
+  CHECK_OBJECT_PROPS (tckobj2, 110, 0, 60);
+
+  /* Check we didn't lose/screwed any references */
+  ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (tckobj1, "Second tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (tckobj2, "Third tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
+  ASSERT_OBJECT_REFCOUNT (obj1, "Second tlobj", 1);
+  ASSERT_OBJECT_REFCOUNT (obj2, "Third tlobj", 1);
+
+  g_object_unref (timeline);
+
+  /* Check we destroyed everything */
+  fail_if (G_IS_OBJECT (tckobj));
+  fail_if (G_IS_OBJECT (tckobj1));
+  fail_if (G_IS_OBJECT (tckobj2));
+  fail_if (G_IS_OBJECT (obj));
+  fail_if (G_IS_OBJECT (obj1));
+  fail_if (G_IS_OBJECT (obj2));
+  fail_if (G_IS_OBJECT (layer));
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_timeline_edition_mode)
+{
+  guint i;
+  GESTrack *track;
+  GESTimeline *timeline;
+  GESTrackObject *tckobj, *tckobj1, *tckobj2;
+  GESTimelineObject *obj, *obj1, *obj2;
+  GESTimelineLayer *layer, *layer1, *layer2;
+  GList *tckobjs, *layers, *tmp;
+
+  ges_init ();
+
+  track = ges_track_new (GES_TRACK_TYPE_CUSTOM, GST_CAPS_ANY);
+  fail_unless (track != NULL);
+
+  timeline = ges_timeline_new ();
+  fail_unless (timeline != NULL);
+
+  fail_unless (ges_timeline_add_track (timeline, track));
+
+  obj = create_custom_tlobj ();
+  obj1 = create_custom_tlobj ();
+  obj2 = create_custom_tlobj ();
+
+  fail_unless (obj && obj1 && obj2);
+
+  /**
+   * Our timeline
+   *
+   *          0-------
+   * layer:   |  obj  |
+   *          0-------10
+   *
+   *                   0--------     0-----------
+   * layer1:           |  obj1  |    |     obj2  |
+   *                  10--------20   50---------60
+   */
+  g_object_set (obj, "start", (guint64) 0, "duration", (guint64) 10,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj1, "start", (guint64) 10, "duration", (guint64) 10,
+      "in-point", (guint64) 0, NULL);
+  g_object_set (obj2, "start", (guint64) 50, "duration", (guint64) 60,
+      "in-point", (guint64) 0, NULL);
+
+  fail_unless ((layer = ges_timeline_append_layer (timeline)) != NULL);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
+
+
+  fail_unless (ges_timeline_layer_add_object (layer, obj));
+  fail_unless ((tckobjs = ges_timeline_object_get_track_objects (obj)) != NULL);
+  fail_unless ((tckobj = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj), 10);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  /* Add a new layer and add objects to it */
+  fail_unless ((layer1 = ges_timeline_append_layer (timeline)) != NULL);
+  fail_unless (layer != layer1);
+  assert_equals_int (ges_timeline_layer_get_priority (layer1), 1);
+
+  fail_unless (ges_timeline_layer_add_object (layer1, obj1));
+  fail_unless ((tckobjs =
+          ges_timeline_object_get_track_objects (obj1)) != NULL);
+  fail_unless ((tckobj1 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj1) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj1), 10);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  fail_unless (ges_timeline_layer_add_object (layer1, obj2));
+  fail_unless ((tckobjs =
+          ges_timeline_object_get_track_objects (obj2)) != NULL);
+  fail_unless ((tckobj2 = GES_TRACK_OBJECT (tckobjs->data)) != NULL);
+  fail_unless (ges_track_object_get_track (tckobj2) == track);
+  assert_equals_uint64 (ges_track_object_get_duration (tckobj2), 60);
+  g_list_free_full (tckobjs, g_object_unref);
+
+  /**
+   * Simple rippling obj to: 10
+   *
+   * New timeline:
+   * ------------
+   *
+   * inpoints 0-------
+   *          |  obj  |
+   * time    10-------20
+   *
+   *                   0--------      0-----------
+   *                   |  obj1  |     |   obj2    |
+   *                  20--------30    60--------120
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 10) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
+
+
+  /* FIXME find a way to check that we are using the same MovingContext
+   * inside the GESTimeline */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, 3, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 40) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 40, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 80, 0, 60);
+  layer2 = ges_timeline_object_get_layer (obj1);
+  assert_equals_int (ges_timeline_layer_get_priority (layer2), 3);
+  /* obj2 should have moved layer too */
+  fail_unless (ges_timeline_object_get_layer (obj2) == layer2);
+  /* We got 2 reference to the same object, unref them */
+  g_object_unref (layer2);
+  g_object_unref (layer2);
+
+  /**
+   * Rippling obj1 back to: 20 (getting to the exact same timeline as before
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, 1, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_NONE, 20) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 10, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 60, 0, 60);
+  layer2 = ges_timeline_object_get_layer (obj1);
+  assert_equals_int (ges_timeline_layer_get_priority (layer2), 1);
+  /* obj2 should have moved layer too */
+  fail_unless (ges_timeline_object_get_layer (obj2) == layer2);
+  /* We got 2 reference to the same object, unref them */
+  g_object_unref (layer2);
+  g_object_unref (layer2);
+
+  /**
+   * Simple move obj to 27 and obj2 to 35
+   *
+   * New timeline:
+   * ------------
+   *
+   * inpoints 0-------
+   *          |  obj  |
+   * time    27-------37
+   *
+   *                   0--------   0-----------
+   *                   |  obj1  |  |   obj2    |
+   *                  20--------30 35---------95
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 27) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_NORMAL,
+          GES_EDGE_NONE, 35) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 27, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+
+  /**
+   * Simple trimming start obj to: 32
+   *
+   * New timeline:
+   * ------------
+   *
+   *                      5-------
+   * layer 0:             |  obj  |
+   *                     32-------37
+   *
+   *               0--------      0-----------
+   * layer 1       |  obj1  |     |   obj2    |
+   *              20--------30    35---------95
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_START, 32) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 5);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+
+  /* Ripple end obj to 35 and move to layer 2
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 1:   |  obj1  |         |   obj2    |
+   *            20--------30       35---------95
+   *
+   *                        5------
+   * layer 2:               |  obj |
+   *                       32------35
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 35) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 3);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+  layer = ges_timeline_object_get_layer (obj);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
+  g_object_unref (layer);
+
+  /* Roll end obj to 50
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 1:   |  obj1  |         |   obj2    |
+   *            20--------30       50---------95
+   *
+   *                        5------
+   * layer 2:               |  obj |
+   *                       32------50
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 50) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 18);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 50, 15, 45);
+  layer = ges_timeline_object_get_layer (obj);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
+  g_object_unref (layer);
+
+  /* Some more intensive roll testing */
+  for (i = 0; i < 20; i++) {
+    gint32 random = g_random_int_range (35, 94);
+    guint64 tck3_inpoint = random - 35;
+
+    fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
+            GES_EDGE_END, random) == TRUE);
+    CHECK_OBJECT_PROPS (tckobj, 32, 5, random - 32);
+    CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+    CHECK_OBJECT_PROPS (tckobj2, random, tck3_inpoint, 95 - random);
+  }
+
+  /* Roll end obj back to 35
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 1:   |  obj1  |         |   obj2    |
+   *            20--------30       35---------95
+   *
+   *                        5------
+   * layer 2:               |  obj |
+   *                       32------35
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, 2, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 35) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 3);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 35, 0, 60);
+  layer = ges_timeline_object_get_layer (obj);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
+  g_object_unref (layer);
+
+  /* Ripple obj end to 52
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0----------
+   * layer 1:   |  obj1  |         |   obj2   |
+   *            20-------30       52---------112
+   *
+   *                        5------
+   * layer 2:               |  obj |
+   *                       32------52
+   *
+   */
+  /* Can not move to the first layer as obj2 should move to a layer with priority < 0 */
+  fail_unless (ges_timeline_object_edit (obj, NULL, 0, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 52) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 32, 5, 20);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 52, 0, 60)
+      layer = ges_timeline_object_get_layer (obj);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 2);
+  g_object_unref (layer);
+
+
+  /* Little check that we have 4 layers in the timeline */
+  layers = ges_timeline_get_layers (timeline);
+  assert_equals_int (g_list_length (layers), 4);
+
+  /* Some refcount checkings */
+  /*  We have a reference to each layer in layers */
+  for (tmp = layers; tmp; tmp = tmp->next)
+    ASSERT_OBJECT_REFCOUNT (layer, "Layer", 2);
+  g_list_free_full (layers, g_object_unref);
+
+  /* We have 3 references:
+   *  track  + timeline  + obj
+   */
+  ASSERT_OBJECT_REFCOUNT (tckobj, "First tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (tckobj1, "Second tckobj", 3);
+  ASSERT_OBJECT_REFCOUNT (tckobj2, "Third tckobj", 3);
+  /* We have 1 ref:
+   * + layer */
+  ASSERT_OBJECT_REFCOUNT (obj, "First tlobj", 1);
+  ASSERT_OBJECT_REFCOUNT (obj1, "Second tlobj", 1);
+  ASSERT_OBJECT_REFCOUNT (obj2, "Third tlobj", 1);
+
+  /* Ripple obj end to 52
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 0:   |  obj1  |         |   obj2    |
+   *            20-------40       62----------112
+   *
+   *                        5------
+   * layer 1:               |  obj |
+   *                       42------60
+   *
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, 0, GES_EDIT_MODE_RIPPLE,
+          GES_EDGE_END, 40) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 42, 5, 20);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Check that movement between layer has been done properly */
+  layer1 = ges_timeline_object_get_layer (obj);
+  layer = ges_timeline_object_get_layer (obj1);
+  assert_equals_int (ges_timeline_layer_get_priority (layer1), 1);
+  assert_equals_int (ges_timeline_layer_get_priority (layer), 0);
+  fail_unless (ges_timeline_object_get_layer (obj2) == layer);
+  g_object_unref (layer1);
+  /* We have 2 references to @layer that we do not need anymore */ ;
+  g_object_unref (layer);
+  g_object_unref (layer);
+
+  /* Trim obj start to 40
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 0:   |  obj1  |         |   obj2    |
+   *            20-------40       62---------112
+   *
+   *                      0------
+   * layer 1:             |  obj |
+   *                     40------62
+   *
+   */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_START, 40) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 40, 3, 22);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 20);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Roll obj end to 25
+   * New timeline:
+   * ------------
+   *
+   *            0--------          0-----------
+   * layer 0:   |  obj1  |         |   obj2    |
+   *            20-------25       62---------112
+   *
+   *                      0------
+   * layer 1:             |  obj |
+   *                     25------62
+   *
+   */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 25) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Make sure that not doing anything when not able to roll */
+  fail_unless (ges_timeline_object_edit (obj, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_START, 65) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 65) == TRUE, 0);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Snaping to edge, so no move */
+  g_object_set (timeline, "snapping-distance", (guint64) 3, NULL);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_END, 27) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /* Snaping to edge, so no move */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_END, 27) == TRUE);
+
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 5);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /**
+   * New timeline:
+   * ------------
+   *                    0----------- 0-------------
+   * inpoints   0-------|--   obj   ||   obj2      |
+   *            |  obj1 25-|------- 62 -----------122
+   * time      20----------30
+   */
+  g_object_set (timeline, "snapping-distance", (guint64) 0, NULL);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_END, 30) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 10);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+  /**
+   * New timeline
+   * ------------
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints   0----------------------- 10--------
+   *            |       obj1            ||  obj2   |
+   * time      20---------------------- 72 --------122
+   */
+  /* Rolling involves only neighbours that are currently snapping */
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 62) == TRUE);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_END, 72) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 20, 0, 52);
+  CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
+
+  /* Test Snapping */
+  /**
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints           5--------------- 10--------
+   *                    |     obj1      ||  obj2   |
+   * time               25------------- 72 --------122
+   */
+  g_object_set (timeline, "snapping-distance", (guint64) 4, NULL);
+  fail_unless (ges_timeline_object_edit (obj1, NULL, -1, GES_EDIT_MODE_TRIM,
+          GES_EDGE_START, 28) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 25, 5, 47);
+  CHECK_OBJECT_PROPS (tckobj2, 72, 10, 50);
+
+  /**
+   *                    0----------
+   *                    |   obj    |
+   *                    25---------62
+   * inpoints           5---------- 0---------
+   *                    |  obj1    ||  obj2   |
+   * time               25-------- 62 --------122
+   */
+  fail_unless (ges_timeline_object_edit (obj2, NULL, -1, GES_EDIT_MODE_ROLL,
+          GES_EDGE_START, 59) == TRUE);
+  CHECK_OBJECT_PROPS (tckobj, 25, 0, 37);
+  CHECK_OBJECT_PROPS (tckobj1, 25, 5, 37);
+  CHECK_OBJECT_PROPS (tckobj2, 62, 0, 60);
+
+}
+
+GST_END_TEST;
+
+static Suite *
+ges_suite (void)
+{
+  Suite *s = suite_create ("ges-timeline-edition");
+  TCase *tc_chain = tcase_create ("timeline-edition");
+
+  suite_add_tcase (s, tc_chain);
+
+  tcase_add_test (tc_chain, test_basic_timeline_edition);
+  tcase_add_test (tc_chain, test_snapping);
+  tcase_add_test (tc_chain, test_timeline_edition_mode);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = ges_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}