test_compilation.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import gettext as gettext_module
  4. import os
  5. import stat
  6. import unittest
  7. from subprocess import Popen
  8. from django.core.management import (
  9. CommandError, call_command, execute_from_command_line,
  10. )
  11. from django.core.management.commands.makemessages import \
  12. Command as MakeMessagesCommand
  13. from django.core.management.utils import find_command
  14. from django.test import SimpleTestCase, mock, override_settings
  15. from django.test.utils import captured_stderr, captured_stdout
  16. from django.utils import six, translation
  17. from django.utils.encoding import force_text
  18. from django.utils.six import StringIO
  19. from django.utils.translation import ugettext
  20. from .utils import RunInTmpDirMixin, copytree
  21. has_msgfmt = find_command('msgfmt')
  22. @unittest.skipUnless(has_msgfmt, 'msgfmt is mandatory for compilation tests')
  23. class MessageCompilationTests(RunInTmpDirMixin, SimpleTestCase):
  24. work_subdir = 'commands'
  25. class PoFileTests(MessageCompilationTests):
  26. LOCALE = 'es_AR'
  27. MO_FILE = 'locale/%s/LC_MESSAGES/django.mo' % LOCALE
  28. def test_bom_rejection(self):
  29. with self.assertRaises(CommandError) as cm:
  30. call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
  31. self.assertIn("file has a BOM (Byte Order Mark)", cm.exception.args[0])
  32. self.assertFalse(os.path.exists(self.MO_FILE))
  33. def test_no_write_access(self):
  34. mo_file_en = 'locale/en/LC_MESSAGES/django.mo'
  35. err_buffer = StringIO()
  36. # put file in read-only mode
  37. old_mode = os.stat(mo_file_en).st_mode
  38. os.chmod(mo_file_en, stat.S_IREAD)
  39. try:
  40. call_command('compilemessages', locale=['en'], stderr=err_buffer, verbosity=0)
  41. err = err_buffer.getvalue()
  42. self.assertIn("not writable location", force_text(err))
  43. finally:
  44. os.chmod(mo_file_en, old_mode)
  45. class PoFileContentsTests(MessageCompilationTests):
  46. # Ticket #11240
  47. LOCALE = 'fr'
  48. MO_FILE = 'locale/%s/LC_MESSAGES/django.mo' % LOCALE
  49. def test_percent_symbol_in_po_file(self):
  50. call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
  51. self.assertTrue(os.path.exists(self.MO_FILE))
  52. class MultipleLocaleCompilationTests(MessageCompilationTests):
  53. MO_FILE_HR = None
  54. MO_FILE_FR = None
  55. def setUp(self):
  56. super(MultipleLocaleCompilationTests, self).setUp()
  57. localedir = os.path.join(self.test_dir, 'locale')
  58. self.MO_FILE_HR = os.path.join(localedir, 'hr/LC_MESSAGES/django.mo')
  59. self.MO_FILE_FR = os.path.join(localedir, 'fr/LC_MESSAGES/django.mo')
  60. def test_one_locale(self):
  61. with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'locale')]):
  62. call_command('compilemessages', locale=['hr'], stdout=StringIO())
  63. self.assertTrue(os.path.exists(self.MO_FILE_HR))
  64. def test_multiple_locales(self):
  65. with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'locale')]):
  66. call_command('compilemessages', locale=['hr', 'fr'], stdout=StringIO())
  67. self.assertTrue(os.path.exists(self.MO_FILE_HR))
  68. self.assertTrue(os.path.exists(self.MO_FILE_FR))
  69. class ExcludedLocaleCompilationTests(MessageCompilationTests):
  70. work_subdir = 'exclude'
  71. MO_FILE = 'locale/%s/LC_MESSAGES/django.mo'
  72. def setUp(self):
  73. super(ExcludedLocaleCompilationTests, self).setUp()
  74. copytree('canned_locale', 'locale')
  75. def test_command_help(self):
  76. with captured_stdout(), captured_stderr():
  77. # `call_command` bypasses the parser; by calling
  78. # `execute_from_command_line` with the help subcommand we
  79. # ensure that there are no issues with the parser itself.
  80. execute_from_command_line(['django-admin', 'help', 'compilemessages'])
  81. def test_one_locale_excluded(self):
  82. call_command('compilemessages', exclude=['it'], stdout=StringIO())
  83. self.assertTrue(os.path.exists(self.MO_FILE % 'en'))
  84. self.assertTrue(os.path.exists(self.MO_FILE % 'fr'))
  85. self.assertFalse(os.path.exists(self.MO_FILE % 'it'))
  86. def test_multiple_locales_excluded(self):
  87. call_command('compilemessages', exclude=['it', 'fr'], stdout=StringIO())
  88. self.assertTrue(os.path.exists(self.MO_FILE % 'en'))
  89. self.assertFalse(os.path.exists(self.MO_FILE % 'fr'))
  90. self.assertFalse(os.path.exists(self.MO_FILE % 'it'))
  91. def test_one_locale_excluded_with_locale(self):
  92. call_command('compilemessages', locale=['en', 'fr'], exclude=['fr'], stdout=StringIO())
  93. self.assertTrue(os.path.exists(self.MO_FILE % 'en'))
  94. self.assertFalse(os.path.exists(self.MO_FILE % 'fr'))
  95. self.assertFalse(os.path.exists(self.MO_FILE % 'it'))
  96. def test_multiple_locales_excluded_with_locale(self):
  97. call_command('compilemessages', locale=['en', 'fr', 'it'], exclude=['fr', 'it'],
  98. stdout=StringIO())
  99. self.assertTrue(os.path.exists(self.MO_FILE % 'en'))
  100. self.assertFalse(os.path.exists(self.MO_FILE % 'fr'))
  101. self.assertFalse(os.path.exists(self.MO_FILE % 'it'))
  102. class CompilationErrorHandling(MessageCompilationTests):
  103. def test_error_reported_by_msgfmt(self):
  104. # po file contains wrong po formatting.
  105. with self.assertRaises(CommandError):
  106. call_command('compilemessages', locale=['ja'], verbosity=0)
  107. def test_msgfmt_error_including_non_ascii(self):
  108. # po file contains invalid msgstr content (triggers non-ascii error content).
  109. # Make sure the output of msgfmt is unaffected by the current locale.
  110. env = os.environ.copy()
  111. env.update({str('LANG'): str('C')})
  112. with mock.patch('django.core.management.utils.Popen', lambda *args, **kwargs: Popen(*args, env=env, **kwargs)):
  113. if six.PY2:
  114. # Various assertRaises on PY2 don't support unicode error messages.
  115. try:
  116. call_command('compilemessages', locale=['ko'], verbosity=0)
  117. except CommandError as err:
  118. self.assertIn("' cannot start a field name", six.text_type(err))
  119. else:
  120. cmd = MakeMessagesCommand()
  121. if cmd.gettext_version < (0, 18, 3):
  122. raise unittest.SkipTest("python-brace-format is a recent gettext addition.")
  123. with self.assertRaisesMessage(CommandError, "' cannot start a field name"):
  124. call_command('compilemessages', locale=['ko'], verbosity=0)
  125. class ProjectAndAppTests(MessageCompilationTests):
  126. LOCALE = 'ru'
  127. PROJECT_MO_FILE = 'locale/%s/LC_MESSAGES/django.mo' % LOCALE
  128. APP_MO_FILE = 'app_with_locale/locale/%s/LC_MESSAGES/django.mo' % LOCALE
  129. class FuzzyTranslationTest(ProjectAndAppTests):
  130. def setUp(self):
  131. super(FuzzyTranslationTest, self).setUp()
  132. gettext_module._translations = {} # flush cache or test will be useless
  133. def test_nofuzzy_compiling(self):
  134. with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'locale')]):
  135. call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
  136. with translation.override(self.LOCALE):
  137. self.assertEqual(ugettext('Lenin'), force_text('Ленин'))
  138. self.assertEqual(ugettext('Vodka'), force_text('Vodka'))
  139. def test_fuzzy_compiling(self):
  140. with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'locale')]):
  141. call_command('compilemessages', locale=[self.LOCALE], fuzzy=True, stdout=StringIO())
  142. with translation.override(self.LOCALE):
  143. self.assertEqual(ugettext('Lenin'), force_text('Ленин'))
  144. self.assertEqual(ugettext('Vodka'), force_text('Водка'))
  145. class AppCompilationTest(ProjectAndAppTests):
  146. def test_app_locale_compiled(self):
  147. call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
  148. self.assertTrue(os.path.exists(self.PROJECT_MO_FILE))
  149. self.assertTrue(os.path.exists(self.APP_MO_FILE))