~timo-jyrinki/ubuntu/trusty/pitivi/backport_utopic_fixes

« back to all changes in this revision

Viewing changes to tests/test_project.py

  • Committer: Package Import Robot
  • Author(s): Sebastian Dröge
  • Date: 2014-03-29 15:22:50 UTC
  • mto: (3.1.23 experimental)
  • mto: This revision was merged to the branch mainline in revision 44.
  • Revision ID: package-import@ubuntu.com-20140329152250-flg9onx416bqf3e3
Tags: upstream-0.93
Import upstream version 0.93

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright (c) 2009, Alessandro Decina <alessandro.d@gmail.com>
 
4
# Copyright (c) 2013, Alex Băluț <alexandru.balut@gmail.com>
 
5
#
 
6
# This program is free software; you can redistribute it and/or
 
7
# modify it under the terms of the GNU Lesser General Public
 
8
# License as published by the Free Software Foundation; either
 
9
# version 2.1 of the License, or (at your option) any later version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
# Lesser General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU Lesser General Public
 
17
# License along with this program; if not, write to the
 
18
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 
19
# Boston, MA 02110-1301, USA.
 
20
 
 
21
import os
 
22
import tempfile
 
23
import time
 
24
 
 
25
from unittest import TestCase
 
26
 
 
27
from gi.repository import GES
 
28
from gi.repository import GLib
 
29
 
 
30
from pitivi.project import Project, ProjectManager
 
31
from pitivi.utils.misc import uri_is_reachable
 
32
 
 
33
 
 
34
class MockProject(object):
 
35
    settings = None
 
36
    format = None
 
37
    uri = None
 
38
    has_mods = True
 
39
 
 
40
    def hasUnsavedModifications(self):
 
41
        return self.has_mods
 
42
 
 
43
    def release(self):
 
44
        pass
 
45
 
 
46
    def disconnect_by_function(self, ignored):
 
47
        pass
 
48
 
 
49
 
 
50
class ProjectManagerListener(object):
 
51
    def __init__(self, manager):
 
52
        self.manager = manager
 
53
        self.connectToProjectManager(self.manager)
 
54
        self._reset()
 
55
 
 
56
    def _reset(self):
 
57
        self.signals = []
 
58
 
 
59
    def connectToProjectManager(self, manager):
 
60
        for signal in ("new-project-loading", "new-project-loaded",
 
61
                "new-project-created", "new-project-failed", "missing-uri",
 
62
                "closing-project", "project-closed"):
 
63
            self.manager.connect(signal, self._recordSignal, signal)
 
64
 
 
65
    def _recordSignal(self, *args):
 
66
        signal = args[-1]
 
67
        args = args[1:-1]
 
68
        self.signals.append((signal, args))
 
69
 
 
70
        return True
 
71
 
 
72
 
 
73
class TestProjectManager(TestCase):
 
74
    def setUp(self):
 
75
        self.manager = ProjectManager(None)
 
76
        self.listener = ProjectManagerListener(self.manager)
 
77
        self.signals = self.listener.signals
 
78
 
 
79
    def testLoadProjectFailedUnknownFormat(self):
 
80
        """
 
81
        Check that new-project-failed is emitted when we don't have a suitable
 
82
        formatter.
 
83
        """
 
84
        uri = "file:///Untitled.meh"
 
85
        self.manager.loadProject(uri)
 
86
 
 
87
        # loading
 
88
        name, args = self.signals[0]
 
89
        self.assertEqual(uri, args[0], self.signals)
 
90
 
 
91
        # failed
 
92
        name, args = self.signals[1]
 
93
        self.assertEqual("new-project-failed", name)
 
94
        signalUri, unused_message = args
 
95
        self.assertEqual(uri, signalUri, self.signals)
 
96
 
 
97
    def testLoadProjectClosesCurrent(self):
 
98
        """
 
99
        Check that new-project-failed is emited if we can't close the current
 
100
        project instance.
 
101
        """
 
102
        state = {"tried-close": False}
 
103
 
 
104
        def close():
 
105
            state["tried-close"] = True
 
106
            return False
 
107
        self.manager.closeRunningProject = close
 
108
 
 
109
        uri = "file:///Untitled.xptv"
 
110
        self.manager.current_project = MockProject()
 
111
        self.manager.loadProject(uri)
 
112
 
 
113
        self.assertEqual(0, len(self.signals))
 
114
        self.failUnless(state["tried-close"], self.signals)
 
115
 
 
116
    def testLoadProject(self):
 
117
        self.manager.newBlankProject()
 
118
 
 
119
        name, args = self.signals[0]
 
