Rev 3033: Generate percentage complete estimation for a whole TaskStack. in http://people.ubuntu.com/~robertc/baz2.0/nested-pb

Robert Collins robertc at robertcollins.net
Tue Nov 20 23:23:46 GMT 2007


At http://people.ubuntu.com/~robertc/baz2.0/nested-pb

------------------------------------------------------------
revno: 3033
revision-id:robertc at robertcollins.net-20071120232331-1se1u6rrqgyqfcae
parent: robertc at robertcollins.net-20071120225024-3x716f66hbw4q4c6
committer: Robert Collins <robertc at robertcollins.net>
branch nick: pb.simplify
timestamp: Wed 2007-11-21 10:23:31 +1100
message:
  Generate percentage complete estimation for a whole TaskStack.
modified:
  bzrlib/progress.py             progress.py-20050610070202-df9faaab791964c0
  bzrlib/tests/test_progress.py  test_progress.py-20060308160359-978c397bc79b7fda
=== modified file 'bzrlib/progress.py'
--- a/bzrlib/progress.py	2007-11-20 22:50:24 +0000
+++ b/bzrlib/progress.py	2007-11-20 23:23:31 +0000
@@ -202,6 +202,24 @@
             extra_message = ":" + self.tasks[-1].get_message()
         return KnownLengthTask.get_message(self) + extra_message
 
+    def percent_complete(self):
+        """Return the percentage of the stack that has been completed."""
+        # NB: This almost certainly wants to be cached.
+        result = self.current/float(self.total)
+        scaling_factor = self.tick_size / float(self.total)
+        for task in self.tasks:
+            try:
+                task_percent = task.percent_complete()
+            except AttributeError:
+                # Task does not support percentage estimates.
+                break
+            result += task_percent * scaling_factor
+            # narrow the scaling factor for the next task.
+            scaling_factor = scaling_factor * task.tick_size / task.total
+        if result > 1.0:
+            result = 1.0
+        return result
+
     def push_task(self, task):
         """Add task as a sub-task of this stack."""
         self.tasks.append(task)

=== modified file 'bzrlib/tests/test_progress.py'
--- a/bzrlib/tests/test_progress.py	2007-11-20 22:50:24 +0000
+++ b/bzrlib/tests/test_progress.py	2007-11-20 23:23:31 +0000
@@ -495,6 +495,79 @@
         stack.pop_task(task)
         self.assertEqual("pulling", stack.get_message())
 
+    def test_percent_complete_no_children(self):
+        # with no children, TaskStack behaves like a KnownLengthTask.
+        stack = TaskStack('', 1)
+        # Choose a tick size which will not sum to 100% to test rounding at the
+        # top.
+        stack.tick_size = 0.23
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        stack.tick()
+        self.assertEqualFloat(0.23, stack.percent_complete())
+        stack.tick()
+        self.assertEqualFloat(0.46, stack.percent_complete())
+        stack.tick()
+        self.assertEqualFloat(0.69, stack.percent_complete())
+        stack.tick()
+        self.assertEqualFloat(0.92, stack.percent_complete())
+        # At > 100% it is capped. Alternatively we could error to enforce good
+        # api usage (should be rounding down not up to give monotonic
+        # behaviour, but this seems nice to clients of the api for now.
+        stack.tick()
+        self.assertEqual(1.0, stack.percent_complete())
+
+    def test_percent_complete_counted_task_child(self):
+        # With a CountedTask child, TaskStack's percentage complete does not
+        # advance.
+        stack = TaskStack('', 1)
+        task = CountedTask("parsing")
+        task_2 = KnownLengthTask("reading", 5)
+        # Choose a tick size which will not sum to 100% to test rounding at the
+        # top.
+        stack.push_task(task)
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        task.tick()
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        # adding a KnownLengthTask under the CountedTask is ignored by
+        # percent_complete because the CountedTask prevents estimation.
+        stack.push_task(task_2)
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        task_2.tick()
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        stack.pop_task(task_2)
+        self.assertEqualFloat(0.0, stack.percent_complete())
+        stack.pop_task(task)
+        stack.tick()
+        self.assertEqualFloat(1.0, stack.percent_complete())
+
+    def test_percent_complete_known_length_task_child(self):
+        # With a KnownLengthTask children, TaskStack's percentage complete 
+        # advances proportional to the child's advance.
+        stack = TaskStack('', 2)
+        task = KnownLengthTask("parsing", 5)
+        task_2 = KnownLengthTask("reading", 5)
+        stack.tick() # 50%
+        stack.push_task(task)
+        self.assertEqualFloat(0.5, stack.percent_complete())
+        task.tick() # 50% + 0.5 * 20%
+        self.assertEqualFloat(0.5 + 0.5 * 0.2, stack.percent_complete())
+        stack.push_task(task_2) # no progress yet
+        self.assertEqualFloat(0.5 + 0.5 * 0.2, stack.percent_complete())
+        task_2.tick() # 50% + 0.5 * 20% + 0.5*0.2 * 20%
+        self.assertEqualFloat(0.5 + 0.5 * 0.2 + 0.5*0.2*0.2,
+            stack.percent_complete())
+        task_2.tick()
+        self.assertEqualFloat(0.5 + 0.5 * 0.2 + 0.5*0.2*0.4,
+            stack.percent_complete())
+        stack.pop_task(task_2)
+        self.assertEqualFloat(0.5 + 0.5 * 0.2, stack.percent_complete())
+        task.tick() # 50% + 0.5 * 40% in the child
+        self.assertEqualFloat(0.5 + 0.5 * 0.4, stack.percent_complete())
+        stack.pop_task(task)
+        self.assertEqualFloat(0.5, stack.percent_complete())
+        stack.tick() # 100%
+        self.assertEqualFloat(1.0, stack.percent_complete())
+
 
 class TestSilentTaskDisplay(TestCase):
 



More information about the bazaar-commits mailing list