1
# PiTiVi , Non-linear video editor
3
# tests/test_projectmanager.py
5
# Copyright (c) 2009, Alessandro Decina <alessandro.d@gmail.com>
7
# This program is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License as published by the Free Software Foundation; either
10
# version 2.1 of the License, or (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# Lesser General Public License for more details.
17
# You should have received a copy of the GNU Lesser General Public
18
# License along with this program; if not, write to the
19
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
# Boston, MA 02110-1301, USA.
22
from unittest import TestCase
24
from pitivi.projectmanager import ProjectManager
25
from pitivi.formatters.base import Formatter, \
26
FormatterError, FormatterLoadError
29
from pitivi.utils import uri_is_reachable
33
class MockProject(object):
39
def hasUnsavedModifications(self):
45
def disconnect_by_function(self, ignored):
49
class ProjectManagerListener(object):
50
def __init__(self, manager):
51
self.manager = manager
52
self.connectToProjectManager(self.manager)
58
def connectToProjectManager(self, manager):
59
for signal in ("new-project-loading", "new-project-loaded",
60
"new-project-created", "new-project-failed", "missing-uri",
61
"closing-project", "project-closed"):
62
self.manager.connect(signal, self._recordSignal, signal)
64
def _recordSignal(self, *args):
67
self.signals.append((signal, args))
72
class TestProjectManager(TestCase):
74
self.manager = ProjectManager()
75
self.listener = ProjectManagerListener(self.manager)
76
self.signals = self.listener.signals
78
def testLoadProjectFailedUnknownFormat(self):
80
Check that new-project-failed is emitted when we don't have a suitable
83
uri = "file:///Untitled.meh"
84
self.manager.loadProject(uri)
85
self.failUnlessEqual(len(self.signals), 2)
88
name, args = self.signals[0]
89
self.failUnlessEqual(args[0], uri)
92
name, args = self.signals[1]
93
self.failUnlessEqual(name, "new-project-failed")
94
signalUri, exception = args
95
self.failUnlessEqual(uri, signalUri)
96
self.failUnless(isinstance(exception, FormatterLoadError))
98
def testLoadProjectFailedCloseCurrent(self):
100
Check that new-project-failed is emited if we can't close the current
103
state = {"tried-close": False}
106
state["tried-close"] = True
108
self.manager.closeRunningProject = close
110
uri = "file:///Untitled.xptv"
111
self.manager.loadProject(uri)
112
self.failUnlessEqual(len(self.signals), 2)
115
name, args = self.signals[0]
116
self.failUnlessEqual(args[0], uri)
119
name, args = self.signals[1]
120
self.failUnlessEqual(name, "new-project-failed")
121
signalUri, exception = args
122
self.failUnlessEqual(uri, signalUri)
123
self.failUnless(isinstance(exception, FormatterLoadError))
124
self.failUnless(state["tried-close"])
126
def testLoadProjectFailedProxyFormatter(self):
128
Check that new-project-failed is proxied when a formatter emits it.
130
class FailFormatter(Formatter):
131
def _validateUri(self, uri):
134
def _loadProject(self, location, project=None):
135
raise FormatterError()
136
self.manager._getFormatterForUri = lambda uri: FailFormatter([])
138
uri = "file:///Untitled.xptv"
139
self.manager.loadProject(uri)
140
self.failUnlessEqual(len(self.signals), 3)
143
name, args = self.signals[0]
144
self.failUnlessEqual(name, "new-project-loading")
145
self.failUnlessEqual(args[0], uri)
148
name, args = self.signals[1]
149
self.failUnlessEqual(name, "new-project-created")
152
name, args = self.signals[2]
153
self.failUnlessEqual(name, "new-project-failed")
154
signalUri, exception = args
155
self.failUnlessEqual(uri, signalUri)
156
self.failUnless(isinstance(exception, FormatterError))
158
def testLoadProjectMissingUri(self):
159
class MissingUriFormatter(Formatter):
160
def _validateUri(self, uri):
163
def _loadProject(self, location, project):
164
self._finishLoadingProject(project)
166
def _getSources(self):
167
# this will emit missing-uri
168
self.validateSourceURI("file:///icantpossiblyexist", None)
171
def _fillTimeline(self):
173
self.manager._getFormatterForUri = lambda uri: MissingUriFormatter([])
175
uri = "file:///Untitled.xptv"
176
self.manager.loadProject(uri)
177
self.failUnlessEqual(len(self.signals), 4)
180
name, args = self.signals[0]
181
self.failUnlessEqual(name, "new-project-loading")
182
self.failUnlessEqual(args[0], uri)
184
name, args = self.signals[1]
185
self.failUnlessEqual(name, "new-project-created")
186
self.failUnlessEqual(args[0].uri, uri)
189
name, args = self.signals[2]
190
self.failUnlessEqual(name, "missing-uri")
191
formatter, signalUri, unused_factory = args
192
self.failUnlessEqual(signalUri, "file:///icantpossiblyexist")
194
def testLoadProjectLoaded(self):
195
class EmptyFormatter(Formatter):
196
def _validateUri(self, uri):
199
def _loadProject(self, location, project):
200
self._finishLoadingProject(project)
202
def _getSources(self):
205
def _fillTimeline(self):
207
self.manager._getFormatterForUri = lambda uri: EmptyFormatter([])
209
uri = "file:///Untitled.xptv"
210
self.manager.loadProject(uri)
211
self.failUnlessEqual(len(self.signals), 3)
214
name, args = self.signals[0]
215
self.failUnlessEqual(args[0], uri)
217
name, args = self.signals[1]
218
self.failUnlessEqual(name, "new-project-created")
220
self.failUnlessEqual(uri, project.uri)
222
name, args = self.signals[2]
223
self.failUnlessEqual(name, "new-project-loaded")
225
self.failUnlessEqual(uri, project.uri)
227
def testCloseRunningProjectNoProject(self):
228
self.failUnless(self.manager.closeRunningProject())
229
self.failIf(self.signals)
231
def testCloseRunningProjectRefuseFromSignal(self):
232
def closing(manager, project):
235
self.manager.current = MockProject()
236
self.manager.current.has_mods = False
237
self.manager.current.uri = "file:///ciao"
238
self.manager.connect("closing-project", closing)
240
self.failIf(self.manager.closeRunningProject())
241
self.failUnlessEqual(len(self.signals), 1)
242
name, args = self.signals[0]
243
self.failUnlessEqual(name, "closing-project")
245
self.failUnless(project is self.manager.current)
247
def testCloseRunningProject(self):
248
current = self.manager.current = MockProject()
249
self.manager.current.has_mods = False
250
self.failUnless(self.manager.closeRunningProject())
251
self.failUnlessEqual(len(self.signals), 2)
253
name, args = self.signals[0]
254
self.failUnlessEqual(name, "closing-project")
256
self.failUnless(project is current)
258
name, args = self.signals[1]
259
self.failUnlessEqual(name, "project-closed")
261
self.failUnless(project is current)
263
self.failUnlessEqual(self.manager.current, None)
265
def testNewBlankProjectCantCloseCurrent(self):
266
def closing(manager, project):
269
self.manager.current = MockProject()
270
self.manager.current.has_mods = False
271
self.manager.current.uri = "file:///ciao"
272
self.manager.connect("closing-project", closing)
273
self.failIf(self.manager.newBlankProject())
274
self.failUnlessEqual(len(self.signals), 1)
275
signal, args = self.signals[0]
276
self.failUnlessEqual(signal, "closing-project")
278
def testNewBlankProject(self):
279
self.failUnless(self.manager.newBlankProject())
280
self.failUnlessEqual(len(self.signals), 3)
282
name, args = self.signals[0]
283
self.failUnlessEqual(name, "new-project-loading")
285
self.failUnlessEqual(uri, None)
287
name, args = self.signals[1]
288
self.failUnlessEqual(name, "new-project-created")
290
self.failUnlessEqual(uri, project.uri)
292
name, args = self.signals[2]
293
self.failUnlessEqual(name, "new-project-loaded")
295
self.failUnless(project is self.manager.current)
297
def testSaveProject(self):
298
uri = "file://" + os.path.abspath("testproject.xptv")
299
uri2 = "file://" + os.path.abspath("testproject2.xptv")
300
path = gst.uri_get_location(uri)
301
path2 = gst.uri_get_location(uri2)
303
# unlink any existing project files
311
self.failUnless(self.manager.newBlankProject())
312
self.failUnless(self.manager.saveProject(
313
self.manager.current, uri, True))
314
self.failUnless(uri_is_reachable(uri))
319
# save project under new path
320
self.failUnless(self.manager.saveProject(
321
self.manager.current, uri2, True))
322
self.failUnless(uri_is_reachable(uri2))
324
# make sure the old path and the new path have different mtime
325
mtime = os.path.getmtime(path)
326
mtime2 = os.path.getmtime(path2)
327
self.failUnless(mtime < mtime2)
332
# save project again under new path (by omitting uri arg)
333
self.failUnless(self.manager.saveProject(
334
self.manager.current, overwrite=True))
336
# regression test for bug 594396
337
# make sure we didn't save to the old URI
338
self.failUnlessEqual(mtime, os.path.getmtime(path))
339
# make sure we did save to the new URI
340
self.failUnless(mtime2 < os.path.getmtime(path2))
342
# unlink any existing project files
349
def testBackupProject(self):
350
uri = "file://" + os.path.abspath("testproject.xptv")
352
# Create and save the project
353
self.manager.newBlankProject()
354
self.manager.saveProject(self.manager.current, uri, True)
357
self.manager._saveBackupCb(self.manager.current, uri)
358
backup_uri = self.manager._makeBackupURI(uri)
359
self.failUnless(uri_is_reachable(uri))
360
self.failUnless(uri_is_reachable(backup_uri))
362
# When closing it should clean the backup
363
self.manager.closeRunningProject()
364
self.failUnless(not uri_is_reachable(backup_uri))
366
# unlink any existing project files
369
os.unlink(backup_uri)