1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

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.
This commit is contained in:
Ramiro Morales 2016-05-29 11:25:05 -03:00
parent 83120af2c1
commit faeeb84edf
3 changed files with 69 additions and 65 deletions

View File

@ -5,6 +5,7 @@ import gettext as gettext_module
import os import os
import shutil import shutil
import stat import stat
import tempfile
import unittest import unittest
from subprocess import Popen from subprocess import Popen
@ -23,15 +24,25 @@ from django.utils.six import StringIO
from django.utils.translation import ugettext from django.utils.translation import ugettext
has_msgfmt = find_command('msgfmt') has_msgfmt = find_command('msgfmt')
source_code_dir = os.path.dirname(upath(__file__))
@unittest.skipUnless(has_msgfmt, 'msgfmt is mandatory for compilation tests') @unittest.skipUnless(has_msgfmt, 'msgfmt is mandatory for compilation tests')
class MessageCompilationTests(SimpleTestCase): class MessageCompilationTests(SimpleTestCase):
test_dir = os.path.abspath(os.path.join(os.path.dirname(upath(__file__)), 'commands')) work_subdir = 'commands'
def setUp(self): def setUp(self):
self._cwd = os.getcwd() 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) self.addCleanup(os.chdir, self._cwd)
os.chdir(self.test_dir) os.chdir(self.test_dir)
@ -114,13 +125,12 @@ class MultipleLocaleCompilationTests(MessageCompilationTests):
class ExcludedLocaleCompilationTests(MessageCompilationTests): 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' MO_FILE = 'locale/%s/LC_MESSAGES/django.mo'
def setUp(self): def setUp(self):
super(ExcludedLocaleCompilationTests, self).setUp() super(ExcludedLocaleCompilationTests, self).setUp()
shutil.copytree('canned_locale', 'locale') shutil.copytree('canned_locale', 'locale')
self.addCleanup(self._rmrf, os.path.join(self.test_dir, 'locale')) self.addCleanup(self._rmrf, os.path.join(self.test_dir, 'locale'))

View File

