Rev 4745: (mbp) Add UIFactory.show_error, show_message, show_warning; in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Oct 14 09:51:49 BST 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4745 [merge]
revision-id: pqm at pqm.ubuntu.com-20091014085144-t3bcnsw7rvm7b6ge
parent: pqm at pqm.ubuntu.com-20091014075341-xjtgl5ji20autac6
parent: mbp at sourcefrog.net-20090923062946-084iws6sphrck1gm
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2009-10-14 09:51:44 +0100
message:
  (mbp) Add UIFactory.show_error, show_message, show_warning;
  	and improve tests and docstring.
added:
  bzrlib/tests/per_uifactory/    bzrlibtestsper_uifac-20090923023429-a6kukw4evi29ovza-1
  bzrlib/tests/per_uifactory/__init__.py __init__.py-20090923045301-o12zypjwsidxn2hy-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_ui.py        test_ui.py-20051130162854-458e667a7414af09
  bzrlib/ui/__init__.py          ui.py-20050824083933-8cf663c763ba53a9
  bzrlib/ui/text.py              text.py-20051130153916-2e438cffc8afc478
=== modified file 'NEWS'
--- a/NEWS	2009-10-13 05:20:50 +0000
+++ b/NEWS	2009-10-14 08:51:44 +0000
@@ -227,6 +227,10 @@
 * ``ProgressTask.note`` is deprecated.
   (Martin Pool)
 
+* ``UIFactory`` now has new ``show_error``, ``show_message`` and
+  ``show_warning`` methods, which can be hooked by non-text UIs.  
+  (Martin Pool)
+
 Internals
 *********
 

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-10-13 08:46:44 +0000
+++ b/bzrlib/tests/__init__.py	2009-10-14 08:51:44 +0000
@@ -3690,6 +3690,7 @@
         'bzrlib.tests.per_repository',
         'bzrlib.tests.per_repository_chk',
         'bzrlib.tests.per_repository_reference',
+        'bzrlib.tests.per_uifactory',
         'bzrlib.tests.per_versionedfile',
         'bzrlib.tests.per_workingtree',
         'bzrlib.tests.test__annotator',

