[Python-modules-commits] [python-django] 03/07: Fix test suite in parallel mode

Raphaël Hertzog hertzog at moszumanska.debian.org
Wed Nov 23 10:15:46 UTC 2016


This is an automated email from the git hooks/post-receive script.

hertzog pushed a commit to branch debian/master
in repository python-django.

commit d7531e26589c6918efa9670d742fa448a61fcc39
Author: Raphaël Hertzog <hertzog at debian.org>
Date:   Wed Nov 23 10:28:23 2016 +0100

    Fix test suite in parallel mode
    
    Origin: upstream, https://code.djangoproject.com/changeset/faeeb84edfebecf5a5f40df9ef816e5f1cd457c6/ https://code.djangoproject.com/changeset/bb7bb379e8cd91a91336946829519d64e919a1d2/
    Bug: https://code.djangoproject.com/ticket/27526
    Bug-Debian: https://bugs.debian.org/844139
---
 tests/i18n/test_compilation.py |  43 ++--------
 tests/i18n/test_extraction.py  | 185 ++++++++---------------------------------
 tests/i18n/test_percents.py    |   5 +-
 tests/i18n/utils.py            |  67 +++++++++++++++
 4 files changed, 110 insertions(+), 190 deletions(-)

diff --git a/tests/i18n/test_compilation.py b/tests/i18n/test_compilation.py
index a65fcac..ac2d468 100644
--- a/tests/i18n/test_compilation.py
+++ b/tests/i18n/test_compilation.py
@@ -3,7 +3,6 @@ from __future__ import unicode_literals
 
 import gettext as gettext_module
 import os
-import shutil
 import stat
 import unittest
 from subprocess import Popen
@@ -17,32 +16,19 @@ from django.core.management.utils import find_command
 from django.test import SimpleTestCase, mock, override_settings
 from django.test.utils import captured_stderr, captured_stdout
 from django.utils import six, translation
-from django.utils._os import upath
 from django.utils.encoding import force_text
 from django.utils.six import StringIO
 from django.utils.translation import ugettext
 
+from .utils import RunInTmpDirMixin, copytree
+
 has_msgfmt = find_command('msgfmt')
 
 
 @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'))
-
-    def setUp(self):
-        self._cwd = os.getcwd()
-        self.addCleanup(os.chdir, self._cwd)
-        os.chdir(self.test_dir)
-
-    def _rmrf(self, dname):
-        if os.path.commonprefix([self.test_dir, os.path.abspath(dname)]) != self.test_dir:
-            return
-        shutil.rmtree(dname)
+class MessageCompilationTests(RunInTmpDirMixin, SimpleTestCase):
 
-    def rmfile(self, filepath):
-        if os.path.exists(filepath):
-            os.remove(filepath)
+    work_subdir = 'commands'
 
 
 class PoFileTests(MessageCompilationTests):
@@ -76,10 +62,6 @@ class PoFileContentsTests(MessageCompilationTests):
     LOCALE = 'fr'
     MO_FILE = 'locale/%s/LC_MESSAGES/django.mo' % LOCALE
 
-    def setUp(self):
-        super(PoFileContentsTests, self).setUp()
-        self.addCleanup(os.unlink, os.path.join(self.test_dir, self.MO_FILE))
-
     def test_percent_symbol_in_po_file(self):
         call_command('compilemessages', locale=[self.LOCALE], stdout=StringIO())
         self.assertTrue(os.path.exists(self.MO_FILE))
@@ -95,8 +77,6 @@ class MultipleLocaleCompilationTests(MessageCompilationTests):
         localedir = os.path.join(self.test_dir, 'locale')
         self.MO_FILE_HR = os.path.join(localedir, 'hr/LC_MESSAGES/django.mo')
         self.MO_FILE_FR = os.path.join(localedir, 'fr/LC_MESSAGES/django.mo')
-        self.addCleanup(self.rmfile, os.path.join(localedir, self.MO_FILE_HR))
-        self.addCleanup(self.rmfile, os.path.join(localedir, self.MO_FILE_FR))
 
     def test_one_locale(self):
         with override_settings(LOCALE_PATHS=[os.path.join(self.test_dir, 'locale')]):