@ -5,6 +5,7 @@ import io
import os import os
import re import re
import shutil import shutil
import tempfile
import time import time
import warnings import warnings
from unittest import SkipTest, skipUnless from unittest import SkipTest, skipUnless
@ -17,7 +18,6 @@ from django.core.management.commands.makemessages import \
Command as MakeMessagesCommand Command as MakeMessagesCommand
from django.core.management.utils import find_command from django.core.management.utils import find_command
from django.test import SimpleTestCase, mock, override_settings 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.test.utils import captured_stderr, captured_stdout
from django.utils import six from django.utils import six
from django.utils._os import upath from django.utils._os import upath
@ -27,23 +27,36 @@ from django.utils.translation import TranslatorCommentWarning
LOCALE = 'de' LOCALE = 'de'
has_xgettext = find_command('xgettext') has_xgettext = find_command('xgettext')
this_directory = os.path.dirname(upath(__file__)) source_code_dir = os.path.dirname(upath(__file__))
class POFileAssertionMixin(object):
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)
@skipUnless(has_xgettext, 'xgettext is mandatory for extraction tests') @skipUnless(has_xgettext, 'xgettext is mandatory for extraction tests')
class ExtractorTests(SerializeMixin, SimpleTestCase): class ExtractorTests(POFileAssertionMixin, SimpleTestCase):
# makemessages scans the current working directory and writes in the work_subdir = 'commands'
# 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__
test_dir = os.path.abspath(os.path.join(this_directory, 'commands'))
PO_FILE = 'locale/%s/LC_MESSAGES/django.po' % LOCALE PO_FILE = 'locale/%s/LC_MESSAGES/django.po' % LOCALE
def setUp(self): 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() self._cwd = os.getcwd()
def _rmrf(self, dname): def _rmrf(self, dname):
@ -73,19 +86,6 @@ class ExtractorTests(SerializeMixin, SimpleTestCase):
po_contents = fp.read() po_contents = fp.read()
return output, po_contents 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): def assertMsgIdPlural(self, msgid, haystack, use_quotes=True):
return self._assertPoKeyword('msgid_plural', msgid, haystack, use_quotes=use_quotes) return self._assertPoKeyword('msgid_plural', msgid, haystack, use_quotes=use_quotes)
@ -464,16 +464,15 @@ class JavascriptExtractorTests(ExtractorTests):
self.assertMsgId("quz", po_contents) self.assertMsgId("quz", po_contents)
self.assertMsgId("foobar", 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): def test_media_static_dirs_ignored(self):
""" """
Regression test for #23583. Regression test for #23583.
""" """
_, po_contents = self._run_makemessages(domain='djangojs') with override_settings(STATIC_ROOT=os.path.join(self.test_dir, 'static/'),
self.assertMsgId("Static content inside app should be included.", po_contents) MEDIA_ROOT=os.path.join(self.test_dir, 'media_root/')):
self.assertNotMsgId("Content from STATIC_ROOT should not be included", po_contents) _, 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='') @override_settings(STATIC_ROOT=None, MEDIA_ROOT='')
def test_default_root_settings(self): def test_default_root_settings(self):
@ -509,13 +508,12 @@ class IgnoredExtractorTests(ExtractorTests):
self.assertIn("ignoring file xxx_ignored.html", out) self.assertIn("ignoring file xxx_ignored.html", out)
self.assertNotMsgId('This should be ignored too.', po_contents) 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): def test_media_static_dirs_ignored(self):
out, _ = self._run_makemessages() with override_settings(STATIC_ROOT=os.path.join(self.test_dir, 'static/'),
self.assertIn("ignoring directory static", out) MEDIA_ROOT=os.path.join(self.test_dir, 'media_root/')):
self.assertIn("ignoring directory media_root", out) out, _ = self._run_makemessages()
self.assertIn("ignoring directory static", out)
self.assertIn("ignoring directory media_root", out)
class SymlinkExtractorTests(ExtractorTests): class SymlinkExtractorTests(ExtractorTests):
@ -721,11 +719,11 @@ class MultipleLocaleExtractionTests(ExtractorTests):
class ExcludedLocaleExtractionTests(ExtractorTests): class ExcludedLocaleExtractionTests(ExtractorTests):
work_subdir = 'exclude'
LOCALES = ['en', 'fr', 'it'] LOCALES = ['en', 'fr', 'it']
PO_FILE = 'locale/%s/LC_MESSAGES/django.po' 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): def _set_times_for_all_po_files(self):
""" """
Set access and modification times to the Unix epoch time for all the .po files. Set access and modification times to the Unix epoch time for all the .po files.
@ -775,9 +773,7 @@ class ExcludedLocaleExtractionTests(ExtractorTests):
class CustomLayoutExtractionTests(ExtractorTests): class CustomLayoutExtractionTests(ExtractorTests):
def setUp(self): work_subdir = 'project_dir'
super(CustomLayoutExtractionTests, self).setUp()
self.test_dir = os.path.join(this_directory, 'project_dir')
def test_no_locale_raises(self): def test_no_locale_raises(self):
os.chdir(self.test_dir) os.chdir(self.test_dir)
@ -785,31 +781,29 @@ class CustomLayoutExtractionTests(ExtractorTests):
with self.assertRaisesMessage(management.CommandError, msg): with self.assertRaisesMessage(management.CommandError, msg):
management.call_command('makemessages', locale=LOCALE, verbosity=0) 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): def test_project_locale_paths(self):
""" """
Test that: Test that:
* translations for an app containing a locale folder are stored in that folder * translations for an app containing a locale folder are stored in that folder
* translations outside of that app are in LOCALE_PATHS[0] * translations outside of that app are in LOCALE_PATHS[0]
""" """
os.chdir(self.test_dir) with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'project_locale')]):
self.addCleanup(shutil.rmtree, os.path.join(settings.LOCALE_PATHS[0], LOCALE), True) os.chdir(self.test_dir)
self.addCleanup(shutil.rmtree, os.path.join(self.test_dir, 'app_with_locale', 'locale', LOCALE), True) 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) management.call_command('makemessages', locale=[LOCALE], verbosity=0)
project_de_locale = os.path.join( project_de_locale = os.path.join(
self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po') self.test_dir, 'project_locale', 'de', 'LC_MESSAGES', 'django.po')
app_de_locale = os.path.join( app_de_locale = os.path.join(
self.test_dir, 'app_with_locale', 'locale', 'de', 'LC_MESSAGES', 'django.po') 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(project_de_locale))
self.assertTrue(os.path.exists(app_de_locale)) self.assertTrue(os.path.exists(app_de_locale))
with open(project_de_locale, 'r') as fp: with open(project_de_locale, 'r') as fp:
po_contents = force_text(fp.read()) po_contents = force_text(fp.read())
self.assertMsgId('This app has no locale directory', po_contents) self.assertMsgId('This app has no locale directory', po_contents)
self.assertMsgId('This is a project-level string', po_contents) self.assertMsgId('This is a project-level string', po_contents)
with open(app_de_locale, 'r') as fp: with open(app_de_locale, 'r') as fp:
po_contents = force_text(fp.read()) po_contents = force_text(fp.read())
self.assertMsgId('This app has a locale directory', po_contents) self.assertMsgId('This app has a locale directory', po_contents)

View File

@ -9,7 +9,7 @@ from django.utils._os import upath
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import activate, get_language, trans_real 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_DIR = os.path.join(os.path.dirname(os.path.abspath(upath(__file__))), 'sampleproject')
SAMPLEPROJECT_LOCALE = os.path.join(SAMPLEPROJECT_DIR, 'locale') SAMPLEPROJECT_LOCALE = os.path.join(SAMPLEPROJECT_DIR, 'locale')
@ -31,7 +31,7 @@ class FrenchTestCase(SimpleTestCase):
activate(self._language) activate(self._language)
class ExtractingStringsWithPercentSigns(FrenchTestCase, ExtractorTests): class ExtractingStringsWithPercentSigns(POFileAssertionMixin, FrenchTestCase):
""" """
Tests the extracted string found in the gettext catalog. Tests the extracted string found in the gettext catalog.