=== added directory 'bzrlib/tests/per_uifactory'
=== added file 'bzrlib/tests/per_uifactory/__init__.py'
--- a/bzrlib/tests/per_uifactory/__init__.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/per_uifactory/__init__.py	2009-09-23 06:29:46 +0000
@@ -0,0 +1,148 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Tests run per UIFactory."""
+
+# Testing UIFactories is a bit interesting because we require they all support a
+# common interface, but the way they implement it can vary very widely.  Between
+# text, batch-mode, graphical and other potential UIFactories, the requirements
+# to set up a factory, to make it respond to requests, and to simulate user
+# input can vary a lot.
+#
+# We want tests that therefore allow for the evaluation of the result to vary
+# per implementation, but we want to check that the supported facilities are
+# the same across all UIFactorys, unless they're specifically skipped.
+#
+# Our normal approach is to use test scenarios but that seems to just end up
+# creating test-like objects inside the scenario.  Therefore we fall back to
+# the older method of putting the common tests in a mixin.
+#
+# Plugins that add new UIFactorys can create their own subclasses.
+
+
+from cStringIO import StringIO
+import unittest
+
+
+from bzrlib import (
+    tests,
+    ui,
+    )
+
+
+class UIFactoryTestMixin(object):
+    """Common tests for UIFactories.
+
+    These are supposed to be expressed with no assumptions about how the
+    UIFactory implements the method, only that it does implement them (or
+    fails cleanly), and that the concrete subclass will make arrangements to
+    build a factory and to examine its behaviour.
+
+    Note that this is *not* a TestCase, because it can't be directly run, but
+    the concrete subclasses should be.
+    """
+
+    def test_note(self):
+        self.factory.note("a note to the user")
+        self._check_note("a note to the user")
+
+    def test_show_error(self):
+        msg = 'an error occurred'
+        self.factory.show_error(msg)
+        self._check_show_error(msg)
+
+    def test_show_message(self):
+        msg = 'a message'
+        self.factory.show_message(msg)
+        self._check_show_message(msg)
+
+    def test_show_warning(self):
+        msg = 'a warning'
+        self.factory.show_warning(msg)
+        self._check_show_warning(msg)
+
+
+class TestTextUIFactory(tests.TestCase, UIFactoryTestMixin):
+
+    def setUp(self):
+        super(TestTextUIFactory, self).setUp()
+        self.stdin = StringIO()
+        self.stdout = StringIO()
+        self.stderr = StringIO()
+        self.factory = ui.text.TextUIFactory(self.stdin, self.stdout,
+            self.stderr)
+
+    def _check_note(self, note_text):
+        self.assertEquals("%s\n" % note_text,
+            self.stdout.getvalue())
+
+    def _check_show_error(self, msg):
+        self.assertEquals("bzr: error: %s\n" % msg,
+            self.stderr.getvalue())
+        self.assertEquals("", self.stdout.getvalue())
+
+    def _check_show_message(self, msg):
+        self.assertEquals("%s\n" % msg,
+            self.stdout.getvalue())
+        self.assertEquals("", self.stderr.getvalue())
+
+    def _check_show_warning(self, msg):
+        self.assertEquals("bzr: warning: %s\n" % msg,
+            self.stderr.getvalue())
+        self.assertEquals("", self.stdout.getvalue())
+
+
+class TestSilentUIFactory(tests.TestCase, UIFactoryTestMixin):
+    # discards output, therefore tests for output expect nothing
+
+    def setUp(self):
+        super(TestSilentUIFactory, self).setUp()
+        self.factory = ui.SilentUIFactory()
+
+    def _check_note(self, note_text):
+        # it's just discarded
+        pass
+
+    def _check_show_error(self, msg):
+        pass
+
+    def _check_show_message(self, msg):
+        pass
+
+    def _check_show_warning(self, msg):
+        pass
+
+
+class TestCannedInputUIFactory(tests.TestCase, UIFactoryTestMixin):
+    # discards output, reads input from variables
+
+    def setUp(self):
+        super(TestCannedInputUIFactory, self).setUp()
+        self.factory = ui.CannedInputUIFactory([])
+
+    def _check_note(self, note_text):
+        pass
+
+    def _check_show_error(self, msg):
+        pass
+
+    def _check_show_message(self, msg):
+        pass
+
+    def _check_show_warning(self, msg):
+        pass
+
+

=== modified file 'bzrlib/tests/test_ui.py'
--- a/bzrlib/tests/test_ui.py	2009-09-24 08:56:52 +0000
+++ b/bzrlib/tests/test_ui.py	2009-10-14 08:51:44 +0000
@@ -54,7 +54,7 @@
     )
 
 
-class UITests(tests.TestCase):
+class TestTextUIFactory(tests.TestCase):
 
     def test_text_factory_ascii_password(self):
         ui = tests.TestUIFactory(stdin='secret\n',
@@ -100,56 +100,6 @@
         finally:
             pb.finished()
 
-    def test_progress_construction(self):
-        """TextUIFactory constructs the right progress view.
-        """
-        for (file_class, term, pb, expected_pb_class) in (
-            # on an xterm, either use them or not as the user requests,
-            # otherwise default on
-            (_TTYStringIO, 'xterm', 'none', NullProgressView),
-            (_TTYStringIO, 'xterm', 'text', TextProgressView),
-            (_TTYStringIO, 'xterm', None, TextProgressView),
-            # on a dumb terminal, again if there's explicit configuration do
-            # it, otherwise default off
-            (_TTYStringIO, 'dumb', 'none', NullProgressView),
-            (_TTYStringIO, 'dumb', 'text', TextProgressView),
-            (_TTYStringIO, 'dumb', None, NullProgressView),
-            # on a non-tty terminal, it's null regardless of $TERM
-            (StringIO, 'xterm', None, NullProgressView),
-            (StringIO, 'dumb', None, NullProgressView),
-            # however, it can still be forced on
-            (StringIO, 'dumb', 'text', TextProgressView),
-            ):
-            os.environ['TERM'] = term
-            if pb is None:
-                if 'BZR_PROGRESS_BAR' in os.environ:
-                    del os.environ['BZR_PROGRESS_BAR']
-            else:
-                os.environ['BZR_PROGRESS_BAR'] = pb
-            stdin = file_class('')
-            stderr = file_class()
-            stdout = file_class()
-            uif = make_ui_for_terminal(stdin, stdout, stderr)
-            self.assertIsInstance(uif, TextUIFactory,
-                "TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
-            self.assertIsInstance(uif.make_progress_view(),
-                expected_pb_class,
-                "TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
-
-    def test_text_ui_non_terminal(self):
-        """Even on non-ttys, make_ui_for_terminal gives a text ui."""
-        stdin = _NonTTYStringIO('')
-        stderr = _NonTTYStringIO()
-        stdout = _NonTTYStringIO()
-        for term_type in ['dumb', None, 'xterm']:
-            if term_type is None:
-                del os.environ['TERM']
-            else:
-                os.environ['TERM'] = term_type
-            uif = make_ui_for_terminal(stdin, stdout, stderr)
-            self.assertIsInstance(uif, TextUIFactory,
-                'TERM=%r' % (term_type,))
-
     def test_progress_note(self):
         stderr = StringIO()
         stdout = StringIO()
@@ -304,6 +254,59 @@
             pb.finished()
 
 
+class UITests(tests.TestCase):
+
+    def test_progress_construction(self):
+        """TextUIFactory constructs the right progress view.
+        """
+        for (file_class, term, pb, expected_pb_class) in (
+            # on an xterm, either use them or not as the user requests,
+            # otherwise default on
+            (_TTYStringIO, 'xterm', 'none', NullProgressView),
+            (_TTYStringIO, 'xterm', 'text', TextProgressView),
+            (_TTYStringIO, 'xterm', None, TextProgressView),
+            # on a dumb terminal, again if there's explicit configuration do
+            # it, otherwise default off
+            (_TTYStringIO, 'dumb', 'none', NullProgressView),
+            (_TTYStringIO, 'dumb', 'text', TextProgressView),
+            (_TTYStringIO, 'dumb', None, NullProgressView),
+            # on a non-tty terminal, it's null regardless of $TERM
+            (StringIO, 'xterm', None, NullProgressView),
+            (StringIO, 'dumb', None, NullProgressView),
+            # however, it can still be forced on
+            (StringIO, 'dumb', 'text', TextProgressView),
+            ):
+            os.environ['TERM'] = term
+            if pb is None:
+                if 'BZR_PROGRESS_BAR' in os.environ:
+                    del os.environ['BZR_PROGRESS_BAR']
+            else:
+                os.environ['BZR_PROGRESS_BAR'] = pb
+            stdin = file_class('')
+            stderr = file_class()
+            stdout = file_class()
+            uif = make_ui_for_terminal(stdin, stdout, stderr)
+            self.assertIsInstance(uif, TextUIFactory,
+                "TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
+            self.assertIsInstance(uif.make_progress_view(),
+                expected_pb_class,
+                "TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
+
+    def test_text_ui_non_terminal(self):
+        """Even on non-ttys, make_ui_for_terminal gives a text ui."""
+        stdin = _NonTTYStringIO('')
+        stderr = _NonTTYStringIO()
+        stdout = _NonTTYStringIO()
+        for term_type in ['dumb', None, 'xterm']:
+            if term_type is None:
+                del os.environ['TERM']
+            else:
+                os.environ['TERM'] = term_type
+            uif = make_ui_for_terminal(stdin, stdout, stderr)
+            self.assertIsInstance(uif, TextUIFactory,
+                'TERM=%r' % (term_type,))
+
+
 class CLIUITests(TestCase):
 
     def test_cli_factory_deprecated(self):

=== modified file 'bzrlib/ui/__init__.py'
--- a/bzrlib/ui/__init__.py	2009-07-22 07:34:08 +0000
+++ b/bzrlib/ui/__init__.py	2009-09-23 06:29:46 +0000
@@ -22,18 +22,18 @@
 Several levels are supported, and you can also register new factories such as
 for a GUI.
 
-UIFactory
+bzrlib.ui.UIFactory
     Semi-abstract base class
 
-SilentUIFactory
+bzrlib.ui.SilentUIFactory
     Produces no output and cannot take any input; useful for programs using
     bzrlib in batch mode or for programs such as loggerhead.
 
-CannedInputUIFactory
+bzrlib.ui.CannedInputUIFactory
     For use in testing; the input values to be returned are provided 
     at construction.
 
-TextUIFactory
+bzrlib.ui.text.TextUIFactory
     Standard text command-line interface, with stdin, stdout, stderr.
     May make more or less advanced use of them, eg in drawing progress bars,
     depending on the detected capabilities of the terminal.
@@ -208,6 +208,22 @@
         """
         pass
 
