Author: Francesco Banconi <francesco.banconi@canonical.com>
Description: Check unicode support
Origin: upstream, http://bazaar.launchpad.net/~juju-gui/juju-quickstart/trunk/revision/67
Bug: https://launchpad.net/bugs/1311321
Bug-Ubuntu: https://launchpad.net/bugs/1311321
Last-Update: 2014-06-27

------------------------------------------------------------
revno: 67 [merge]
committer: Francesco Banconi <francesco.banconi@canonical.com>
branch nick: juju-quickstart
timestamp: Wed 2014-04-30 11:24:49 -0700
message:
  Check unicode support.
  
  Ensure utf-8 is supported before starting
  the Urwid interactive session.
  This way we hope to avoid bad crashes 
  when the user has locale configuration problems.
  
  Tests: `make check`.
  
  QA:
  If you have juju-quickstart installed, you should
  be able to make it crash by overriding all the language
  env vars, like the following:
  `LC_ALL=C juju-quickstart -i`
  You should see a UnicodeEncodeError: 
  'ascii' codec can't encode character u'\u2582' in position 0: 
  ordinal not in range(128)
  
  With this branch, the following should instead work as usual:
  `LC_ALL=C .venv/bin/python juju-quickstart -i`.
  
  I am not sure how to make Urwid fail so that we can QA
  the system exit in the code, but at least that code path 
  is tested.
  
  R=rharding
  CC=
  https://codereview.appspot.com/90740043

=== modified file 'quickstart/cli/base.py'
--- old/quickstart/cli/base.py	2014-01-03 10:22:24 +0000
+++ new/quickstart/cli/base.py	2014-04-24 14:47:18 +0000
@@ -26,6 +26,7 @@
 from __future__ import unicode_literals
 
 from collections import namedtuple
+import sys
 
 import urwid
 
@@ -44,6 +45,24 @@
 )
 
 
+def _check_encoding():
+    """Set the Urwid global byte encoding to utf-8.
+
+    Exit the application if, for some reasons, the change does not have effect.
+    """
+    urwid.set_encoding('utf-8')
+    if not urwid.supports_unicode():
+        # Note: the following message must only include ASCII characters.
+        msg = (
+            'Error: your terminal does not seem to support UTF-8 encoding.\n'
+            'Please check your locale settings.\n'
+            'On Ubuntu, running the following might fix the problem:\n'
+            '  sudo locale-gen en_US.UTF-8\n'
+            '  sudo dpkg-reconfigure locales'
+        )
+        sys.exit(msg.encode('ascii'))
+
+
 class _MainLoop(urwid.MainLoop):
     """A customized Urwid loop.
 
@@ -109,6 +128,7 @@
           with the exit shortcut. See the quickstart.cli.views module docstring
           for more information about this functionality.
     """
+    _check_encoding()
     # Set up the application header.
     title = urwid.Text('\npreparing...')
     header_line = urwid.Divider('\N{LOWER ONE QUARTER BLOCK}')

=== modified file 'quickstart/tests/cli/test_base.py'
--- old/quickstart/tests/cli/test_base.py	2014-01-03 10:22:24 +0000
+++ new/quickstart/tests/cli/test_base.py	2014-04-24 14:49:16 +0000
@@ -18,14 +18,49 @@
 
 from __future__ import unicode_literals
 
+from contextlib import contextmanager
 import unittest
 
+import mock
 import urwid
 
 from quickstart.cli import base
 from quickstart.tests.cli import helpers as cli_helpers
 
 
+class TestCheckEncoding(unittest.TestCase):
+
+    @contextmanager
+    def patch_urwid(self, supports_unicode=True):
+        """Patch the urwid.set_encoding and urwid.supports_unicode calls.
+
+        Ensure the mock functions are called once with the expected arguments.
+
+        Make urwid.supports_unicode return the given supports_unicode value.
+        """
+        mock_supports_unicode = mock.Mock(return_value=supports_unicode)
+        with mock.patch('urwid.set_encoding') as mock_set_encoding:
+            with mock.patch('urwid.supports_unicode', mock_supports_unicode):
+                yield
+        mock_set_encoding.assert_called_once_with('utf-8')
+        mock_supports_unicode.assert_called_once_with()
+
+    def test_unicode_supported(self):
+        # The utf-8 encoding is properly set and the function exits without
+        # errors.
+        with self.patch_urwid(supports_unicode=True):
+            base._check_encoding()
+
+    def test_unicode_not_supported(self):
+        # If unicode is not supported, the program exits with an error.
+        with self.patch_urwid(supports_unicode=False):
+            with self.assertRaises(SystemExit) as context_manager:
+                base._check_encoding()
+        self.assertIn(
+            b'Error: your terminal does not seem to support UTF-8 encoding.',
+            bytes(context_manager.exception))
+
+
 class TestMainLoop(unittest.TestCase):
 
     def setUp(self):

