[pitivi] Add tests for ProjectManager.loadProject and fix things in the process.



commit 013ef2efdd64efc216b2e9a552559b4608b0e145
Author: Alessandro Decina <alessandro d gmail com>
Date:   Thu Jun 4 19:27:54 2009 +0200

    Add tests for ProjectManager.loadProject and fix things in the process.
---
 pitivi/application.py        |    6 +-
 pitivi/formatters/base.py    |   22 +++---
 pitivi/project.py            |   16 +---
 pitivi/projectmanager.py     |   33 ++++---
 pitivi/ui/mainwindow.py      |    2 +-
 pitivi/ui/propertyeditor.py  |    2 +-
 tests/test_projectmanager.py |  188 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 225 insertions(+), 44 deletions(-)

diff --git a/pitivi/application.py b/pitivi/application.py
index 7ed7a4a..b2f506a 100644
--- a/pitivi/application.py
+++ b/pitivi/application.py
@@ -88,7 +88,7 @@ class Pitivi(Loggable, Signallable):
     __signals__ = {
         "new-project" : ["project"],
 
-        "new-project-loading" : ["project"],
+        "new-project-loading" : ["uri"],
         "new-project-loaded" : ["project"],
         "new-project-failed" : ["uri", "exception"],
         "closing-project" : ["project"],
@@ -172,8 +172,8 @@ class Pitivi(Loggable, Signallable):
         projectManager.connect("project-closed",
                 self._projectManagerProjectClosed)
 
-    def _projectManagerNewProjectLoading(self, projectManager, project):
-        self.emit("new-project-loading", project)
+    def _projectManagerNewProjectLoading(self, projectManager, uri):
+        self.emit("new-project-loading", uri)
 
     def _projectManagerNewProjectLoaded(self, projectManager, project):
         self.current = project
diff --git a/pitivi/formatters/base.py b/pitivi/formatters/base.py
index c02ab5b..707ca84 100644
--- a/pitivi/formatters/base.py
+++ b/pitivi/formatters/base.py
@@ -33,7 +33,7 @@ from pitivi.factories.base import SourceFactory
 class FormatterError(Exception):
     pass
 
-class FormatterURIError(Exception):
+class FormatterURIError(FormatterError):
     """An error occured with a URI"""
 
 class FormatterLoadError(FormatterError):
@@ -63,7 +63,6 @@ class Formatter(Signallable, Loggable):
     """
 
     __signals__ = {
-        "new-project-loading": ["project"],
         "new-project-loaded": ["project"],
         "new-project-failed": ["uri", "exception"],
         "missing-uri" : ["uri"]
@@ -92,6 +91,15 @@ class Formatter(Signallable, Loggable):
         except FormatterError, e:
             self.emit("new-project-failed", location, e)
 
+    def _validateUri(self, uri):
+        # check if the location is
+        # .. a uri
+        # .. a valid uri
+        # .. a reachable valid uri
+        # FIXME : Allow subclasses to handle this for 'online' (non-file://) URI
+        if not uri_is_valid(uri) or not uri_is_reachable(uri):
+            raise FormatterURIError()
+
     def _loadProjectUnchecked(self, location, project=None):
         """
         Loads the project from the given location.
@@ -111,15 +119,7 @@ class Formatter(Signallable, Loggable):
         project.uri = location
 
         self.log("location:%s, project:%r", location, project)
-        self.emit("new-project-loading", project)
-
-        # check if the location is
-        # .. a uri
-        # .. a valid uri
-        # .. a reachable valid uri
-        # FIXME : Allow subclasses to handle this for 'online' (non-file://) URI
-        if not uri_is_valid(location) or not uri_is_reachable(location):
-            raise FormatterURIError()
+        self._validateUri(location)
 
         # parse the format (subclasses)
         # FIXME : maybe have a convenience method for opening a location
diff --git a/pitivi/project.py b/pitivi/project.py
index a7ad77c..e3eda3b 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -24,19 +24,13 @@
 Project class
 """
 
-import os.path
-import gst
-import traceback
-from gettext import gettext as _
 from pitivi.log.loggable import Loggable
 from pitivi.timeline.timeline import Timeline
-from pitivi.timeline.track import Track
 from pitivi.stream import AudioStream, VideoStream
 from pitivi.pipeline import Pipeline
 from pitivi.factories.timeline import TimelineSourceFactory
 from pitivi.sourcelist import SourceList
 from pitivi.settings import ExportSettings
-from pitivi.configure import APPNAME
 from pitivi.signalinterface import Signallable
 from pitivi.action import ViewAction
 
@@ -44,10 +38,6 @@ class ProjectError(Exception):
     """Project error"""
     pass
 
-class ProjectSaveLoadError(ProjectError):
-    """Error while loading/saving project"""
-    pass
-
 class Project(Signallable, Loggable):
     """The base class for PiTiVi projects
 
@@ -189,18 +179,16 @@ class Project(Signallable, Loggable):
         @type location: C{URI}
         @param overwrite: Whether to overwrite existing location.
         @type overwrite: C{bool}
-
-        @raises ProjectSaveLoadError: If no uri was provided and none was set
-        previously.
         """
         # import here to break circular import
         from pitivi.formatters.format import save_project
+        from pitivi.formatters.base import FormatterError
 
         self.log("saving...")
         location = location or self.uri
 
         if location == None:
-            raise ProjectSaveLoadError("Location unknown")
+            raise FormatterError("Location unknown")
 
         save_project(self, location or self.uri, self.format,
                      overwrite)
diff --git a/pitivi/projectmanager.py b/pitivi/projectmanager.py
index 5fd8d69..a4130fb 100644
--- a/pitivi/projectmanager.py
+++ b/pitivi/projectmanager.py
@@ -26,19 +26,21 @@ import gst
 
 from pitivi.project import Project
 from pitivi.formatters.format import get_formatter_for_uri
+from pitivi.formatters.base import FormatterLoadError
+
 from pitivi.signalinterface import Signallable
 from pitivi.log.loggable import Loggable
 from pitivi.stream import AudioStream, VideoStream
 from pitivi.timeline.track import Track
 
-
 class ProjectManager(Signallable, Loggable):
     __signals__ = {
-        "new-project-loading": ["project"],
-        "new-project-loaded": ["project"],
+        "new-project-loading": ["uri"],
         "new-project-failed": ["uri", "exception"],
+        "new-project-loaded": ["project"],
         "closing-project": ["project"],
         "project-closed": ["project"],
+        "missing-uri": ["formatter", "uri"],
     }
 
     def __init__(self):
@@ -49,15 +51,17 @@ class ProjectManager(Signallable, Loggable):
 
     def loadProject(self, uri):
         """ Load the given project file"""
-        formatter = get_formatter_for_uri(uri)
+        self.emit("new-project-loading", uri)
+
+        formatter = self._getFormatterForUri(uri)
         if not formatter:
             self.emit("new-project-failed", uri,
-                    Exception(_("Not a valid project file.")))
+                    FormatterLoadError(_("Not a valid project file.")))
             return
 
         if not self.closeRunningProject():
             self.emit("new-project-failed", uri,
-                    Exception(_("Couldn't close current project")))
+                    FormatterLoadError(_("Couldn't close current project")))
             return
 
         project = formatter.newProject()
@@ -69,6 +73,9 @@ class ProjectManager(Signallable, Loggable):
         """ close the current project """
         self.info("closing running project")
 
+        if self.current is None:
+            return True
+
         if self.current.hasUnsavedModifications():
             if not self.current.save():
                 return False
@@ -88,8 +95,9 @@ class ProjectManager(Signallable, Loggable):
         if self.current is not None and not self.closeRunningProject():
             return
 
+        # we don't have an URI here, None means we're loading a new project
+        self.emit("new-project-loading", None)
         project = Project(_("New Project"))
-        self.emit("new-project-loading", project)
         self.current = project
 
         # FIXME: this should not be hard-coded
@@ -104,10 +112,11 @@ class ProjectManager(Signallable, Loggable):
         self.emit("new-project-loaded", self.current)
 
 
+    def _getFormatterForUri(self, uri):
+        return get_formatter_for_uri(uri)
+
     def _connectToFormatter(self, formatter):
         formatter.connect("missing-uri", self._formatterMissingURICb)
-        formatter.connect("new-project-loading",
-                self._formatterNewProjectLoading)
         formatter.connect("new-project-loaded",
                 self._formatterNewProjectLoaded)
         formatter.connect("new-project-failed",
@@ -115,13 +124,9 @@ class ProjectManager(Signallable, Loggable):
 
     def _disconnectFromFormatter(self, formatter):
         formatter.disconnect_by_function(self._formatterMissingURICb)
-        formatter.disconnect_by_function(self._formatterNewProjectLoading)
         formatter.disconnect_by_function(self._formatterNewProjectLoaded)
         formatter.disconnect_by_function(self._formatterNewProjectFailed)
 
-    def _formatterNewProjectLoading(self, formatter, project):
-        self.emit("new-project-loading", project)
-
     def _formatterNewProjectLoaded(self, formatter, project):
         self._disconnectFromFormatter(formatter)
 
@@ -137,4 +142,4 @@ class ProjectManager(Signallable, Loggable):
         self.emit("new-project-failed", uri, exception)
 
     def _formatterMissingURICb(self, formatter, uri):
-        self.emit("missing-uri", formatter, uri)
+        return self.emit("missing-uri", formatter, uri)
diff --git a/pitivi/ui/mainwindow.py b/pitivi/ui/mainwindow.py
index 60bdd11..469b1b9 100644
--- a/pitivi/ui/mainwindow.py
+++ b/pitivi/ui/mainwindow.py
@@ -638,13 +638,13 @@ class PitiviMainWindow(gtk.Window, Loggable):
     @handler(app, "new-project-loaded")
     def _newProjectLoadedCb(self, unused_pitivi, project):
         self.log("A NEW project is loaded, update the UI!")
+        self.project = project
         # ungrey UI
         self.set_sensitive(True)
 
     @handler(app, "new-project-loading")
     def _newProjectLoadingCb(self, unused_instance, project):
         self.log("A NEW project is being loaded, deactivate UI")
-        self.project = project
         # grey UI
         self.set_sensitive(False)
 
diff --git a/pitivi/ui/propertyeditor.py b/pitivi/ui/propertyeditor.py
index e35e73a..556e71f 100644
--- a/pitivi/ui/propertyeditor.py
+++ b/pitivi/ui/propertyeditor.py
@@ -87,7 +87,7 @@ class PropertyEditor(gtk.ScrolledWindow):
 
     instance = receiver()
 
-    @handler(instance, "new-project-loading")
+    @handler(instance, "new-project-loaded")
     def _newProjectLoading(self, unused_inst, project):
         self.timeline = project.timeline
 
diff --git a/tests/test_projectmanager.py b/tests/test_projectmanager.py
new file mode 100644
index 0000000..9a930b5
--- /dev/null
+++ b/tests/test_projectmanager.py
@@ -0,0 +1,188 @@
+# PiTiVi , Non-linear video editor
+#
+#       tests/test_projectmanager.py
+#
+# Copyright (c) 2009, Alessandro Decina <alessandro d gmail com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+from unittest import TestCase
+
+from pitivi.projectmanager import ProjectManager
+from pitivi.formatters.base import Formatter, \
+        FormatterError, FormatterLoadError
+
+class ProjectManagerListener(object):
+    def __init__(self, manager):
+        self.manager = manager
+        self.connectToProjectManager(self.manager)
+        self._reset()
+
+    def _reset(self):
+        self.signals = []
+
+    def connectToProjectManager(self, manager):
+        for signal in ("new-project-loading", "new-project-loaded",
+            "new-project-failed", "missing-uri"):
+            self.manager.connect(signal, self._recordSignal, signal)
+
+    def _recordSignal(self, *args):
+        signal = args[-1]
+        args = args[1:-1]
+        self.signals.append((signal, args))
+
+
+class TestProjectManager(TestCase):
+    def setUp(self):
+        self.manager = ProjectManager()
+        self.listener = ProjectManagerListener(self.manager)
+        self.signals = self.listener.signals
+
+    def testLoadProjectFailedUnknownFormat(self):
+        """
+        Check that new-project-failed is emitted when we don't have a suitable
+        formatter.
+        """
+        uri = "file:///Untitled.meh"
+        self.manager.loadProject(uri)
+        self.failUnlessEqual(len(self.signals), 2)
+
+        # loading
+        name, args = self.signals[0]
+        self.failUnlessEqual(args[0], uri)
+
+        # failed
+        name, args = self.signals[1]
+        self.failUnlessEqual(name, "new-project-failed")
+        signalUri, exception = args
+        self.failUnlessEqual(uri, signalUri)
+        self.failUnless(isinstance(exception, FormatterLoadError))
+
+    def testLoadProjectFailedCloseCurrent(self):
+        """
+        Check that new-project-failed is emited if we can't close the current
+        project instance.
+        """
+        state = {"tried-close": False}
+        def close():
+            state["tried-close"] = True
+            return False
+        self.manager.closeRunningProject = close
+
+        uri = "file:///Untitled.xptv"
+        self.manager.loadProject(uri)
+        self.failUnlessEqual(len(self.signals), 2)
+
+        # loading
+        name, args = self.signals[0]
+        self.failUnlessEqual(args[0], uri)
+
+        # failed
+        name, args = self.signals[1]
+        self.failUnlessEqual(name, "new-project-failed")
+        signalUri, exception = args
+        self.failUnlessEqual(uri, signalUri)
+        self.failUnless(isinstance(exception, FormatterLoadError))
+        self.failUnless(state["tried-close"])
+
+    def testLoadProjectFailedProxyFormatter(self):
+        """
+        Check that new-project-failed is proxied when a formatter emits it.
+        """
+        class FailFormatter(Formatter):
+            def _validateUri(self, uri):
+                pass
+
+            def _parse(self, location, project=None):
+                raise FormatterError()
+        self.manager._getFormatterForUri = lambda uri: FailFormatter()
+
+        uri = "file:///Untitled.xptv"
+        self.manager.loadProject(uri)
+        self.failUnlessEqual(len(self.signals), 2)
+
+        # loading
+        name, args = self.signals[0]
+        self.failUnlessEqual(args[0], uri)
+
+        # failed
+        name, args = self.signals[1]
+        self.failUnlessEqual(name, "new-project-failed")
+        signalUri, exception = args
+        self.failUnlessEqual(uri, signalUri)
+        self.failUnless(isinstance(exception, FormatterError))
+
+    def testLoadProjectMissingUri(self):
+        class FailFormatter(Formatter):
+            def _validateUri(self, uri):
+                pass
+
+            def _parse(self, location, project=None):
+                pass
+
+            def _getSources(self):
+                # this will emit missing-uri
+                self.validateSourceURI("file:///icantpossiblyexist")
+                return []
+
+            def _fillTimeline(self):
+                pass
+        self.manager._getFormatterForUri = lambda uri: FailFormatter()
+
+        uri = "file:///Untitled.xptv"
+        self.manager.loadProject(uri)
+        self.failUnlessEqual(len(self.signals), 3)
+
+        # loading
+        name, args = self.signals[0]
+        self.failUnlessEqual(args[0], uri)
+
+        # failed
+        name, args = self.signals[1]
+        self.failUnlessEqual(name, "missing-uri")
+        formatter, signalUri = args
+        self.failUnlessEqual(signalUri, "file:///icantpossiblyexist")
+
+
+    def testLoadProjectLoaded(self):
+        class FailFormatter(Formatter):
+            def _validateUri(self, uri):
+                pass
+
+            def _parse(self, location, project=None):
+                pass
+
+            def _getSources(self):
+                return []
+
+            def _fillTimeline(self):
+                pass
+        self.manager._getFormatterForUri = lambda uri: FailFormatter()
+
+        uri = "file:///Untitled.xptv"
+        self.manager.loadProject(uri)
+        self.failUnlessEqual(len(self.signals), 2)
+
+        # loading
+        name, args = self.signals[0]
+        self.failUnlessEqual(args[0], uri)
+
+        # failed
+        name, args = self.signals[1]
+        self.failUnlessEqual(name, "new-project-loaded")
+        project = args[0]
+        self.failUnlessEqual(uri, project.uri)
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]