summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hellmann <doug.hellmann@dreamhost.com>2014-05-07 12:31:43 -0700
committerDoug Hellmann <doug.hellmann@dreamhost.com>2014-06-04 09:50:34 -0700
commitd601e1c55ba44e4eaacdf7277fd4cbff1cbd755e (patch)
treea73151344b557fda8bce55481db2975c580600fa
parent73d16d8134a841a394265b037837d706e661d666 (diff)
downloadoslo-i18n-d601e1c55ba44e4eaacdf7277fd4cbff1cbd755e.tar.gz
Split up monolithic test file
Split the tests up into multiple files for easier maintenance. Update the import order of modules used by the tests. bp graduate-oslo-i18n Change-Id: If225bfa0a4d319ebc530c6497f74a20d0f25835b
-rw-r--r--tests/test_factory.py60
-rw-r--r--tests/test_gettextutils.py668
-rw-r--r--tests/test_handler.py107
-rw-r--r--tests/test_logging.py42
-rw-r--r--tests/test_message.py522
-rw-r--r--tests/utils.py42
6 files changed, 779 insertions, 662 deletions
diff --git a/tests/test_factory.py b/tests/test_factory.py
new file mode 100644
index 0000000..2cf031a
--- /dev/null
+++ b/tests/test_factory.py
@@ -0,0 +1,60 @@
+# Copyright 2012 Red Hat, Inc.
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+import six
+
+from oslotest import base as test_base
+
+from oslo.i18n import gettextutils
+
+
+class TranslatorFactoryTest(test_base.BaseTestCase):
+
+ def test_lazy(self):
+ with mock.patch.object(gettextutils, 'Message') as msg:
+ tf = gettextutils.TranslatorFactory('domain', lazy=True)
+ tf.primary('some text')
+ msg.assert_called_with('some text', domain='domain')
+
+ def test_py2(self):
+ with mock.patch.object(six, 'PY3', False):
+ with mock.patch('gettext.translation') as translation:
+ trans = mock.Mock()
+ translation.return_value = trans
+ trans.gettext.side_effect = AssertionError(
+ 'should have called ugettext')
+ tf = gettextutils.TranslatorFactory('domain', lazy=False)
+ tf.primary('some text')
+ trans.ugettext.assert_called_with('some text')
+
+ def test_py3(self):
+ with mock.patch.object(six, 'PY3', True):
+ with mock.patch('gettext.translation') as translation:
+ trans = mock.Mock()
+ translation.return_value = trans
+ trans.ugettext.side_effect = AssertionError(
+ 'should have called gettext')
+ tf = gettextutils.TranslatorFactory('domain', lazy=False)
+ tf.primary('some text')
+ trans.gettext.assert_called_with('some text')
+
+ def test_log_level_domain_name(self):
+ with mock.patch.object(gettextutils.TranslatorFactory,
+ '_make_translation_func') as mtf:
+ tf = gettextutils.TranslatorFactory('domain', lazy=False)
+ tf._make_log_translation_func('mylevel')
+ mtf.assert_called_with('domain-log-mylevel')
diff --git a/tests/test_gettextutils.py b/tests/test_gettextutils.py
index db26fc8..210dd94 100644
--- a/tests/test_gettextutils.py
+++ b/tests/test_gettextutils.py
@@ -17,16 +17,18 @@
import gettext
import logging
-from oslo.i18n import gettextutils
-
from babel import localedata
import mock
import six
-import testtools
from oslotest import base as test_base
from oslotest import moxstubout
+
from tests import fakes
+from tests import utils
+
+from oslo.i18n import gettextutils
+
LOG = logging.getLogger(__name__)
@@ -141,664 +143,6 @@ class GettextTest(test_base.BaseTestCase):
mock_translation.side_effect = translator
# translate() works on msgs and on objects whose unicode reps are msgs
- obj = SomeObject(message)
+ obj = utils.SomeObject(message)
self.assertEqual(es_translation, gettextutils.translate(message, 'es'))
self.assertEqual(es_translation, gettextutils.translate(obj, 'es'))
-
-
-class MessageTestCase(test_base.BaseTestCase):
- """Unit tests for locale Message class."""
-
- @staticmethod
- def message(msg):
- return gettextutils.Message(msg)
-
- def test_message_id_and_message_text(self):
- message = gettextutils.Message('1')
- self.assertEqual('1', message.msgid)
- self.assertEqual('1', message)
- message = gettextutils.Message('1', msgtext='A')
- self.assertEqual('1', message.msgid)
- self.assertEqual('A', message)
-
- def test_message_is_unicode(self):
- message = self.message('some %s') % 'message'
- self.assertIsInstance(message, six.text_type)
-
- @mock.patch('locale.getdefaultlocale')
- @mock.patch('gettext.translation')
- def test_create_message_non_english_default_locale(self,
- mock_translation,
- mock_getdefaultlocale):
- msgid = 'A message in English'
- es_translation = 'A message in Spanish'
-
- es_translations = {msgid: es_translation}
- translations_map = {'es': es_translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
- mock_getdefaultlocale.return_value = ('es',)
-
- message = gettextutils.Message(msgid)
-
- # The base representation of the message is in Spanish, as well as
- # the default translation, since the default locale was Spanish.
- self.assertEqual(es_translation, message)
- self.assertEqual(es_translation, message.translate())
-
- def test_translate_returns_unicode(self):
- message = self.message('some %s') % 'message'
- self.assertIsInstance(message.translate(), six.text_type)
-
- def test_mod_with_named_parameters(self):
- msgid = ("%(description)s\nCommand: %(cmd)s\n"
- "Exit code: %(exit_code)s\nStdout: %(stdout)r\n"
- "Stderr: %(stderr)r %%(something)s")
- params = {'description': 'test1',
- 'cmd': 'test2',
- 'exit_code': 'test3',
- 'stdout': 'test4',
- 'stderr': 'test5',
- 'something': 'trimmed'}
-
- result = self.message(msgid) % params
-
- expected = msgid % params
- self.assertEqual(result, expected)
- self.assertEqual(result.translate(), expected)
-
- def test_multiple_mod_with_named_parameter(self):
- msgid = ("%(description)s\nCommand: %(cmd)s\n"
- "Exit code: %(exit_code)s\nStdout: %(stdout)r\n"
- "Stderr: %(stderr)r")
- params = {'description': 'test1',
- 'cmd': 'test2',
- 'exit_code': 'test3',
- 'stdout': 'test4',
- 'stderr': 'test5'}
-
- # Run string interpolation the first time to make a new Message
- first = self.message(msgid) % params
-
- # Run string interpolation on the new Message, to replicate
- # one of the error paths with some Exception classes we've
- # implemented in OpenStack. We should receive a second Message
- # object, but the translation results should be the same.
- #
- # The production code that triggers this problem does something
- # like:
- #
- # msg = _('there was a problem %(name)s') % {'name': 'some value'}
- # LOG.error(msg)
- # raise BadExceptionClass(msg)
- #
- # where BadExceptionClass does something like:
- #
- # class BadExceptionClass(Exception):
- # def __init__(self, msg, **kwds):
- # super(BadExceptionClass, self).__init__(msg % kwds)
- #
- expected = first % {}
-
- # Base message id should be the same
- self.assertEqual(first.msgid, expected.msgid)
-
- # Preserved arguments should be the same
- self.assertEqual(first.params, expected.params)
-
- # Should have different objects
- self.assertIsNot(expected, first)
-
- # Final translations should be the same
- self.assertEqual(expected.translate(), first.translate())
-
- def test_mod_with_named_parameters_no_space(self):
- msgid = ("Request: %(method)s http://%(server)s:"
- "%(port)s%(url)s with headers %(headers)s")
- params = {'method': 'POST',
- 'server': 'test1',
- 'port': 1234,
- 'url': 'test2',
- 'headers': {'h1': 'val1'}}
-
- result = self.message(msgid) % params
-
- expected = msgid % params
- self.assertEqual(result, expected)
- self.assertEqual(result.translate(), expected)
-
- def test_mod_with_dict_parameter(self):
- msgid = "Test that we can inject a dictionary %s"
- params = {'description': 'test1'}
-
- result = self.message(msgid) % params
-
- expected = msgid % params
- self.assertEqual(result, expected)
- self.assertEqual(result.translate(), expected)
-
- def test_mod_with_integer_parameters(self):
- msgid = "Some string with params: %d"
- params = [0, 1, 10, 24124]
-
- messages = []
- results = []
- for param in params:
- messages.append(msgid % param)
- results.append(self.message(msgid) % param)
-
- for message, result in zip(messages, results):
- self.assertEqual(type(result), gettextutils.Message)
- self.assertEqual(result.translate(), message)
-
- # simulate writing out as string
- result_str = '%s' % result.translate()
- self.assertEqual(result_str, message)
- self.assertEqual(result, message)
-
- def test_mod_copies_parameters(self):
- msgid = "Found object: %(current_value)s"
- changing_dict = {'current_value': 1}
- # A message created with some params
- result = self.message(msgid) % changing_dict
- # The parameters may change
- changing_dict['current_value'] = 2
- # Even if the param changes when the message is
- # translated it should use the original param
- self.assertEqual(result.translate(), 'Found object: 1')
-
- def test_mod_deep_copies_parameters(self):
- msgid = "Found list: %(current_list)s"
- changing_list = list([1, 2, 3])
- params = {'current_list': changing_list}
- # Apply the params
- result = self.message(msgid) % params
- # Change the list
- changing_list.append(4)
- # Even though the list changed the message
- # translation should use the original list
- self.assertEqual(result.translate(), "Found list: [1, 2, 3]")
-
- def test_mod_deep_copies_param_nodeep_param(self):
- msgid = "Value: %s"
- params = NoDeepCopyObject(5)
- # Apply the params
- result = self.message(msgid) % params
- self.assertEqual(result.translate(), "Value: 5")
-
- def test_mod_deep_copies_param_nodeep_dict(self):
- msgid = "Values: %(val1)s %(val2)s"
- params = {'val1': 1, 'val2': NoDeepCopyObject(2)}
- # Apply the params
- result = self.message(msgid) % params
- self.assertEqual(result.translate(), "Values: 1 2")
-
- # Apply again to make sure other path works as well
- params = {'val1': 3, 'val2': NoDeepCopyObject(4)}
- result = self.message(msgid) % params
- self.assertEqual(result.translate(), "Values: 3 4")
-
- def test_mod_returns_a_copy(self):
- msgid = "Some msgid string: %(test1)s %(test2)s"
- message = self.message(msgid)
- m1 = message % {'test1': 'foo', 'test2': 'bar'}
- m2 = message % {'test1': 'foo2', 'test2': 'bar2'}
-
- self.assertIsNot(message, m1)
- self.assertIsNot(message, m2)
- self.assertEqual(m1.translate(),
- msgid % {'test1': 'foo', 'test2': 'bar'})
- self.assertEqual(m2.translate(),
- msgid % {'test1': 'foo2', 'test2': 'bar2'})
-
- def test_mod_with_none_parameter(self):
- msgid = "Some string with params: %s"
- message = self.message(msgid) % None
- self.assertEqual(msgid % None, message)
- self.assertEqual(msgid % None, message.translate())
-
- def test_mod_with_missing_parameters(self):
- msgid = "Some string with params: %s %s"
- test_me = lambda: self.message(msgid) % 'just one'
- # Just like with strings missing parameters raise TypeError
- self.assertRaises(TypeError, test_me)
-
- def test_mod_with_extra_parameters(self):
- msgid = "Some string with params: %(param1)s %(param2)s"
- params = {'param1': 'test',
- 'param2': 'test2',
- 'param3': 'notinstring'}
-
- result = self.message(msgid) % params
-
- expected = msgid % params
- self.assertEqual(result, expected)
- self.assertEqual(result.translate(), expected)
-
- # Make sure unused params still there
- self.assertEqual(result.params.keys(), params.keys())
-
- def test_mod_with_missing_named_parameters(self):
- msgid = ("Some string with params: %(param1)s %(param2)s"
- " and a missing one %(missing)s")
- params = {'param1': 'test',
- 'param2': 'test2'}
-
- test_me = lambda: self.message(msgid) % params
- # Just like with strings missing named parameters raise KeyError
- self.assertRaises(KeyError, test_me)
-
- def test_add_disabled(self):
- msgid = "A message"
- test_me = lambda: self.message(msgid) + ' some string'
- self.assertRaises(TypeError, test_me)
-
- def test_radd_disabled(self):
- msgid = "A message"
- test_me = lambda: SomeObject('test') + self.message(msgid)
- self.assertRaises(TypeError, test_me)
-
- @testtools.skipIf(six.PY3, 'test specific to Python 2')
- def test_str_disabled(self):
- msgid = "A message"
- test_me = lambda: str(self.message(msgid))
- self.assertRaises(UnicodeError, test_me)
-
- @mock.patch('gettext.translation')
- def test_translate(self, mock_translation):
- en_message = 'A message in the default locale'
- es_translation = 'A message in Spanish'
- message = gettextutils.Message(en_message)
-
- es_translations = {en_message: es_translation}
- translations_map = {'es': es_translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- self.assertEqual(es_translation, message.translate('es'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_from_unicoded_object(self, mock_translation):
- en_message = 'A message in the default locale'
- es_translation = 'A message in Spanish'
- message = gettextutils.Message(en_message)
- es_translations = {en_message: es_translation}
- translations_map = {'es': es_translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- # Here we are not testing the Message object directly but the result
- # of unicoding() an object whose unicode representation is a Message
- obj = SomeObject(message)
- unicoded_obj = six.text_type(obj)
-
- self.assertEqual(es_translation, unicoded_obj.translate('es'))
-
- @mock.patch('gettext.translation')
- def test_translate_multiple_languages(self, mock_translation):
- en_message = 'A message in the default locale'
- es_translation = 'A message in Spanish'
- zh_translation = 'A message in Chinese'
- message = gettextutils.Message(en_message)
-
- es_translations = {en_message: es_translation}
- zh_translations = {en_message: zh_translation}
- translations_map = {'es': es_translations,
- 'zh': zh_translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- self.assertEqual(es_translation, message.translate('es'))
- self.assertEqual(zh_translation, message.translate('zh'))
- self.assertEqual(en_message, message.translate(None))
- self.assertEqual(en_message, message.translate('en'))
- self.assertEqual(en_message, message.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_param(self, mock_translation):
- message_with_params = 'A message: %s'
- es_translation = 'A message in Spanish: %s'
- param = 'A Message param'
-
- translations = {message_with_params: es_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- msg = msg % param
-
- default_translation = message_with_params % param
- expected_translation = es_translation % param
- self.assertEqual(expected_translation, msg.translate('es'))
- self.assertEqual(default_translation, msg.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_object_param(self, mock_translation):
- message_with_params = 'A message: %s'
- es_translation = 'A message in Spanish: %s'
- param = 'A Message param'
- param_translation = 'A Message param in Spanish'
-
- translations = {message_with_params: es_translation,
- param: param_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- param_msg = gettextutils.Message(param)
-
- # Here we are testing translation of a Message with another object
- # that can be translated via its unicode() representation, this is
- # very common for instance when modding an Exception with a Message
- obj = SomeObject(param_msg)
- msg = msg % obj
-
- default_translation = message_with_params % param
- expected_translation = es_translation % param_translation
-
- self.assertEqual(expected_translation, msg.translate('es'))
- self.assertEqual(default_translation, msg.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_param_from_unicoded_obj(self,
- mock_translation):
- message_with_params = 'A message: %s'
- es_translation = 'A message in Spanish: %s'
- param = 'A Message param'
-
- translations = {message_with_params: es_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- msg = msg % param
-
- default_translation = message_with_params % param
- expected_translation = es_translation % param
-
- obj = SomeObject(msg)
- unicoded_obj = six.text_type(obj)
-
- self.assertEqual(expected_translation, unicoded_obj.translate('es'))
- self.assertEqual(default_translation, unicoded_obj.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_message_parameter(self, mock_translation):
- message_with_params = 'A message with param: %s'
- es_translation = 'A message with param in Spanish: %s'
- message_param = 'A message param'
- es_param_translation = 'A message param in Spanish'
-
- translations = {message_with_params: es_translation,
- message_param: es_param_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- msg_param = gettextutils.Message(message_param)
- msg = msg % msg_param
-
- default_translation = message_with_params % message_param
- expected_translation = es_translation % es_param_translation
- self.assertEqual(expected_translation, msg.translate('es'))
- self.assertEqual(default_translation, msg.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_message_parameters(self, mock_translation):
- message_with_params = 'A message with params: %s %s'
- es_translation = 'A message with params in Spanish: %s %s'
- message_param = 'A message param'
- es_param_translation = 'A message param in Spanish'
- another_message_param = 'Another message param'
- another_es_param_translation = 'Another message param in Spanish'
-
- translations = {message_with_params: es_translation,
- message_param: es_param_translation,
- another_message_param: another_es_param_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- param_1 = gettextutils.Message(message_param)
- param_2 = gettextutils.Message(another_message_param)
- msg = msg % (param_1, param_2)
-
- default_translation = message_with_params % (message_param,
- another_message_param)
- expected_translation = es_translation % (es_param_translation,
- another_es_param_translation)
- self.assertEqual(expected_translation, msg.translate('es'))
- self.assertEqual(default_translation, msg.translate('XX'))
-
- @mock.patch('gettext.translation')
- def test_translate_message_with_named_parameters(self, mock_translation):
- message_with_params = 'A message with params: %(param)s'
- es_translation = 'A message with params in Spanish: %(param)s'
- message_param = 'A Message param'
- es_param_translation = 'A message param in Spanish'
-
- translations = {message_with_params: es_translation,
- message_param: es_param_translation}
- translator = fakes.FakeTranslations.translator({'es': translations})
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(message_with_params)
- msg_param = gettextutils.Message(message_param)
- msg = msg % {'param': msg_param}
-
- default_translation = message_with_params % {'param': message_param}
- expected_translation = es_translation % {'param': es_param_translation}
- self.assertEqual(expected_translation, msg.translate('es'))
- self.assertEqual(default_translation, msg.translate('XX'))
-
- @mock.patch('locale.getdefaultlocale')
- @mock.patch('gettext.translation')
- def test_translate_message_non_default_locale(self,
- mock_translation,
- mock_getdefaultlocale):
- message_with_params = 'A message with params: %(param)s'
- es_translation = 'A message with params in Spanish: %(param)s'
- zh_translation = 'A message with params in Chinese: %(param)s'
- fr_translation = 'A message with params in French: %(param)s'
-
- message_param = 'A Message param'
- es_param_translation = 'A message param in Spanish'
- zh_param_translation = 'A message param in Chinese'
- fr_param_translation = 'A message param in French'
-
- es_translations = {message_with_params: es_translation,
- message_param: es_param_translation}
- zh_translations = {message_with_params: zh_translation,
- message_param: zh_param_translation}
- fr_translations = {message_with_params: fr_translation,
- message_param: fr_param_translation}
-
- translator = fakes.FakeTranslations.translator({'es': es_translations,
- 'zh': zh_translations,
- 'fr': fr_translations})
- mock_translation.side_effect = translator
- mock_getdefaultlocale.return_value = ('es',)
-
- msg = gettextutils.Message(message_with_params)
- msg_param = gettextutils.Message(message_param)
- msg = msg % {'param': msg_param}
-
- es_translation = es_translation % {'param': es_param_translation}
- zh_translation = zh_translation % {'param': zh_param_translation}
- fr_translation = fr_translation % {'param': fr_param_translation}
-
- # Because sys.getdefaultlocale() was Spanish,
- # the default translation will be to Spanish
- self.assertEqual(es_translation, msg)
- self.assertEqual(es_translation, msg.translate())
- self.assertEqual(es_translation, msg.translate('es'))
-
- # Translation into other locales still works
- self.assertEqual(zh_translation, msg.translate('zh'))
- self.assertEqual(fr_translation, msg.translate('fr'))
-
-
-class TranslationHandlerTestCase(test_base.BaseTestCase):
-
- def setUp(self):
- super(TranslationHandlerTestCase, self).setUp()
-
- self.stream = six.StringIO()
- self.destination_handler = logging.StreamHandler(self.stream)
- self.translation_handler = gettextutils.TranslationHandler('zh_CN')
- self.translation_handler.setTarget(self.destination_handler)
-
- self.logger = logging.getLogger('localehander_logger')
- self.logger.setLevel(logging.DEBUG)
- self.logger.addHandler(self.translation_handler)
-
- def test_set_formatter(self):
- formatter = 'some formatter'
- self.translation_handler.setFormatter(formatter)
- self.assertEqual(formatter, self.translation_handler.target.formatter)
-
- @mock.patch('gettext.translation')
- def test_emit_translated_message(self, mock_translation):
- log_message = 'A message to be logged'
- log_message_translation = 'A message to be logged in Chinese'
- translations = {log_message: log_message_translation}
- translations_map = {'zh_CN': translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(log_message)
-
- self.logger.info(msg)
- self.assertIn(log_message_translation, self.stream.getvalue())
-
- @mock.patch('gettext.translation')
- def test_emit_translated_message_with_args(self, mock_translation):
- log_message = 'A message to be logged %s'
- log_message_translation = 'A message to be logged in Chinese %s'
- log_arg = 'Arg to be logged'
- log_arg_translation = 'An arg to be logged in Chinese'
-
- translations = {log_message: log_message_translation,
- log_arg: log_arg_translation}
- translations_map = {'zh_CN': translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(log_message)
- arg = gettextutils.Message(log_arg)
-
- self.logger.info(msg, arg)
- self.assertIn(log_message_translation % log_arg_translation,
- self.stream.getvalue())
-
- @mock.patch('gettext.translation')
- def test_emit_translated_message_with_named_args(self, mock_translation):
- log_message = 'A message to be logged %(arg1)s $(arg2)s'
- log_message_translation = 'Chinese msg to be logged %(arg1)s $(arg2)s'
- log_arg_1 = 'Arg1 to be logged'
- log_arg_1_translation = 'Arg1 to be logged in Chinese'
- log_arg_2 = 'Arg2 to be logged'
- log_arg_2_translation = 'Arg2 to be logged in Chinese'
-
- translations = {log_message: log_message_translation,
- log_arg_1: log_arg_1_translation,
- log_arg_2: log_arg_2_translation}
- translations_map = {'zh_CN': translations}
- translator = fakes.FakeTranslations.translator(translations_map)
- mock_translation.side_effect = translator
-
- msg = gettextutils.Message(log_message)
- arg_1 = gettextutils.Message(log_arg_1)
- arg_2 = gettextutils.Message(log_arg_2)
-
- self.logger.info(msg, {'arg1': arg_1, 'arg2': arg_2})
- translation = log_message_translation % {'arg1': log_arg_1_translation,
- 'arg2': log_arg_2_translation}
- self.assertIn(translation, self.stream.getvalue())
-
-
-class TranslatorFactoryTest(test_base.BaseTestCase):
-
- def test_lazy(self):
- with mock.patch.object(gettextutils, 'Message') as msg:
- tf = gettextutils.TranslatorFactory('domain', lazy=True)
- tf.primary('some text')
- msg.assert_called_with('some text', domain='domain')
-
- def test_py2(self):
- with mock.patch.object(six, 'PY3', False):
- with mock.patch('gettext.translation') as translation:
- trans = mock.Mock()
- translation.return_value = trans
- trans.gettext.side_effect = AssertionError(
- 'should have called ugettext')
- tf = gettextutils.TranslatorFactory('domain', lazy=False)
- tf.primary('some text')
- trans.ugettext.assert_called_with('some text')
-
- def test_py3(self):
- with mock.patch.object(six, 'PY3', True):
- with mock.patch('gettext.translation') as translation:
- trans = mock.Mock()
- translation.return_value = trans
- trans.ugettext.side_effect = AssertionError(
- 'should have called gettext')
- tf = gettextutils.TranslatorFactory('domain', lazy=False)
- tf.primary('some text')
- trans.gettext.assert_called_with('some text')
-
- def test_log_level_domain_name(self):
- with mock.patch.object(gettextutils.TranslatorFactory,
- '_make_translation_func') as mtf:
- tf = gettextutils.TranslatorFactory('domain', lazy=False)
- tf._make_log_translation_func('mylevel')
- mtf.assert_called_with('domain-log-mylevel')
-
-
-class LogLevelTranslationsTest(test_base.BaseTestCase):
-
- def test_info(self):
- self._test('info')
-
- def test_warning(self):
- self._test('warning')
-
- def test_error(self):
- self._test('error')
-
- def test_critical(self):
- self._test('critical')
-
- def _test(self, level):
- with mock.patch.object(gettextutils.TranslatorFactory,
- '_make_translation_func') as mtf:
- tf = gettextutils.TranslatorFactory('domain', lazy=False)
- getattr(tf, 'log_%s' % level)
- mtf.assert_called_with('domain-log-%s' % level)
-
-
-class SomeObject(object):
-
- def __init__(self, message):
- self.message = message
-
- def __unicode__(self):
- return self.message
- # alias for Python 3
- __str__ = __unicode__
-
-
-class NoDeepCopyObject(object):
-
- def __init__(self, value):
- self.value = value
-
- if six.PY3:
- def __str__(self):
- return str(self.value)
- else:
- def __unicode__(self):
- return unicode(self.value)
-
- def __deepcopy__(self, memo):
- raise TypeError('Deep Copy not supported')
diff --git a/tests/test_handler.py b/tests/test_handler.py
new file mode 100644
index 0000000..6b634da
--- /dev/null
+++ b/tests/test_handler.py
@@ -0,0 +1,107 @@
+# Copyright 2012 Red Hat, Inc.
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import logging
+
+import mock
+import six
+
+from oslotest import base as test_base
+
+from tests import fakes
+
+from oslo.i18n import gettextutils
+
+LOG = logging.getLogger(__name__)
+
+
+class TranslationHandlerTestCase(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(TranslationHandlerTestCase, self).setUp()
+
+ self.stream = six.StringIO()
+ self.destination_handler = logging.StreamHandler(self.stream)
+ self.translation_handler = gettextutils.TranslationHandler('zh_CN')
+ self.translation_handler.setTarget(self.destination_handler)
+
+ self.logger = logging.getLogger('localehander_logger')
+ self.logger.setLevel(logging.DEBUG)
+ self.logger.addHandler(self.translation_handler)
+
+ def test_set_formatter(self):
+ formatter = 'some formatter'
+ self.translation_handler.setFormatter(formatter)
+ self.assertEqual(formatter, self.translation_handler.target.formatter)
+
+ @mock.patch('gettext.translation')
+ def test_emit_translated_message(self, mock_translation):
+ log_message = 'A message to be logged'
+ log_message_translation = 'A message to be logged in Chinese'
+ translations = {log_message: log_message_translation}
+ translations_map = {'zh_CN': translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(log_message)
+
+ self.logger.info(msg)
+ self.assertIn(log_message_translation, self.stream.getvalue())
+
+ @mock.patch('gettext.translation')
+ def test_emit_translated_message_with_args(self, mock_translation):
+ log_message = 'A message to be logged %s'
+ log_message_translation = 'A message to be logged in Chinese %s'
+ log_arg = 'Arg to be logged'
+ log_arg_translation = 'An arg to be logged in Chinese'
+
+ translations = {log_message: log_message_translation,
+ log_arg: log_arg_translation}
+ translations_map = {'zh_CN': translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(log_message)
+ arg = gettextutils.Message(log_arg)
+
+ self.logger.info(msg, arg)
+ self.assertIn(log_message_translation % log_arg_translation,
+ self.stream.getvalue())
+
+ @mock.patch('gettext.translation')
+ def test_emit_translated_message_with_named_args(self, mock_translation):
+ log_message = 'A message to be logged %(arg1)s $(arg2)s'
+ log_message_translation = 'Chinese msg to be logged %(arg1)s $(arg2)s'
+ log_arg_1 = 'Arg1 to be logged'
+ log_arg_1_translation = 'Arg1 to be logged in Chinese'
+ log_arg_2 = 'Arg2 to be logged'
+ log_arg_2_translation = 'Arg2 to be logged in Chinese'
+
+ translations = {log_message: log_message_translation,
+ log_arg_1: log_arg_1_translation,
+ log_arg_2: log_arg_2_translation}
+ translations_map = {'zh_CN': translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(log_message)
+ arg_1 = gettextutils.Message(log_arg_1)
+ arg_2 = gettextutils.Message(log_arg_2)
+
+ self.logger.info(msg, {'arg1': arg_1, 'arg2': arg_2})
+ translation = log_message_translation % {'arg1': log_arg_1_translation,
+ 'arg2': log_arg_2_translation}
+ self.assertIn(translation, self.stream.getvalue())
diff --git a/tests/test_logging.py b/tests/test_logging.py
new file mode 100644
index 0000000..b4273d6
--- /dev/null
+++ b/tests/test_logging.py
@@ -0,0 +1,42 @@
+# Copyright 2012 Red Hat, Inc.
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+from oslotest import base as test_base
+
+from oslo.i18n import gettextutils
+
+
+class LogLevelTranslationsTest(test_base.BaseTestCase):
+
+ def test_info(self):
+ self._test('info')
+
+ def test_warning(self):
+ self._test('warning')
+
+ def test_error(self):
+ self._test('error')
+
+ def test_critical(self):
+ self._test('critical')
+
+ def _test(self, level):
+ with mock.patch.object(gettextutils.TranslatorFactory,
+ '_make_translation_func') as mtf:
+ tf = gettextutils.TranslatorFactory('domain', lazy=False)
+ getattr(tf, 'log_%s' % level)
+ mtf.assert_called_with('domain-log-%s' % level)
diff --git a/tests/test_message.py b/tests/test_message.py
new file mode 100644
index 0000000..f324472
--- /dev/null
+++ b/tests/test_message.py
@@ -0,0 +1,522 @@
+# Copyright 2012 Red Hat, Inc.
+# Copyright 2013 IBM Corp.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import logging
+
+import mock
+import six
+import testtools
+
+from oslotest import base as test_base
+
+from tests import fakes
+from tests import utils
+
+from oslo.i18n import gettextutils
+
+LOG = logging.getLogger(__name__)
+
+
+class MessageTestCase(test_base.BaseTestCase):
+ """Unit tests for locale Message class."""
+
+ @staticmethod
+ def message(msg):
+ return gettextutils.Message(msg)
+
+ def test_message_id_and_message_text(self):
+ message = gettextutils.Message('1')
+ self.assertEqual('1', message.msgid)
+ self.assertEqual('1', message)
+ message = gettextutils.Message('1', msgtext='A')
+ self.assertEqual('1', message.msgid)
+ self.assertEqual('A', message)
+
+ def test_message_is_unicode(self):
+ message = self.message('some %s') % 'message'
+ self.assertIsInstance(message, six.text_type)
+
+ @mock.patch('locale.getdefaultlocale')
+ @mock.patch('gettext.translation')
+ def test_create_message_non_english_default_locale(self,
+ mock_translation,
+ mock_getdefaultlocale):
+ msgid = 'A message in English'
+ es_translation = 'A message in Spanish'
+
+ es_translations = {msgid: es_translation}
+ translations_map = {'es': es_translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+ mock_getdefaultlocale.return_value = ('es',)
+
+ message = gettextutils.Message(msgid)
+
+ # The base representation of the message is in Spanish, as well as
+ # the default translation, since the default locale was Spanish.
+ self.assertEqual(es_translation, message)
+ self.assertEqual(es_translation, message.translate())
+
+ def test_translate_returns_unicode(self):
+ message = self.message('some %s') % 'message'
+ self.assertIsInstance(message.translate(), six.text_type)
+
+ def test_mod_with_named_parameters(self):
+ msgid = ("%(description)s\nCommand: %(cmd)s\n"
+ "Exit code: %(exit_code)s\nStdout: %(stdout)r\n"
+ "Stderr: %(stderr)r %%(something)s")
+ params = {'description': 'test1',
+ 'cmd': 'test2',
+ 'exit_code': 'test3',
+ 'stdout': 'test4',
+ 'stderr': 'test5',
+ 'something': 'trimmed'}
+
+ result = self.message(msgid) % params
+
+ expected = msgid % params
+ self.assertEqual(result, expected)
+ self.assertEqual(result.translate(), expected)
+
+ def test_multiple_mod_with_named_parameter(self):
+ msgid = ("%(description)s\nCommand: %(cmd)s\n"
+ "Exit code: %(exit_code)s\nStdout: %(stdout)r\n"
+ "Stderr: %(stderr)r")
+ params = {'description': 'test1',
+ 'cmd': 'test2',
+ 'exit_code': 'test3',
+ 'stdout': 'test4',
+ 'stderr': 'test5'}
+
+ # Run string interpolation the first time to make a new Message
+ first = self.message(msgid) % params
+
+ # Run string interpolation on the new Message, to replicate
+ # one of the error paths with some Exception classes we've
+ # implemented in OpenStack. We should receive a second Message
+ # object, but the translation results should be the same.
+ #
+ # The production code that triggers this problem does something
+ # like:
+ #
+ # msg = _('there was a problem %(name)s') % {'name': 'some value'}
+ # LOG.error(msg)
+ # raise BadExceptionClass(msg)
+ #
+ # where BadExceptionClass does something like:
+ #
+ # class BadExceptionClass(Exception):
+ # def __init__(self, msg, **kwds):
+ # super(BadExceptionClass, self).__init__(msg % kwds)
+ #
+ expected = first % {}
+
+ # Base message id should be the same
+ self.assertEqual(first.msgid, expected.msgid)
+
+ # Preserved arguments should be the same
+ self.assertEqual(first.params, expected.params)
+
+ # Should have different objects
+ self.assertIsNot(expected, first)
+
+ # Final translations should be the same
+ self.assertEqual(expected.translate(), first.translate())
+
+ def test_mod_with_named_parameters_no_space(self):
+ msgid = ("Request: %(method)s http://%(server)s:"
+ "%(port)s%(url)s with headers %(headers)s")
+ params = {'method': 'POST',
+ 'server': 'test1',
+ 'port': 1234,
+ 'url': 'test2',
+ 'headers': {'h1': 'val1'}}
+
+ result = self.message(msgid) % params
+
+ expected = msgid % params
+ self.assertEqual(result, expected)
+ self.assertEqual(result.translate(), expected)
+
+ def test_mod_with_dict_parameter(self):
+ msgid = "Test that we can inject a dictionary %s"
+ params = {'description': 'test1'}
+
+ result = self.message(msgid) % params
+
+ expected = msgid % params
+ self.assertEqual(result, expected)
+ self.assertEqual(result.translate(), expected)
+
+ def test_mod_with_integer_parameters(self):
+ msgid = "Some string with params: %d"
+ params = [0, 1, 10, 24124]
+
+ messages = []
+ results = []
+ for param in params:
+ messages.append(msgid % param)
+ results.append(self.message(msgid) % param)
+
+ for message, result in zip(messages, results):
+ self.assertEqual(type(result), gettextutils.Message)
+ self.assertEqual(result.translate(), message)
+
+ # simulate writing out as string
+ result_str = '%s' % result.translate()
+ self.assertEqual(result_str, message)
+ self.assertEqual(result, message)
+
+ def test_mod_copies_parameters(self):
+ msgid = "Found object: %(current_value)s"
+ changing_dict = {'current_value': 1}
+ # A message created with some params
+ result = self.message(msgid) % changing_dict
+ # The parameters may change
+ changing_dict['current_value'] = 2
+ # Even if the param changes when the message is
+ # translated it should use the original param
+ self.assertEqual(result.translate(), 'Found object: 1')
+
+ def test_mod_deep_copies_parameters(self):
+ msgid = "Found list: %(current_list)s"
+ changing_list = list([1, 2, 3])
+ params = {'current_list': changing_list}
+ # Apply the params
+ result = self.message(msgid) % params
+ # Change the list
+ changing_list.append(4)
+ # Even though the list changed the message
+ # translation should use the original list
+ self.assertEqual(result.translate(), "Found list: [1, 2, 3]")
+
+ def test_mod_deep_copies_param_nodeep_param(self):
+ msgid = "Value: %s"
+ params = utils.NoDeepCopyObject(5)
+ # Apply the params
+ result = self.message(msgid) % params
+ self.assertEqual(result.translate(), "Value: 5")
+
+ def test_mod_deep_copies_param_nodeep_dict(self):
+ msgid = "Values: %(val1)s %(val2)s"
+ params = {'val1': 1, 'val2': utils.NoDeepCopyObject(2)}
+ # Apply the params
+ result = self.message(msgid) % params
+ self.assertEqual(result.translate(), "Values: 1 2")
+
+ # Apply again to make sure other path works as well
+ params = {'val1': 3, 'val2': utils.NoDeepCopyObject(4)}
+ result = self.message(msgid) % params
+ self.assertEqual(result.translate(), "Values: 3 4")
+
+ def test_mod_returns_a_copy(self):
+ msgid = "Some msgid string: %(test1)s %(test2)s"
+ message = self.message(msgid)
+ m1 = message % {'test1': 'foo', 'test2': 'bar'}
+ m2 = message % {'test1': 'foo2', 'test2': 'bar2'}
+
+ self.assertIsNot(message, m1)
+ self.assertIsNot(message, m2)
+ self.assertEqual(m1.translate(),
+ msgid % {'test1': 'foo', 'test2': 'bar'})
+ self.assertEqual(m2.translate(),
+ msgid % {'test1': 'foo2', 'test2': 'bar2'})
+
+ def test_mod_with_none_parameter(self):
+ msgid = "Some string with params: %s"
+ message = self.message(msgid) % None
+ self.assertEqual(msgid % None, message)
+ self.assertEqual(msgid % None, message.translate())
+
+ def test_mod_with_missing_parameters(self):
+ msgid = "Some string with params: %s %s"
+ test_me = lambda: self.message(msgid) % 'just one'
+ # Just like with strings missing parameters raise TypeError
+ self.assertRaises(TypeError, test_me)
+
+ def test_mod_with_extra_parameters(self):
+ msgid = "Some string with params: %(param1)s %(param2)s"
+ params = {'param1': 'test',
+ 'param2': 'test2',
+ 'param3': 'notinstring'}
+
+ result = self.message(msgid) % params
+
+ expected = msgid % params
+ self.assertEqual(result, expected)
+ self.assertEqual(result.translate(), expected)
+
+ # Make sure unused params still there
+ self.assertEqual(result.params.keys(), params.keys())
+
+ def test_mod_with_missing_named_parameters(self):
+ msgid = ("Some string with params: %(param1)s %(param2)s"
+ " and a missing one %(missing)s")
+ params = {'param1': 'test',
+ 'param2': 'test2'}
+
+ test_me = lambda: self.message(msgid) % params
+ # Just like with strings missing named parameters raise KeyError
+ self.assertRaises(KeyError, test_me)
+
+ def test_add_disabled(self):
+ msgid = "A message"
+ test_me = lambda: self.message(msgid) + ' some string'
+ self.assertRaises(TypeError, test_me)
+
+ def test_radd_disabled(self):
+ msgid = "A message"
+ test_me = lambda: utils.SomeObject('test') + self.message(msgid)
+ self.assertRaises(TypeError, test_me)
+
+ @testtools.skipIf(six.PY3, 'test specific to Python 2')
+ def test_str_disabled(self):
+ msgid = "A message"
+ test_me = lambda: str(self.message(msgid))
+ self.assertRaises(UnicodeError, test_me)
+
+ @mock.patch('gettext.translation')
+ def test_translate(self, mock_translation):
+ en_message = 'A message in the default locale'
+ es_translation = 'A message in Spanish'
+ message = gettextutils.Message(en_message)
+
+ es_translations = {en_message: es_translation}
+ translations_map = {'es': es_translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ self.assertEqual(es_translation, message.translate('es'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_from_unicoded_object(self, mock_translation):
+ en_message = 'A message in the default locale'
+ es_translation = 'A message in Spanish'
+ message = gettextutils.Message(en_message)
+ es_translations = {en_message: es_translation}
+ translations_map = {'es': es_translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ # Here we are not testing the Message object directly but the result
+ # of unicoding() an object whose unicode representation is a Message
+ obj = utils.SomeObject(message)
+ unicoded_obj = six.text_type(obj)
+
+ self.assertEqual(es_translation, unicoded_obj.translate('es'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_multiple_languages(self, mock_translation):
+ en_message = 'A message in the default locale'
+ es_translation = 'A message in Spanish'
+ zh_translation = 'A message in Chinese'
+ message = gettextutils.Message(en_message)
+
+ es_translations = {en_message: es_translation}
+ zh_translations = {en_message: zh_translation}
+ translations_map = {'es': es_translations,
+ 'zh': zh_translations}
+ translator = fakes.FakeTranslations.translator(translations_map)
+ mock_translation.side_effect = translator
+
+ self.assertEqual(es_translation, message.translate('es'))
+ self.assertEqual(zh_translation, message.translate('zh'))
+ self.assertEqual(en_message, message.translate(None))
+ self.assertEqual(en_message, message.translate('en'))
+ self.assertEqual(en_message, message.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_param(self, mock_translation):
+ message_with_params = 'A message: %s'
+ es_translation = 'A message in Spanish: %s'
+ param = 'A Message param'
+
+ translations = {message_with_params: es_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ msg = msg % param
+
+ default_translation = message_with_params % param
+ expected_translation = es_translation % param
+ self.assertEqual(expected_translation, msg.translate('es'))
+ self.assertEqual(default_translation, msg.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_object_param(self, mock_translation):
+ message_with_params = 'A message: %s'
+ es_translation = 'A message in Spanish: %s'
+ param = 'A Message param'
+ param_translation = 'A Message param in Spanish'
+
+ translations = {message_with_params: es_translation,
+ param: param_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ param_msg = gettextutils.Message(param)
+
+ # Here we are testing translation of a Message with another object
+ # that can be translated via its unicode() representation, this is
+ # very common for instance when modding an Exception with a Message
+ obj = utils.SomeObject(param_msg)
+ msg = msg % obj
+
+ default_translation = message_with_params % param
+ expected_translation = es_translation % param_translation
+
+ self.assertEqual(expected_translation, msg.translate('es'))
+ self.assertEqual(default_translation, msg.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_param_from_unicoded_obj(self,
+ mock_translation):
+ message_with_params = 'A message: %s'
+ es_translation = 'A message in Spanish: %s'
+ param = 'A Message param'
+
+ translations = {message_with_params: es_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ msg = msg % param
+
+ default_translation = message_with_params % param
+ expected_translation = es_translation % param
+
+ obj = utils.SomeObject(msg)
+ unicoded_obj = six.text_type(obj)
+
+ self.assertEqual(expected_translation, unicoded_obj.translate('es'))
+ self.assertEqual(default_translation, unicoded_obj.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_message_parameter(self, mock_translation):
+ message_with_params = 'A message with param: %s'
+ es_translation = 'A message with param in Spanish: %s'
+ message_param = 'A message param'
+ es_param_translation = 'A message param in Spanish'
+
+ translations = {message_with_params: es_translation,
+ message_param: es_param_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ msg_param = gettextutils.Message(message_param)
+ msg = msg % msg_param
+
+ default_translation = message_with_params % message_param
+ expected_translation = es_translation % es_param_translation
+ self.assertEqual(expected_translation, msg.translate('es'))
+ self.assertEqual(default_translation, msg.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_message_parameters(self, mock_translation):
+ message_with_params = 'A message with params: %s %s'
+ es_translation = 'A message with params in Spanish: %s %s'
+ message_param = 'A message param'
+ es_param_translation = 'A message param in Spanish'
+ another_message_param = 'Another message param'
+ another_es_param_translation = 'Another message param in Spanish'
+
+ translations = {message_with_params: es_translation,
+ message_param: es_param_translation,
+ another_message_param: another_es_param_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ param_1 = gettextutils.Message(message_param)
+ param_2 = gettextutils.Message(another_message_param)
+ msg = msg % (param_1, param_2)
+
+ default_translation = message_with_params % (message_param,
+ another_message_param)
+ expected_translation = es_translation % (es_param_translation,
+ another_es_param_translation)
+ self.assertEqual(expected_translation, msg.translate('es'))
+ self.assertEqual(default_translation, msg.translate('XX'))
+
+ @mock.patch('gettext.translation')
+ def test_translate_message_with_named_parameters(self, mock_translation):
+ message_with_params = 'A message with params: %(param)s'
+ es_translation = 'A message with params in Spanish: %(param)s'
+ message_param = 'A Message param'
+ es_param_translation = 'A message param in Spanish'
+
+ translations = {message_with_params: es_translation,
+ message_param: es_param_translation}
+ translator = fakes.FakeTranslations.translator({'es': translations})
+ mock_translation.side_effect = translator
+
+ msg = gettextutils.Message(message_with_params)
+ msg_param = gettextutils.Message(message_param)
+ msg = msg % {'param': msg_param}
+
+ default_translation = message_with_params % {'param': message_param}
+ expected_translation = es_translation % {'param': es_param_translation}
+ self.assertEqual(expected_translation, msg.translate('es'))
+ self.assertEqual(default_translation, msg.translate('XX'))
+
+ @mock.patch('locale.getdefaultlocale')
+ @mock.patch('gettext.translation')
+ def test_translate_message_non_default_locale(self,
+ mock_translation,
+ mock_getdefaultlocale):
+ message_with_params = 'A message with params: %(param)s'
+ es_translation = 'A message with params in Spanish: %(param)s'
+ zh_translation = 'A message with params in Chinese: %(param)s'
+ fr_translation = 'A message with params in French: %(param)s'
+
+ message_param = 'A Message param'
+ es_param_translation = 'A message param in Spanish'
+ zh_param_translation = 'A message param in Chinese'
+ fr_param_translation = 'A message param in French'
+
+ es_translations = {message_with_params: es_translation,
+ message_param: es_param_translation}
+ zh_translations = {message_with_params: zh_translation,
+ message_param: zh_param_translation}
+ fr_translations = {message_with_params: fr_translation,
+ message_param: fr_param_translation}
+
+ translator = fakes.FakeTranslations.translator({'es': es_translations,
+ 'zh': zh_translations,
+ 'fr': fr_translations})
+ mock_translation.side_effect = translator
+ mock_getdefaultlocale.return_value = ('es',)
+
+ msg = gettextutils.Message(message_with_params)
+ msg_param = gettextutils.Message(message_param)
+ msg = msg % {'param': msg_param}
+
+ es_translation = es_translation % {'param': es_param_translation}
+ zh_translation = zh_translation % {'param': zh_param_translation}
+ fr_translation = fr_translation % {'param': fr_param_translation}
+
+ # Because sys.getdefaultlocale() was Spanish,
+ # the default translation will be to Spanish
+ self.assertEqual(es_translation, msg)
+ self.assertEqual(es_translation, msg.translate())
+ self.assertEqual(es_translation, msg.translate('es'))
+
+ # Translation into other locales still works
+ self.assertEqual(zh_translation, msg.translate('zh'))
+ self.assertEqual(fr_translation, msg.translate('fr'))
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000..a6ad6c3
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,42 @@
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import six
+
+
+class SomeObject(object):
+
+ def __init__(self, message):
+ self.message = message
+
+ def __unicode__(self):
+ return self.message
+ # alias for Python 3
+ __str__ = __unicode__
+
+
+class NoDeepCopyObject(object):
+
+ def __init__(self, value):
+ self.value = value
+
+ if six.PY3:
+ def __str__(self):
+ return str(self.value)
+ else:
+ def __unicode__(self):
+ return unicode(self.value)
+
+ def __deepcopy__(self, memo):
+ raise TypeError('Deep Copy not supported')