@@ -114,15 +94,13 @@ class MultipleLocaleCompilationTests(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'
 
     def setUp(self):
         super(ExcludedLocaleCompilationTests, self).setUp()
-
-        shutil.copytree('canned_locale', 'locale')
-        self.addCleanup(self._rmrf, os.path.join(self.test_dir, 'locale'))
+        copytree('canned_locale', 'locale')
 
     def test_command_help(self):
         with captured_stdout(), captured_stderr():
@@ -160,15 +138,11 @@ class ExcludedLocaleCompilationTests(MessageCompilationTests):
 class CompilationErrorHandling(MessageCompilationTests):
     def test_error_reported_by_msgfmt(self):
         # po file contains wrong po formatting.
-        mo_file = 'locale/ja/LC_MESSAGES/django.mo'
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, mo_file))
         with self.assertRaises(CommandError):
             call_command('compilemessages', locale=['ja'], verbosity=0)
 
     def test_msgfmt_error_including_non_ascii(self):
         # po file contains invalid msgstr content (triggers non-ascii error content).
-        mo_file = 'locale/ko/LC_MESSAGES/django.mo'
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, mo_file))
         # Make sure the output of msgfmt is unaffected by the current locale.
         env = os.environ.copy()
         env.update({str('LANG'): str('C')})
@@ -192,11 +166,6 @@ class ProjectAndAppTests(MessageCompilationTests):
     PROJECT_MO_FILE = 'locale/%s/LC_MESSAGES/django.mo' % LOCALE
     APP_MO_FILE = 'app_with_locale/locale/%s/LC_MESSAGES/django.mo' % LOCALE
 
-    def setUp(self):
-        super(ProjectAndAppTests, self).setUp()
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, self.PROJECT_MO_FILE))
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, self.APP_MO_FILE))
-
 
 class FuzzyTranslationTest(ProjectAndAppTests):
 
diff --git a/tests/i18n/test_extraction.py b/tests/i18n/test_extraction.py
index f57b161..d00a50d 100644
--- a/tests/i18n/test_extraction.py
+++ b/tests/i18n/test_extraction.py
@@ -9,7 +9,6 @@ import time
 import warnings
 from unittest import SkipTest, skipUnless
 
-from django.conf import settings
 from django.core import management
 from django.core.management import execute_from_command_line
 from django.core.management.base import CommandError
@@ -17,52 +16,25 @@ from django.core.management.commands.makemessages import \
     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
 from django.utils.encoding import force_text
 from django.utils.six import StringIO
 from django.utils.translation import TranslatorCommentWarning
 
+from .utils import POFileAssertionMixin, RunInTmpDirMixin, copytree
+
 LOCALE = 'de'
 has_xgettext = find_command('xgettext')
-this_directory = os.path.dirname(upath(__file__))
 
 
 @skipUnless(has_xgettext, 'xgettext is mandatory for extraction tests')
-class ExtractorTests(SerializeMixin, SimpleTestCase):
-
-    # 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__
+class ExtractorTests(POFileAssertionMixin, RunInTmpDirMixin, SimpleTestCase):
 
-    test_dir = os.path.abspath(os.path.join(this_directory, 'commands'))
+    work_subdir = 'commands'
 
     PO_FILE = 'locale/%s/LC_MESSAGES/django.po' % LOCALE
 
-    def setUp(self):
-        self._cwd = os.getcwd()
-
-    def _rmrf(self, dname):
-        if os.path.commonprefix([self.test_dir, os.path.abspath(dname)]) != self.test_dir:
-            return
-        shutil.rmtree(dname)
-
-    def rmfile(self, filepath):
-        if os.path.exists(filepath):
-            os.remove(filepath)
-
-    def tearDown(self):
-        os.chdir(self.test_dir)
-        try:
-            self._rmrf('locale/%s' % LOCALE)
-        except OSError:
-            pass
-        os.chdir(self._cwd)
-
     def _run_makemessages(self, **options):
         os.chdir(self.test_dir)
         out = StringIO()
@@ -73,19 +45,6 @@ class ExtractorTests(SerializeMixin, SimpleTestCase):
             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)
 
