1
# -*- coding: utf-8 -*-
3
# Copyright (c) 2009, Alessandro Decina <alessandro.d@gmail.com>
4
# Copyright (c) 2013, Alex Băluț <alexandru.balut@gmail.com>
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.
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.
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.
25
from unittest import TestCase
27
from gi.repository import GES
28
from gi.repository import GLib
30
from pitivi.project import Project, ProjectManager
31
from pitivi.utils.misc import uri_is_reachable
34
class MockProject(object):
40
def hasUnsavedModifications(self):
46
def disconnect_by_function(self, ignored):
50
class ProjectManagerListener(object):
51
def __init__(self, manager):
52
self.manager = manager
53
self.connectToProjectManager(self.manager)
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)
65
def _recordSignal(self, *args):
68
self.signals.append((signal, args))
73
class TestProjectManager(TestCase):
75
self.manager = ProjectManager(None)
76
self.listener = ProjectManagerListener(self.manager)
77
self.signals = self.listener.signals
79
def testLoadProjectFailedUnknownFormat(self):
81
Check that new-project-failed is emitted when we don't have a suitable
84
uri = "file:///Untitled.meh"
85
self.manager.loadProject(uri)
88
name, args = self.signals[0]
89
self.assertEqual(uri, args[0], self.signals)
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)
97
def testLoadProjectClosesCurrent(self):
99
Check that new-project-failed is emited if we can't close the current
102
state = {"tried-close": False}
105
state["tried-close"] = True
107
self.manager.closeRunningProject = close
109
uri = "file:///Untitled.xptv"
110
self.manager.current_project = MockProject()
111
self.manager.loadProject(uri)
113
self.assertEqual(0, len(self.signals))
114
self.failUnless(state["tried-close"], self.signals)
116
def testLoadProject(self):
117
self.manager.newBlankProject()
119
name, args = self.signals[0]
120
self.assertEqual("new-project-loading", name, self.signals)
122
name, args = self.signals[1]
123
self.assertEqual("new-project-created", name, self.signals)
125
name, args = self.signals[2]
126
self.assertEqual("new-project-loaded", name, self.signals)
128
def testMissingUriForwarded(self):
132
def missingUriCb(self, project, error, clip_asset, mainloop, result):
133
print project, error, clip_asset, mainloop, result
137
self.mainloop = GLib.MainLoop()
140
self.manager.connect("missing-uri", missingUriCb, self.mainloop, result)
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'>
148
<asset id='file:///icantpossiblyexist.png' extractable-type-name='GESUriClip' />
151
<track caps='video/x-raw' track-type='4' track-id='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' />
158
uri = "file://%s" % xges_path
160
self.assertTrue(self.manager.loadProject(uri))
162
GLib.timeout_add_seconds(5, quit, self.mainloop)
164
self.assertTrue(result[0], "missing not missing")
168
def testCloseRunningProjectNoProject(self):
169
self.failUnless(self.manager.closeRunningProject())
170
self.failIf(self.signals)
172
def testCloseRunningProjectRefuseFromSignal(self):
173
def closing(manager, project):
176
self.manager.current_project = MockProject()
177
self.manager.current_project.uri = "file:///ciao"
178
self.manager.connect("closing-project", closing)
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)
185
self.failUnless(project is self.manager.current_project)
187
def testCloseRunningProject(self):
188
current = self.manager.current_project = MockProject()
189
self.failUnless(self.manager.closeRunningProject())
190
self.assertEqual(2, len(self.signals))
192
name, args = self.signals[0]
193
self.assertEqual("closing-project", name)
195
self.failUnless(project is current)
197
name, args = self.signals[1]
198
self.assertEqual("project-closed", name)
200
self.failUnless(project is current)
202
self.failUnless(self.manager.current_project is None)
204
def testNewBlankProjectCantCloseCurrent(self):
205
def closing(manager, project):
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)
216
def testNewBlankProject(self):
217
self.failUnless(self.manager.newBlankProject())
218
self.assertEqual(3, len(self.signals))
220
name, args = self.signals[0]
221
self.assertEqual("new-project-loading", name)
223
self.failUnless(uri is None)
225
name, args = self.signals[1]
226
self.assertEqual("new-project-created", name)
228
self.assertEqual(uri, project.uri)
230
name, args = self.signals[2]
231
self.assertEqual("new-project-loaded", name)
233
self.failUnless(project is self.manager.current_project)
235
def testSaveProject(self):
236
self.failUnless(self.manager.newBlankProject())
238
unused, path = tempfile.mkstemp(suffix=".xges")
239
unused, path2 = tempfile.mkstemp(suffix=".xges")
241
uri = "file://" + os.path.abspath(path)
242
uri2 = "file://" + os.path.abspath(path2)
245
self.failUnless(self.manager.saveProject(uri=uri, backup=False))
246
self.failUnless(uri_is_reachable(uri))
251
# Save the project at a new location.
252
self.failUnless(self.manager.saveProject(uri2, backup=False))
253
self.failUnless(uri_is_reachable(uri2))
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)
263
# Save project again under the new path (by omitting uri arg)
264
self.failUnless(self.manager.saveProject(backup=False))
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))
275
def testMakeBackupUri(self):
276
uri = "file:///tmp/x.xges"
277
self.assertEqual(uri + "~", self.manager._makeBackupURI(uri))
279
def testBackupProject(self):
280
self.manager.newBlankProject()
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)
290
self.assertTrue(self.manager.saveProject(self.manager.current_project, backup=True))
291
self.failUnless(uri_is_reachable(backup_uri))
293
self.manager.closeRunningProject()
294
self.assertFalse(uri_is_reachable(backup_uri), "Backup file not deleted when project closed")
297
class TestProjectLoading(TestCase):
300
self.mainloop = GLib.MainLoop()
305
def testLoadedCallback(self):
306
def loaded(project, timeline, mainloop, result):
313
# Create a blank project and save it.
314
project = Project("noname")
316
project.connect("loaded", loaded, self.mainloop, result)
318
self.assertTrue(project.createTimeline())
319
GLib.timeout_add_seconds(5, quit, self.mainloop)
321
self.assertTrue(result[0], "Blank project creation failed to trigger signal: loaded")
323
# Load the blank project and make sure "loaded" is triggered.
324
unused, xges_path = tempfile.mkstemp()
325
uri = "file://%s" % xges_path
327
project.save(project.timeline, uri, None, overwrite=True)
329
project2 = Project(uri=uri)
330
self.assertTrue(project2.createTimeline())
332
project2.connect("loaded", loaded, self.mainloop, result)
333
GLib.timeout_add_seconds(5, quit, self.mainloop)
335
self.assertTrue(result[0], "Blank project loading failed to trigger signal: loaded")
339
def testAssetAddingRemovingAdding(self):
340
def loaded(project, timeline, mainloop, result, uris):
342
project.addUris(uris)
344
def added(project, mainloop, result, uris):
346
assets = project.list_assets(GES.UriClip)
348
project.remove_asset(asset)
349
GLib.idle_add(readd, mainloop, result, uris)
351
def readd(mainloop, result, uris):
352
project.addUris(uris)
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)
366
self.assertTrue(project.createTimeline())
367
GLib.timeout_add_seconds(5, quit, self.mainloop)
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")
374
class TestExportSettings(TestCase):
375
"""Test the project.MultimediaSettings class."""
378
self.project = Project()
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')
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
390
setattr(self.project, attr, attr_value1)
391
setattr(self.project, dependant_attr, {})
392
getattr(self.project, dependant_attr)["key1"] = "v1"
394
setattr(self.project, attr, attr_value2)
395
setattr(self.project, dependant_attr, {})
396
getattr(self.project, dependant_attr)["key2"] = "v2"
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, {})
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, {})
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))
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))