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