@@ -177,7 +136,6 @@ class ExtractorTests(SerializeMixin, SimpleTestCase):
 class BasicExtractorTests(ExtractorTests):
 
     def test_comments_extractor(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with io.open(self.PO_FILE, 'r', encoding='utf-8') as fp:
@@ -211,7 +169,6 @@ class BasicExtractorTests(ExtractorTests):
             )
 
     def test_special_char_extracted(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with io.open(self.PO_FILE, 'r', encoding='utf-8') as fp:
@@ -219,7 +176,6 @@ class BasicExtractorTests(ExtractorTests):
             self.assertMsgId("Non-breaking space\u00a0:", po_contents)
 
     def test_blocktrans_trimmed(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -237,7 +193,6 @@ class BasicExtractorTests(ExtractorTests):
         self.assertTrue(MakeMessagesCommand.leave_locale_alone)
 
     def test_extraction_error(self):
-        os.chdir(self.test_dir)
         msg = (
             'Translation blocks must not include other block tags: blocktrans '
             '(file %s, line 3)' % os.path.join('templates', 'template_with_error.tpl')
@@ -248,9 +203,7 @@ class BasicExtractorTests(ExtractorTests):
         self.assertFalse(os.path.exists('./templates/template_with_error.tpl.py'))
 
     def test_unicode_decode_error(self):
-        os.chdir(self.test_dir)
         shutil.copyfile('./not_utf8.sample', './not_utf8.txt')
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'not_utf8.txt'))
         out = StringIO()
         management.call_command('makemessages', locale=[LOCALE], stdout=out)
         self.assertIn("UnicodeDecodeError: skipped file not_utf8.txt in .",
@@ -265,9 +218,7 @@ class BasicExtractorTests(ExtractorTests):
 
     def test_extraction_warning(self):
         """test xgettext warning about multiple bare interpolation placeholders"""
-        os.chdir(self.test_dir)
         shutil.copyfile('./code.sample', './code_sample.py')
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'code_sample.py'))
         out = StringIO()
         management.call_command('makemessages', locale=[LOCALE], stdout=out)
         self.assertIn("code_sample.py:4", force_text(out.getvalue()))
@@ -278,7 +229,6 @@ class BasicExtractorTests(ExtractorTests):
         {% trans %} and {% blocktrans %} template tags.
         Refs #14806.
         """
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -309,7 +259,6 @@ class BasicExtractorTests(ExtractorTests):
             self.assertMsgId("Translatable literal #8d %(a)s", po_contents)
 
     def test_context_in_single_quotes(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -324,7 +273,6 @@ class BasicExtractorTests(ExtractorTests):
 
     def test_template_comments(self):
         """Template comment tags on the same line of other constructs (#19552)"""
-        os.chdir(self.test_dir)
         # Test detection/end user reporting of old, incorrect templates
         # translator comments syntax
         with warnings.catch_warnings(record=True) as ws:
@@ -439,9 +387,7 @@ class BasicExtractorTests(ExtractorTests):
     def test_po_file_encoding_when_updating(self):
         """Update of PO file doesn't corrupt it with non-UTF-8 encoding on Python3+Windows (#23271)"""
         BR_PO_BASE = 'locale/pt_BR/LC_MESSAGES/django'
-        os.chdir(self.test_dir)
         shutil.copyfile(BR_PO_BASE + '.pristine', BR_PO_BASE + '.po')
-        self.addCleanup(self.rmfile, os.path.join(self.test_dir, 'locale', 'pt_BR', 'LC_MESSAGES', 'django.po'))
         management.call_command('makemessages', locale=['pt_BR'], verbosity=0)
         self.assertTrue(os.path.exists(BR_PO_BASE + '.po'))
         with io.open(BR_PO_BASE + '.po', 'r', encoding='utf-8') as fp:
@@ -454,7 +400,6 @@ class JavascriptExtractorTests(ExtractorTests):
     PO_FILE = 'locale/%s/LC_MESSAGES/djangojs.po' % LOCALE
 
     def test_javascript_literals(self):
-        os.chdir(self.test_dir)
         _, po_contents = self._run_makemessages(domain='djangojs')
         self.assertMsgId('This literal should be included.', po_contents)
         self.assertMsgId('gettext_noop should, too.', po_contents)
@@ -471,16 +416,15 @@ class JavascriptExtractorTests(ExtractorTests):
         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 +460,12 @@ class IgnoredExtractorTests(ExtractorTests):
         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):