120
        self.assertEqual("new-project-loading", name, self.signals)
 
121
 
 
122
        name, args = self.signals[1]
 
123
        self.assertEqual("new-project-created", name, self.signals)
 
124
 
 
125
        name, args = self.signals[2]
 
126
        self.assertEqual("new-project-loaded", name, self.signals)
 
127
 
 
128
    def testMissingUriForwarded(self):
 
129
        def quit(mainloop):
 
130
            mainloop.quit()
 
131
 
 
132
        def missingUriCb(self, project, error, clip_asset, mainloop, result):
 
133
            print project, error, clip_asset, mainloop, result
 
134
            result[0] = True
 
135
            mainloop.quit()
 
136
 
 
137
        self.mainloop = GLib.MainLoop()
 
138
 
 
139
        result = [False]
 
140
        self.manager.connect("missing-uri", missingUriCb, self.mainloop, result)
 
141
 
 
142
        # Load a project with a missing asset.
 
143
        unused, xges_path = tempfile.mkstemp()
 
144
        with open(xges_path, "w") as xges:
 
145
            xges.write("""<ges version='0.1'>
 
146
  <project>
 
147
    <ressources>
 
148
      <asset id='file:///icantpossiblyexist.png' extractable-type-name='GESUriClip' />
 
149
    </ressources>
 
150
    <timeline>
 
151
      <track caps='video/x-raw' track-type='4' track-id='0' />
 
152
      <layer priority='0'>
 
153
        <clip id='0' asset-id='file:///icantpossiblyexist.png' type-name='GESUriClip' layer-priority='0' track-types='4' start='0' duration='2590000000' inpoint='0' rate='0' />
 
154
      </layer>
 
155
    </timeline>
 
156
</project>
 
157
</ges>""")
 
158
        uri = "file://%s" % xges_path
 
159
        try:
 
160
            self.assertTrue(self.manager.loadProject(uri))
 
161
 
 
162
            GLib.timeout_add_seconds(5, quit, self.mainloop)
 
163
            self.mainloop.run()
 
164
            self.assertTrue(result[0], "missing not missing")
 
165
        finally:
 
166
            os.remove(xges_path)
 
167
 
 
168
    def testCloseRunningProjectNoProject(self):
 
169
        self.failUnless(self.manager.closeRunningProject())
 
170
        self.failIf(self.signals)
 
171
 
 
172
    def testCloseRunningProjectRefuseFromSignal(self):
 
173
        def closing(manager, project):
 
174
            return False
 
175
 
 
176
        self.manager.current_project = MockProject()
 
177
        self.manager.current_project.uri = "file:///ciao"
 
178
        self.manager.connect("closing-project", closing)
 
179
 
 
180
        self.failIf(self.manager.closeRunningProject())
 
181
        self.assertEqual(1, len(self.signals))
 
182
        name, args = self.signals[0]
 
183
        self.assertEqual("closing-project", name)
 
184
        project = args[0]
 
185
        self.failUnless(project is self.manager.current_project)
 
186
 
 
187
    def testCloseRunningProject(self):
 
188
        current = self.manager.current_project = MockProject()
 
189
        self.failUnless(self.manager.closeRunningProject())
 
190
        self.assertEqual(2, len(self.signals))
 
191
 
 
192
        name, args = self.signals[0]
 
193
        self.assertEqual("closing-project", name)
 
194
        project = args[0]
 
195
        self.failUnless(project is current)
 
196
 
 
197
        name, args = self.signals[1]
 
198
        self.assertEqual("project-closed", name)
 
199
        project = args[0]
 
200
        self.failUnless(project is current)
 
201
 
 
202
        self.failUnless(self.manager.current_project is None)
 
203
 
 
204
    def testNewBlankProjectCantCloseCurrent(self):
 
205
        def closing(manager, project):
 
206
            return False
 
207
 
 
208
        self.manager.current_project = MockProject()
 
209
        self.manager.current_project.uri = "file:///ciao"
 
210
        self.manager.connect("closing-project", closing)
 
211
        self.failIf(self.manager.newBlankProject())
 
212
        self.assertEqual(1, len(self.signals))
 
213
        signal, args = self.signals[0]
 
214
        self.assertEqual("closing-project", signal)
 
215
 
 
216
    def testNewBlankProject(self):
 
217
        self.failUnless(self.manager.newBlankProject())
 
218
        self.assertEqual(3, len(self.signals))
 
219
 
 
220
        name, args = self.signals[0]
 
221
        self.assertEqual("new-project-loading", name)
 
222
        uri = args[0]
 
223
        self.failUnless(uri is None)
 
