Permalink
Browse files

[1.10.x] Fixed #26677 -- Converted some i18n tests to use disposable …

…FS tree.

This allows makemessages/compilemessages tests in `test_extraction.py`
and `test_compilation.py` to actually run isolated from each other
(unaffected by stray FS objects left by cleanup actions failures, debug
sessions, etc.) and to take advantage of the parallel tests execution
feature like most of the Django test suite.

`test_percents.py` gets slightly refactored to not inherit from the new
machinery which sets up every test case to copy and run under a
temporary tree.

Backport of faeeb84 from master
  • Loading branch information...
ramiro authored and timgraham committed May 29, 2016
1 parent d0f417b commit c1bd4679e896301dd26f0bb51d905a194eb358b7
Showing with 71 additions and 67 deletions.
  1. +13 −3 tests/i18n/test_compilation.py
  2. +56 −62 tests/i18n/test_extraction.py
  3. +2 −2 tests/i18n/test_percents.py
@@ -5,6 +5,7 @@
import os
import shutil
import stat
+import tempfile
import unittest
from subprocess import Popen
@@ -23,15 +24,25 @@
from django.utils.translation import ugettext
has_msgfmt = find_command('msgfmt')
+source_code_dir = os.path.dirname(upath(__file__))
@unittest.skipUnless(has_msgfmt, 'msgfmt is mandatory for compilation tests')
class MessageCompilationTests(SimpleTestCase):
- test_dir = os.path.abspath(os.path.join(os.path.dirname(upath(__file__)), 'commands'))
+ work_subdir = 'commands'
def setUp(self):
self._cwd = os.getcwd()
+ self.work_dir = tempfile.mkdtemp(prefix='i18n_')
+ self.test_dir = os.path.abspath(os.path.join(self.work_dir, self.work_subdir))
+ shutil.copytree(os.path.join(source_code_dir, self.work_subdir), self.test_dir)
+ # Make sure we step out of the temporary working tree before we
+ # remove it as we might be pulling the rug from under our own feet
+ # othewise. Rhis is especially true on Windows.
+ # Remember cleanup actions registered with addCleanup() are called in
+ # reverse so this ordering is important:
+ self.addCleanup(self._rmrf, self.test_dir)
self.addCleanup(os.chdir, self._cwd)
os.chdir(self.test_dir)
@@ -114,13 +125,12 @@ def test_multiple_locales(self):
class ExcludedLocaleCompilationTests(MessageCompilationTests):
- test_dir = os.path.abspath(os.path.join(os.path.dirname(upath(__file__)), 'exclude'))
+ work_subdir = 'exclude'
MO_FILE = 'locale/%s/LC_MESSAGES/django.mo'
def setUp(self):
super(ExcludedLocaleCompilationTests, self).setUp()
-
shutil.copytree('canned_locale', 'locale')
self.addCleanup(self._rmrf, os.path.join(self.test_dir, 'locale'))
@@ -5,6 +5,7 @@
import os
import re
import shutil
+import tempfile
import time
import warnings
from unittest import SkipTest, skipUnless
@@ -17,7 +18,6 @@
Command as MakeMessagesCommand
from django.core.management.utils import find_command
from django.test import SimpleTestCase, mock, override_settings
-from django.test.testcases import SerializeMixin
from django.test.utils import captured_stderr, captured_stdout
from django.utils import six
from django.utils._os import upath
@@ -27,23 +27,36 @@
LOCALE = 'de'
has_xgettext = find_command('xgettext')
-this_directory = os.path.dirname(upath(__file__))
+source_code_dir = os.path.dirname(upath(__file__))
-@skipUnless(has_xgettext, 'xgettext is mandatory for extraction tests')
-class ExtractorTests(SerializeMixin, SimpleTestCase):
+class POFileAssertionMixin(object):
- # makemessages scans the current working directory and writes in the
- # locale subdirectory. There aren't any options to control this. As a
- # consequence tests can't run in parallel. Since i18n tests run in less
- # than 4 seconds, serializing them with SerializeMixin is acceptable.
- lockfile = __file__
+ def _assertPoKeyword(self, keyword, expected_value, haystack, use_quotes=True):
+ q = '"'
+ if use_quotes:
+ expected_value = '"%s"' % expected_value
+ q = "'"
+ needle = '%s %s' % (keyword, expected_value)
+ expected_value = re.escape(expected_value)
+ return self.assertTrue(re.search('^%s %s' % (keyword, expected_value), haystack, re.MULTILINE),
+ 'Could not find %(q)s%(n)s%(q)s in generated PO file' % {'n': needle, 'q': q})
- test_dir = os.path.abspath(os.path.join(this_directory, 'commands'))
+ def assertMsgId(self, msgid, haystack, use_quotes=True):
+ return self._assertPoKeyword('msgid', msgid, haystack, use_quotes=use_quotes)
+
+
+@skipUnless(has_xgettext, 'xgettext is mandatory for extraction tests')
+class ExtractorTests(POFileAssertionMixin, SimpleTestCase):
+
+ work_subdir = 'commands'
PO_FILE = 'locale/%s/LC_MESSAGES/django.po' % LOCALE
def setUp(self):
+ self.work_dir = tempfile.mkdtemp(prefix='i18n_')
+ self.test_dir = os.path.abspath(os.path.join(self.work_dir, self.work_subdir))
+ shutil.copytree(os.path.join(source_code_dir, self.work_subdir), self.test_dir)
self._cwd = os.getcwd()
def _rmrf(self, dname):
@@ -73,19 +86,6 @@ def _run_makemessages(self, **options):
po_contents = fp.read()
return output, po_contents
- def _assertPoKeyword(self, keyword, expected_value, haystack, use_quotes=True):
- q = '"'
- if use_quotes:
- expected_value = '"%s"' % expected_value
- q = "'"
- needle = '%s %s' % (keyword, expected_value)
- expected_value = re.escape(expected_value)
- return self.assertTrue(re.search('^%s %s' % (keyword, expected_value), haystack, re.MULTILINE),
- 'Could not find %(q)s%(n)s%(q)s in generated PO file' % {'n': needle, 'q': q})
-
- def assertMsgId(self, msgid, haystack, use_quotes=True):
- return self._assertPoKeyword('msgid', msgid, haystack, use_quotes=use_quotes)
-
def assertMsgIdPlural(self, msgid, haystack, use_quotes=True):
return self._assertPoKeyword('msgid_plural', msgid, haystack, use_quotes=use_quotes)
@@ -471,16 +471,15 @@ def test_javascript_literals(self):
self.assertMsgId("quz", po_contents)
self.assertMsgId("foobar", po_contents)
- @override_settings(
- STATIC_ROOT=os.path.join(this_directory, 'commands', 'static/'),
- MEDIA_ROOT=os.path.join(this_directory, 'commands', 'media_root/'))
def test_media_static_dirs_ignored(self):
"""
Regression test for #23583.
"""
- _, po_contents = self._run_makemessages(domain='djangojs')
- self.assertMsgId("Static content inside app should be included.", po_contents)
- self.assertNotMsgId("Content from STATIC_ROOT should not be included", po_contents)
+ with override_settings(STATIC_ROOT=os.path.join(self.test_dir, 'static/'),
+ MEDIA_ROOT=os.path.join(self.test_dir, 'media_root/')):
+ _, po_contents = self._run_makemessages(domain='djangojs')
+ self.assertMsgId("Static content inside app should be included.", po_contents)
+ self.assertNotMsgId("Content from STATIC_ROOT should not be included", po_contents)
@override_settings(STATIC_ROOT=None, MEDIA_ROOT='')
def test_default_root_settings(self):
@@ -516,13 +515,12 @@ def test_ignore_file_patterns(self):
self.assertIn("ignoring file xxx_ignored.html", out)
self.assertNotMsgId('This should be ignored too.', po_contents)
- @override_settings(
- STATIC_ROOT=os.path.join(this_directory, 'commands', 'static/'),
- MEDIA_ROOT=os.path.join(this_directory, 'commands', 'media_root/'))
def test_media_static_dirs_ignored(self):
- out, _ = self._run_makemessages()
- self.assertIn("ignoring directory static", out)
- self.assertIn("ignoring directory media_root", out)
+ with override_settings(STATIC_ROOT=os.path.join(self.test_dir, 'static/'),
+ MEDIA_ROOT=os.path.join(self.test_dir, 'media_root/')):
+ out, _ = self._run_makemessages()
+ self.assertIn("ignoring directory static", out)
+ self.assertIn("ignoring directory media_root", out)
class SymlinkExtractorTests(ExtractorTests):
@@ -728,11 +726,11 @@ def test_multiple_locales(self):
class ExcludedLocaleExtractionTests(ExtractorTests):
+ work_subdir = 'exclude'
+
LOCALES = ['en', 'fr', 'it']
PO_FILE = 'locale/%s/LC_MESSAGES/django.po'
- test_dir = os.path.abspath(os.path.join(this_directory, 'exclude'))
-
def _set_times_for_all_po_files(self):
"""
Set access and modification times to the Unix epoch time for all the .po files.
@@ -782,41 +780,37 @@ def test_multiple_locales_excluded_with_locale(self):
class CustomLayoutExtractionTests(ExtractorTests):
- def setUp(self):
- super(CustomLayoutExtractionTests, self).setUp()
- self.test_dir = os.path.join(this_directory, 'project_dir')
+ work_subdir = 'project_dir'
def test_no_locale_raises(self):
os.chdir(self.test_dir)
msg = "Unable to find a locale path to store translations for file"
with self.assertRaisesMessage(management.CommandError, msg):
management.call_command('makemessages', locale=LOCALE, verbosity=0)
- @override_settings(
- LOCALE_PATHS=[os.path.join(this_directory, 'project_dir', 'project_locale')],
- )
def test_project_locale_paths(self):
"""
Test that:
* translations for an app containing a locale folder are stored in that folder
* translations outside of that app are in LOCALE_PATHS[0]
"""
- os.chdir(self.test_dir)
- self.addCleanup(shutil.rmtree, os.path.join(settings.LOCALE_PATHS[0], LOCALE), True)
- self.addCleanup(shutil.rmtree, os.path.join(self.test_dir, 'app_with_locale', 'locale', LOCALE), True)
-
- management.call_command('makemessages', locale=[LOCALE], verbosity=0)
- project_de_locale = os.path.join(
- self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po')
- app_de_locale = os.path.join(
- self.test_dir, 'app_with_locale', 'locale', 'de', 'LC_MESSAGES', 'django.po')
- self.assertTrue(os.path.exists(project_de_locale))
- self.assertTrue(os.path.exists(app_de_locale))
-
- with open(project_de_locale, 'r') as fp:
- po_contents = force_text(fp.read())
- self.assertMsgId('This app has no locale directory', po_contents)
- self.assertMsgId('This is a project-level string', po_contents)
- with open(app_de_locale, 'r') as fp:
- po_contents = force_text(fp.read())
- self.assertMsgId('This app has a locale directory', po_contents)
+ with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'project_locale')]):
+ os.chdir(self.test_dir)
+ self.addCleanup(shutil.rmtree, os.path.join(settings.LOCALE_PATHS[0], LOCALE), True)
+ self.addCleanup(shutil.rmtree, os.path.join(self.test_dir, 'app_with_locale', 'locale', LOCALE), True)
+
+ management.call_command('makemessages', locale=[LOCALE], verbosity=0)
+ project_de_locale = os.path.join(
+ self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po')
+ app_de_locale = os.path.join(
+ self.test_dir, 'app_with_locale', 'locale', 'de', 'LC_MESSAGES', 'django.po')
+ self.assertTrue(os.path.exists(project_de_locale))
+ self.assertTrue(os.path.exists(app_de_locale))
+
+ with open(project_de_locale, 'r') as fp:
+ po_contents = force_text(fp.read())
+ self.assertMsgId('This app has no locale directory', po_contents)
+ self.assertMsgId('This is a project-level string', po_contents)
+ with open(app_de_locale, 'r') as fp:
+ po_contents = force_text(fp.read())
+ self.assertMsgId('This app has a locale directory', po_contents)
@@ -9,7 +9,7 @@
from django.utils.encoding import force_text
from django.utils.translation import activate, get_language, trans_real
-from .test_extraction import ExtractorTests
+from .test_extraction import POFileAssertionMixin
SAMPLEPROJECT_DIR = os.path.join(os.path.dirname(os.path.abspath(upath(__file__))), 'sampleproject')
SAMPLEPROJECT_LOCALE = os.path.join(SAMPLEPROJECT_DIR, 'locale')
@@ -31,7 +31,7 @@ def tearDown(self):
activate(self._language)
-class ExtractingStringsWithPercentSigns(FrenchTestCase, ExtractorTests):
+class ExtractingStringsWithPercentSigns(POFileAssertionMixin, FrenchTestCase):
"""
Tests the extracted string found in the gettext catalog.

0 comments on commit c1bd467

Please sign in to comment.