@@ -531,15 +474,6 @@ class SymlinkExtractorTests(ExtractorTests):
         super(SymlinkExtractorTests, self).setUp()
         self.symlinked_dir = os.path.join(self.test_dir, 'templates_symlinked')
 
-    def tearDown(self):
-        super(SymlinkExtractorTests, self).tearDown()
-        os.chdir(self.test_dir)
-        try:
-            os.remove(self.symlinked_dir)
-        except OSError:
-            pass
-        os.chdir(self._cwd)
-
     def test_symlink(self):
         # On Python < 3.2 os.symlink() exists only on Unix
         if hasattr(os, 'symlink'):
@@ -570,17 +504,7 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
 
     PO_FILE_ES = 'locale/es/LC_MESSAGES/django.po'
 
-    def tearDown(self):
-        super(CopyPluralFormsExtractorTests, self).tearDown()
-        os.chdir(self.test_dir)
-        try:
-            self._rmrf('locale/es')
-        except OSError:
-            pass
-        os.chdir(self._cwd)
-
     def test_copy_plural_forms(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -589,7 +513,6 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
 
     def test_override_plural_forms(self):
         """Ticket #20311."""
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=['es'], extensions=['djtpl'], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE_ES))
         with io.open(self.PO_FILE_ES, 'r', encoding='utf-8') as fp:
@@ -603,7 +526,6 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
         found inside a {% trans %} tag and also in another file inside a
         {% blocktrans %} with a plural (#17375).
         """
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], extensions=['html', 'djtpl'], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -616,7 +538,6 @@ class CopyPluralFormsExtractorTests(ExtractorTests):
 class NoWrapExtractorTests(ExtractorTests):
 
     def test_no_wrap_enabled(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0, no_wrap=True)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -628,7 +549,6 @@ class NoWrapExtractorTests(ExtractorTests):
             )
 
     def test_no_wrap_disabled(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0, no_wrap=False)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -645,14 +565,12 @@ class LocationCommentsTests(ExtractorTests):
 
     def test_no_location_enabled(self):
         """Behavior is correct if --no-location switch is specified. See #16903."""
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0, no_location=True)
         self.assertTrue(os.path.exists(self.PO_FILE))
         self.assertLocationCommentNotPresent(self.PO_FILE, None, 'test.html')
 
     def test_no_location_disabled(self):
         """Behavior is correct if --no-location switch isn't specified."""
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0, no_location=False)
         self.assertTrue(os.path.exists(self.PO_FILE))
         # #16903 -- Standard comment with source file relative path should be present