224
 
 
225
        name, args = self.signals[1]
 
226
        self.assertEqual("new-project-created", name)
 
227
        project = args[0]
 
228
        self.assertEqual(uri, project.uri)
 
229
 
 
230
        name, args = self.signals[2]
 
231
        self.assertEqual("new-project-loaded", name)
 
232
        project = args[0]
 
233
        self.failUnless(project is self.manager.current_project)
 
234
 
 
235
    def testSaveProject(self):
 
236
        self.failUnless(self.manager.newBlankProject())
 
237
 
 
238
        unused, path = tempfile.mkstemp(suffix=".xges")
 
239
        unused, path2 = tempfile.mkstemp(suffix=".xges")
 
240
        try:
 
241
            uri = "file://" + os.path.abspath(path)
 
242
            uri2 = "file://" + os.path.abspath(path2)
 
243
 
 
244
            # Save the project.
 
245
            self.failUnless(self.manager.saveProject(uri=uri, backup=False))
 
246
            self.failUnless(uri_is_reachable(uri))
 
247
 
 
248
            # Wait a bit.
 
249
            time.sleep(0.1)
 
250
 
 
251
            # Save the project at a new location.
 
252
            self.failUnless(self.manager.saveProject(uri2, backup=False))
 
253
            self.failUnless(uri_is_reachable(uri2))
 
254
 
 
255
            # Make sure the old path and the new path have different mtimes.
 
256
            mtime = os.path.getmtime(path)
 
257
            mtime2 = os.path.getmtime(path2)
 
258
            self.assertLess(mtime, mtime2)
 
259
 
 
260
            # Wait a bit more.
 
261
            time.sleep(0.1)
 
262
 
 
263
            # Save project again under the new path (by omitting uri arg)
 
264
            self.failUnless(self.manager.saveProject(backup=False))
 
265
 
 
266
            # regression test for bug 594396
 
267
            # make sure we didn't save to the old URI
 
268
            self.assertEqual(mtime, os.path.getmtime(path))
 
269
            # make sure we did save to the new URI
 
270
            self.assertLess(mtime2, os.path.getmtime(path2))
 
271
        finally:
 
272
            os.remove(path)
 
273
            os.remove(path2)
 
274
 
 
275
    def testMakeBackupUri(self):
 
276
        uri = "file:///tmp/x.xges"
 
277
        self.assertEqual(uri + "~", self.manager._makeBackupURI(uri))
 
278
 
 
279
    def testBackupProject(self):
 
280
        self.manager.newBlankProject()
 
281
 
 
282
        # Assign an uri to the project where it's saved by default.
 
283
        unused, xges_path = tempfile.mkstemp(suffix=".xges")
 
284
        uri = "file://" + os.path.abspath(xges_path)
 
285
        self.manager.current_project.uri = uri
 
286
        # This is where the automatic backup file is saved.
 
287
        backup_uri = self.manager._makeBackupURI(uri)
 
288
 
 
289
        # Save the backup
 
290
        self.assertTrue(self.manager.saveProject(self.manager.current_project, backup=True))
 
291
        self.failUnless(uri_is_reachable(backup_uri))
 
292
 
 
293
        self.manager.closeRunningProject()
 
294
        self.assertFalse(uri_is_reachable(backup_uri), "Backup file not deleted when project closed")
 
295
 
 
296
 
 
297
class TestProjectLoading(TestCase):
 
298
 
 
299
    def setUp(self):
 
300
        self.mainloop = GLib.MainLoop()
 
301
 
 
302
    def tearDown(self):
 
303
        pass
 
304
 
 
305
    def testLoadedCallback(self):
 
306
        def loaded(project, timeline, mainloop, result):
 
307
            result[0] = True
 
308
            mainloop.quit()
 
309
 
 
310
        def quit(mainloop):
 
311
            mainloop.quit()
 
312
 
 
313
        # Create a blank project and save it.
 
314
        project = Project("noname")
 
315
        result = [False]
 
316
        project.connect("loaded", loaded, self.mainloop, result)
 
317
 
 
318
        self.assertTrue(project.createTimeline())
 
319
        GLib.timeout_add_seconds(5, quit, self.mainloop)
 
320
        self.mainloop.run()
 
321
        self.assertTrue(result[0], "Blank project creation failed to trigger signal: loaded")
 
322
 
 
323
        # Load the blank project and make sure "loaded" is triggered.
 
324
        unused, xges_path = tempfile.mkstemp()
 
325
        uri = "file://%s" % xges_path
 
326
        try:
 
327
            project.save(project.timeline, uri, None, overwrite=True)
 
