[pitivi] undo: Rollback an action that raises an exception.



commit c34184625ebe15ec8ea7afdb414bac1b43c85001
Author: Stefan Popa <stefanpopa2209 gmail com>
Date:   Tue Aug 1 16:31:48 2017 +0300

    undo: Rollback an action that raises an exception.
    
    If an exception is raised in the self.app.action_log.started context,
    catch it and rollback the started operation. Otherwise, the following
    undoable operations will fail because the undo stack is not empty.
    
    Reviewed-by: Alex Băluț <alexandru balut gmail com>
    Differential Revision: https://phabricator.freedesktop.org/D1816

 pitivi/undo/undo.py |   11 +++++++++--
 tests/test_undo.py  |   18 ++++++++++++++++++
 2 files changed, 27 insertions(+), 2 deletions(-)
---
diff --git a/pitivi/undo/undo.py b/pitivi/undo/undo.py
index 721f5da..9c826a9 100644
--- a/pitivi/undo/undo.py
+++ b/pitivi/undo/undo.py
@@ -188,8 +188,15 @@ class UndoableActionLog(GObject.Object, Loggable):
     def started(self, action_group_name, **kwargs):
         """Gets a context manager which commits the transaction at the end."""
         self.begin(action_group_name, **kwargs)
-        yield
-        self.commit(action_group_name)
+        try:
+            yield
+        except:
+            self.warning("An exception occurred while recording a "
+                         "high-level operation. Rolling back.")
+            self.rollback()
+            raise
+        else:
+            self.commit(action_group_name)
 
     def begin(self, action_group_name, finalizing_action=None, toplevel=False):
         """Starts recording a high-level operation which later can be undone.
diff --git a/tests/test_undo.py b/tests/test_undo.py
index 9c5f386..408458a 100644
--- a/tests/test_undo.py
+++ b/tests/test_undo.py
@@ -388,6 +388,24 @@ class TestUndoableActionLog(TestCase):
         self.log.begin("nested1")
         self.log.begin("nested2", toplevel=False)
 
+    def test_failing_operation_rollback(self):
+        """Checks that failing operations are rolled back."""
+        action = mock.Mock(spec=UndoableAction)
+
+        class WatchingError(Exception):
+            pass
+
+        with self.assertRaises(WatchingError):
+            with self.log.started("failing_op"):
+                self.log.push(action)
+                raise WatchingError()
+
+        # Check the rollback happened
+        self.assertEqual(action.do.call_count, 0)
+        self.assertEqual(action.undo.call_count, 1)
+        # Check the undo and redo stacks are empty
+        self.assertEqual(len(self.log.undo_stacks), 0)
+        self.assertEqual(len(self.log.redo_stacks), 0)
 
 class TestGObjectObserver(TestCase):
 


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