@@ -663,7 +581,6 @@ class LocationCommentsTests(ExtractorTests):
         Ensure no leaky paths in comments, e.g. #: path\to\file.html.py:123
         Refs #21209/#26341.
         """
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE))
         with open(self.PO_FILE, 'r') as fp:
@@ -677,28 +594,16 @@ class KeepPotFileExtractorTests(ExtractorTests):
 
     POT_FILE = 'locale/django.pot'
 
-    def tearDown(self):
-        super(KeepPotFileExtractorTests, self).tearDown()
-        os.chdir(self.test_dir)
-        try:
-            os.unlink(self.POT_FILE)
-        except OSError:
-            pass
-        os.chdir(self._cwd)
-
     def test_keep_pot_disabled_by_default(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0)
         self.assertFalse(os.path.exists(self.POT_FILE))
 
     def test_keep_pot_explicitly_disabled(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0,
                                 keep_pot=False)
         self.assertFalse(os.path.exists(self.POT_FILE))
 
     def test_keep_pot_enabled(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=[LOCALE], verbosity=0,
                                 keep_pot=True)
         self.assertTrue(os.path.exists(self.POT_FILE))
@@ -709,18 +614,7 @@ class MultipleLocaleExtractionTests(ExtractorTests):
     PO_FILE_DE = 'locale/de/LC_MESSAGES/django.po'
     LOCALES = ['pt', 'de', 'ch']
 
-    def tearDown(self):
-        super(MultipleLocaleExtractionTests, self).tearDown()
-        os.chdir(self.test_dir)
-        for locale in self.LOCALES:
-            try:
-                self._rmrf('locale/%s' % locale)
-            except OSError:
-                pass
-        os.chdir(self._cwd)
-
     def test_multiple_locales(self):
-        os.chdir(self.test_dir)
         management.call_command('makemessages', locale=['pt', 'de'], verbosity=0)
         self.assertTrue(os.path.exists(self.PO_FILE_PT))
         self.assertTrue(os.path.exists(self.PO_FILE_DE))
@@ -728,11 +622,11 @@ class MultipleLocaleExtractionTests(ExtractorTests):
 
 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.
@@ -742,10 +636,8 @@ class ExcludedLocaleExtractionTests(ExtractorTests):
 
     def setUp(self):
         super(ExcludedLocaleExtractionTests, self).setUp()
-        os.chdir(self.test_dir)  # ExtractorTests.tearDown() takes care of restoring.
-        shutil.copytree('canned_locale', 'locale')
+        copytree('canned_locale', 'locale')
         self._set_times_for_all_po_files()
-        self.addCleanup(self._rmrf, os.path.join(self.test_dir, 'locale'))
 
     def test_command_help(self):
         with captured_stdout(), captured_stderr():
@@ -782,41 +674,32 @@ class ExcludedLocaleExtractionTests(ExtractorTests):
 
 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')]):
+            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)
diff --git a/tests/i18n/test_percents.py b/tests/i18n/test_percents.py
index 4ff8e71..8ce4a0b 100644
--- a/tests/i18n/test_percents.py
+++ b/tests/i18n/test_percents.py
@@ -9,7 +9,8 @@ from django.utils._os import upath
 from django.utils.encoding import force_text
 from django.utils.translation import activate, get_language, trans_real
 
-from .test_extraction import ExtractorTests
+from .utils 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 +32,7 @@ class FrenchTestCase(SimpleTestCase):
         activate(self._language)
 
 
-class ExtractingStringsWithPercentSigns(FrenchTestCase, ExtractorTests):
+class ExtractingStringsWithPercentSigns(POFileAssertionMixin, FrenchTestCase):
     """
     Tests the extracted string found in the gettext catalog.
 
diff --git a/tests/i18n/utils.py b/tests/i18n/utils.py
new file mode 100644
index 0000000..4fac02f
--- /dev/null
+++ b/tests/i18n/utils.py
@@ -0,0 +1,67 @@
+import os
+import re
+import shutil
+import tempfile
+
+from django.utils._os import upath
+
+
+source_code_dir = os.path.dirname(upath(__file__))
+
+
+def copytree(src, dst):
+    shutil.copytree(src, dst, ignore=shutil.ignore_patterns('*.pyc', '__pycache__'))
+
+
+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)
+
+
+class RunInTmpDirMixin(object):
+    """
+    Allow i18n tests that need to generate .po/.mo files to run in an isolated
+    temporary filesystem tree created by tempfile.mkdtemp() that contains a
+    clean copy of the relevant test code.
+
+    Test classes using this mixin need to define a `work_subdir` attribute
+    which designates the subdir under `tests/i18n/` that will be copied to the
+    temporary tree from which its test cases will run.
+
+    The setUp() method sets the current working dir to the temporary tree.
+    It'll be removed when cleaning up.
+    """
+
+    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))
+        copytree(os.path.join(source_code_dir, self.work_subdir), self.test_dir)
+        # Step out of the temporary working tree before removing it to avoid
+        # deletion problems on Windows. Cleanup actions registered with
+        # addCleanup() are called in reverse so preserve this ordering.
+        self.addCleanup(self._rmrf, self.test_dir)
+        self.addCleanup(os.chdir, self._cwd)
+        os.chdir(self.test_dir)
+
+    def _rmrf(self, dname):
+        if os.path.commonprefix([self.test_dir, os.path.abspath(dname)]) != self.test_dir:
+            return
+        shutil.rmtree(dname)
+
+    def rmfile(self, filepath):
+        if os.path.exists(filepath):
+            os.remove(filepath)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django.git



More information about the Python-modules-commits mailing list