Reimplement the timeline editing API
[platform/upstream/gst-editing-services.git] / tests / check / python / test_group.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (c) 2015, Thibault Saunier
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program; if not, write to the
17 # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 # Boston, MA 02110-1301, USA.
19
20 from . import overrides_hack
21
22 import gi
23
24 gi.require_version("Gst", "1.0")
25 gi.require_version("GES", "1.0")
26
27 from gi.repository import Gst  # noqa
28 from gi.repository import GES  # noqa
29
30 from . import common  # noqa
31
32 import unittest  # noqa
33 from unittest import mock
34
35 Gst.init(None)
36 GES.init()
37
38
39 class TestGroup(common.GESSimpleTimelineTest):
40
41     def testCopyGroup(self):
42         clip1 = GES.TestClip.new()
43         clip1.props.duration = 10
44
45         self.layer.add_clip(clip1)
46
47         self.assertEqual(len(clip1.get_children(False)), 2)
48
49         group = GES.Group.new()
50         self.assertTrue(group.add(clip1))
51
52         self.assertEqual(len(group.get_children(False)), 1)
53
54         group_copy = group.copy(True)
55         self.assertEqual(len(group_copy.get_children(False)), 0)
56
57         self.assertTrue(group_copy.paste(10))
58         clips = self.layer.get_clips()
59         self.assertEqual(len(clips), 2)
60         self.assertEqual(clips[1].props.start, 10)
61
62         clips[1].edit([], 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10)
63         clips = self.layer.get_clips()
64         self.assertEqual(len(clips), 1)
65
66     def testPasteChangedGroup(self):
67         clip1 = GES.TestClip.new()
68         clip1.props.duration = 10
69
70         clip2 = GES.TestClip.new()
71         clip2.props.start = 20
72         clip2.props.duration = 10
73
74         self.layer.add_clip(clip1)
75         self.layer.add_clip(clip2)
76
77         self.assertEqual(len(clip1.get_children(False)), 2)
78
79         group = GES.Group.new()
80         self.assertTrue(group.add(clip1))
81
82         self.assertEqual(len(group.get_children(False)), 1)
83
84         group_copy = group.copy(True)
85         self.assertEqual(len(group_copy.get_children(False)), 0)
86
87         self.assertTrue(group.add(clip2))
88         self.assertEqual(len(group.get_children(False)), 2)
89         self.assertEqual(len(group_copy.get_children(False)), 0)
90
91         self.assertTrue(group_copy.paste(10))
92         clips = self.layer.get_clips()
93         self.assertEqual(len(clips), 3)
94         self.assertEqual(clips[1].props.start, 10)
95
96     def testPasteChangedGroup(self):
97         clip1 = GES.TestClip.new()
98         clip1.props.duration = 10
99
100         clip2 = GES.TestClip.new()
101         clip2.props.start = 20
102         clip2.props.duration = 10
103
104         self.layer.add_clip(clip1)
105         self.layer.add_clip(clip2)
106
107         self.assertEqual(len(clip1.get_children(False)), 2)
108
109         group = GES.Group.new()
110         self.assertTrue(group.add(clip1))
111
112         self.assertEqual(len(group.get_children(False)), 1)
113
114         group_copy = group.copy(True)
115         self.assertEqual(len(group_copy.get_children(False)), 0)
116
117         self.assertTrue(group.add(clip2))
118         self.assertEqual(len(group.get_children(False)), 2)
119         self.assertEqual(len(group_copy.get_children(False)), 0)
120
121         self.assertTrue(group_copy.paste(10))
122         clips = self.layer.get_clips()
123         self.assertEqual(len(clips), 3)
124         self.assertEqual(clips[1].props.start, 10)
125
126     def test_move_clips_between_layers_with_auto_transition(self):
127         self.timeline.props.auto_transition = True
128         layer2 = self.timeline.append_layer()
129         clip1 = GES.TestClip.new()
130         clip1.props.start = 0
131         clip1.props.duration = 30
132
133         clip2 = GES.TestClip.new()
134         clip2.props.start = 20
135         clip2.props.duration = 20
136
137         self.layer.add_clip(clip1)
138         self.layer.add_clip(clip2)
139
140         clips = self.layer.get_clips()
141         self.assertEqual(len(clips), 4)
142         self.assertEqual(layer2.get_clips(), [])
143
144         group = GES.Container.group(clips)
145         self.assertIsNotNone(group)
146
147         self.assertTrue(clip1.edit(
148             self.timeline.get_layers(), 1, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 0))
149         self.assertEqual(self.layer.get_clips(), [])
150
151         clips = layer2.get_clips()
152         self.assertEqual(len(clips), 4)
153
154     def test_remove_emits_signal(self):
155         clip1 = GES.TestClip.new()
156         self.layer.add_clip(clip1)
157
158         group = GES.Group.new()
159         child_removed_cb = mock.Mock()
160         group.connect("child-removed", child_removed_cb)
161
162         group.add(clip1)
163         group.remove(clip1)
164         child_removed_cb.assert_called_once_with(group, clip1)
165
166         group.add(clip1)
167         child_removed_cb.reset_mock()
168         group.ungroup(recursive=False)
169         child_removed_cb.assert_called_once_with(group, clip1)
170
171     def test_loaded_project_has_groups(self):
172         mainloop = common.create_main_loop()
173         timeline = common.create_project(with_group=True, saved=True)
174         layer, = timeline.get_layers()
175         group, = timeline.get_groups()
176         self.assertEqual(len(layer.get_clips()), 2)
177         for clip in layer.get_clips():
178             self.assertEqual(clip.get_parent(), group)
179
180         # Reload the project, check the group.
181         project = GES.Project.new(uri=timeline.get_asset().props.uri)
182
183         loaded_called = False
184         def loaded(unused_project, unused_timeline):
185             nonlocal loaded_called
186             loaded_called = True
187             mainloop.quit()
188         project.connect("loaded", loaded)
189
190         timeline = project.extract()
191
192         mainloop.run()
193         self.assertTrue(loaded_called)
194
195         layer, = timeline.get_layers()
196         group, = timeline.get_groups()
197         self.assertEqual(len(layer.get_clips()), 2)
198         for clip in layer.get_clips():
199             self.assertEqual(clip.get_parent(), group)
200
201     def test_moving_group_with_transition(self):
202         self.timeline.props.auto_transition = True
203         clip1 = GES.TestClip.new()
204         clip1.props.start = 0
205         clip1.props.duration = 30
206
207         clip2 = GES.TestClip.new()
208         clip2.props.start = 20
209         clip2.props.duration = 20
210
211         self.layer.add_clip(clip1)
212         self.layer.add_clip(clip2)
213
214         clips = self.layer.get_clips()
215         self.assertEqual(len(clips), 4)
216
217         video_transition = None
218         audio_transition = None
219         for clip in clips:
220             if isinstance(clip, GES.TransitionClip):
221                 if isinstance(clip.get_children(False)[0], GES.VideoTransition):
222                     video_transition = clip
223                 else:
224                     audio_transition = clip
225         self.assertIsNotNone(audio_transition)
226         self.assertIsNotNone(video_transition)
227
228         self.assertEqual(video_transition.props.start, 20)
229         self.assertEqual(video_transition.props.duration, 10)
230         self.assertEqual(audio_transition.props.start, 20)
231         self.assertEqual(audio_transition.props.duration, 10)
232
233         group = GES.Container.group(clips)
234         self.assertIsNotNone(group)
235
236         self.assertTrue(clip2.edit(
237             self.timeline.get_layers(), 0,
238             GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 25))
239         clip2.props.start = 25
240
241         clips = self.layer.get_clips()
242         self.assertEqual(len(clips), 4)
243         self.assertEqual(clip1.props.start, 5)
244         self.assertEqual(clip1.props.duration, 30)
245         self.assertEqual(clip2.props.start, 25)
246         self.assertEqual(clip2.props.duration, 20)
247
248         self.assertEqual(video_transition.props.start, 25)
249         self.assertEqual(video_transition.props.duration, 10)
250         self.assertEqual(audio_transition.props.start, 25)
251         self.assertEqual(audio_transition.props.duration, 10)
252
253     def test_moving_group_snapping_from_the_middle(self):
254         self.track_types = [GES.TrackType.AUDIO]
255         super().setUp()
256         snapped_positions = []
257         def snapping_started_cb(timeline, first_element, second_element,
258                                 position, snapped_positions):
259             snapped_positions.append(position)
260
261         self.timeline.props.snapping_distance = 5
262         self.timeline.connect("snapping-started", snapping_started_cb,
263                               snapped_positions)
264
265         for start in range(0, 20, 5):
266             clip = GES.TestClip.new()
267             clip.props.start = start
268             clip.props.duration = 5
269             self.layer.add_clip(clip)
270
271         clips = self.layer.get_clips()
272         self.assertEqual(len(clips), 4)
273
274         group = GES.Container.group(clips[1:3])
275         self.assertIsNotNone(group)
276
277         self.assertTimelineTopology([
278             [
279                 (GES.TestClip, 0, 5),
280                 (GES.TestClip, 5, 5),
281                 (GES.TestClip, 10, 5),
282                 (GES.TestClip, 15, 5),
283             ],
284         ], groups=[clips[1:3]])
285
286         self.assertEqual(clips[1].props.start, 5)
287         self.assertEqual(clips[2].props.start, 10)
288         clips[2].edit([], 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 11)
289
290         self.assertEqual(snapped_positions[0], 5)
291         self.assertTimelineTopology([
292             [
293                 (GES.TestClip, 0, 5),
294                 (GES.TestClip, 5, 5),
295                 (GES.TestClip, 10, 5),
296                 (GES.TestClip, 15, 5),
297             ],
298         ], groups=[clips[1:3]])
299
300     def test_rippling_with_group(self):
301         self.track_types = [GES.TrackType.AUDIO]
302         super().setUp()
303         for _ in range(4):
304             self.append_clip()
305
306         snapped_positions = []
307         def snapping_started_cb(timeline, first_element, second_element,
308                                 position, snapped_positions):
309             snapped_positions.append(position)
310
311         self.timeline.props.snapping_distance = 5
312         self.timeline.connect("snapping-started", snapping_started_cb,
313                               snapped_positions)
314
315         clips = self.layer.get_clips()
316         self.assertEqual(len(clips), 4)
317
318         group_clips = clips[1:3]
319         GES.Container.group(group_clips)
320         self.assertTimelineTopology([
321             [
322                 (GES.TestClip, 0, 10),
323                 (GES.TestClip, 10, 10),
324                 (GES.TestClip, 20, 10),
325                 (GES.TestClip, 30, 10),
326             ],
327         ], groups=[group_clips])
328
329         self.assertFalse(clips[2].edit([], 0, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 5))
330
331         self.assertTimelineTopology([
332             [
333                 (GES.TestClip, 0, 10),
334                 (GES.TestClip, 10, 10),
335                 (GES.TestClip, 20, 10),
336                 (GES.TestClip, 30, 10),
337             ],
338         ], groups=[group_clips])
339
340         # Negative start...
341         self.assertFalse(clips[2].edit([], 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 1))
342         self.assertTimelineTopology([
343             [
344                 (GES.TestClip, 0, 10),
345                 (GES.TestClip, 10, 10),
346                 (GES.TestClip, 20, 10),
347                 (GES.TestClip, 30, 10),
348             ],
349         ], groups=[group_clips])
350
351         self.assertTrue(clips[2].edit([], 1, GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 20))
352         self.assertTimelineTopology([
353             [
354                 (GES.TestClip, 0, 10),
355             ],
356             [
357                 (GES.TestClip, 10, 10),
358                 (GES.TestClip, 20, 10),
359                 (GES.TestClip, 30, 10),
360             ],
361         ], groups=[group_clips])