328
 
 
329
            project2 = Project(uri=uri)
 
330
            self.assertTrue(project2.createTimeline())
 
331
            result = [False]
 
332
            project2.connect("loaded", loaded, self.mainloop, result)
 
333
            GLib.timeout_add_seconds(5, quit, self.mainloop)
 
334
            self.mainloop.run()
 
335
            self.assertTrue(result[0], "Blank project loading failed to trigger signal: loaded")
 
336
        finally:
 
337
            os.remove(xges_path)
 
338
 
 
339
    def testAssetAddingRemovingAdding(self):
 
340
        def loaded(project, timeline, mainloop, result, uris):
 
341
            result[0] = True
 
342
            project.addUris(uris)
 
343
 
 
344
        def added(project, mainloop, result, uris):
 
345
            result[1] = True
 
346
            assets = project.list_assets(GES.UriClip)
 
347
            asset = assets[0]
 
348
            project.remove_asset(asset)
 
349
            GLib.idle_add(readd, mainloop, result, uris)
 
350
 
 
351
        def readd(mainloop, result, uris):
 
352
            project.addUris(uris)
 
353
            result[2] = True
 
354
            mainloop.quit()
 
355
 
 
356
        def quit(mainloop):
 
357
            mainloop.quit()
 
358
 
 
359
        # Create a blank project and save it.
 
360
        project = Project("noname")
 
361
        result = [False, False, False]
 
362
        uris = ["file://%s/samples/tears_of_steel.webm" % os.path.dirname(os.path.abspath(__file__))]
 
363
        project.connect("loaded", loaded, self.mainloop, result, uris)
 
364
        project.connect("done-importing", added, self.mainloop, result, uris)
 
365
 
 
366
        self.assertTrue(project.createTimeline())
 
367
        GLib.timeout_add_seconds(5, quit, self.mainloop)
 
368
        self.mainloop.run()
 
369
        self.assertTrue(result[0], "Project creation failed to trigger signal: loaded")
 
370
        self.assertTrue(result[1], "Asset add failed to trigger signal: done-importing")
 
371
        self.assertTrue(result[2], "Asset re-adding failed")
 
372
 
 
373
 
 
374
class TestExportSettings(TestCase):
 
375
    """Test the project.MultimediaSettings class."""
 
376
 
 
377
    def setUp(self):
 
378
        self.project = Project()
 
379
 
 
380
    def testMasterAttributes(self):
 
381
        self._testMasterAttribute('muxer', dependant_attr='containersettings')
 
382
        self._testMasterAttribute('vencoder', dependant_attr='vcodecsettings')
 
383
        self._testMasterAttribute('aencoder', dependant_attr='acodecsettings')
 
384
 
 
385
    def _testMasterAttribute(self, attr, dependant_attr):
 
386
        """Test changing the specified attr has effect on its dependant attr."""
 
387
        attr_value1 = "%s_value1" % attr
 
388
        attr_value2 = "%s_value2" % attr
 
389
 
 
390
        setattr(self.project, attr, attr_value1)
 
391
        setattr(self.project, dependant_attr, {})
 
392
        getattr(self.project, dependant_attr)["key1"] = "v1"
 
393
 
 
394
        setattr(self.project, attr, attr_value2)
 
395
        setattr(self.project, dependant_attr, {})
 
396
        getattr(self.project, dependant_attr)["key2"] = "v2"
 
397
 
 
398
        setattr(self.project, attr, attr_value1)
 
399
        self.assertTrue("key1" in getattr(self.project, dependant_attr))
 
400
        self.assertFalse("key2" in getattr(self.project, dependant_attr))
 
401
        self.assertEqual("v1", getattr(self.project, dependant_attr)["key1"])
 
402
        setattr(self.project, dependant_attr, {})
 
403
 
 
404
        setattr(self.project, attr, attr_value2)
 
405
        self.assertFalse("key1" in getattr(self.project, dependant_attr))
 
406
        self.assertTrue("key2" in getattr(self.project, dependant_attr))
 
407
        self.assertEqual("v2", getattr(self.project, dependant_attr)["key2"])
 
408
        setattr(self.project, dependant_attr, {})
 
409
 
 
410
        setattr(self.project, attr, attr_value1)
 
411
        self.assertFalse("key1" in getattr(self.project, dependant_attr))
 
412
        self.assertFalse("key2" in getattr(self.project, dependant_attr))
 
413
 
 
414
        setattr(self.project, attr, attr_value2)
 
415
        self.assertFalse("key1" in getattr(self.project, dependant_attr))
 
416
        self.assertFalse("key2" in getattr(self.project, dependant_attr))