+    def show_error(self, msg):
+        """Show an error message (not an exception) to the user.
+        
+        The message should not have an error prefix or trailing newline.  That
+        will be added by the factory if appropriate. 
+        """
+        raise NotImplementedError(self.show_error)
+
+    def show_message(self, msg):
+        """Show a message to the user."""
+        raise NotImplementedError(self.show_message)
+
+    def show_warning(self, msg):
+        """Show a warning to the user."""
+        raise NotImplementedError(self.show_warning)
+
 
 
 class CLIUIFactory(UIFactory):
@@ -318,6 +334,15 @@
     def get_username(self, prompt, **kwargs):
         return None
 
+    def show_error(self, msg):
+        pass
+
+    def show_message(self, msg):
+        pass
+
+    def show_warning(self, msg):
+        pass
+
 
 class CannedInputUIFactory(SilentUIFactory):
     """A silent UI that return canned input."""

=== modified file 'bzrlib/ui/text.py'
--- a/bzrlib/ui/text.py	2009-08-06 02:23:37 +0000
+++ b/bzrlib/ui/text.py	2009-09-23 06:29:46 +0000
@@ -49,9 +49,6 @@
                  stdout=None,
                  stderr=None):
         """Create a TextUIFactory.
-
-        :param bar_type: The type of progress bar to create.  Deprecated
-            and ignored; a TextProgressView is always used.
         """
         super(TextUIFactory, self).__init__()
         # TODO: there's no good reason not to pass all three streams, maybe we
@@ -176,6 +173,17 @@
         self._progress_view.show_transport_activity(transport,
             direction, byte_count)
 
+    def show_error(self, msg):
+        self.clear_term()
+        self.stderr.write("bzr: error: %s\n" % msg)
+
+    def show_message(self, msg):
+        self.note(msg)
+
+    def show_warning(self, msg):
+        self.clear_term()
+        self.stderr.write("bzr: warning: %s\n" % msg)
+
     def _progress_updated(self, task):
         """A task has been updated and wants to be displayed.
         """




More information about the bazaar-commits mailing list