diff --git a/tests/path.py b/tests/path.py
index 0d935fe4a..573d3d3ca 100755
--- a/tests/path.py
+++ b/tests/path.py
@@ -123,6 +123,9 @@ class path(text_type):
"""
os.unlink(self)
+ def utime(self, arg):
+ os.utime(self, arg)
+
def write_text(self, text, **kwargs):
"""
Writes the given `text` to the file.
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 4f0f3cc2b..bbcf93eba 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -16,15 +16,31 @@ import re
from subprocess import Popen, PIPE
from xml.etree import ElementTree
+from nose.tools import assert_equal, assert_in, assert_not_in
from six import string_types
-from util import tempdir, rootdir, path, with_app, SkipTest
+from util import tempdir, rootdir, path, gen_with_app, SkipTest
root = tempdir / 'test-intl'
-def with_intl_app(*args, **kw):
+def re_search(regex, text, flags=0):
+ if not re.search(regex, text, flags):
+ assert False, '%r did not match %r' % (regex, text)
+
+
+def not_re_search(regex, text, flags=0):
+ if re.search(regex, text, flags):
+ assert False, '%r did match %r' % (regex, text)
+
+
+def startswith(thing, prefix):
+ if not thing.startswith(prefix):
+ assert False, '%r does not start with %r' % (thing, prefix)
+
+
+def gen_with_intl_app(*args, **kw):
default_kw = {
'testroot': 'intl',
'confoverrides': {
@@ -33,7 +49,7 @@ def with_intl_app(*args, **kw):
},
}
default_kw.update(kw)
- return with_app(*args, **default_kw)
+ return gen_with_app(*args, **default_kw)
def setup_module():
@@ -97,126 +113,38 @@ def assert_elem(elem, texts=None, refs=None, names=None):
assert _names == names
-@with_intl_app(buildername='text')
-def test_simple(app, status, warning):
- app.builder.build(['bom'])
- result = (app.outdir / 'bom.txt').text(encoding='utf-8')
- expect = (u"\nDatei mit UTF-8"
- u"\n***************\n" # underline matches new translation
- u"\nThis file has umlauts: äöü.\n")
- assert result == expect
+@gen_with_intl_app('text', freshenv=True)
+def test_text_builder(app, status, warning):
+ app.builder.build_all()
-
-@with_intl_app(buildername='text')
-def test_subdir(app, status, warning):
- app.builder.build(['subdir/contents'])
- result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8')
- assert result.startswith(u"\nsubdir contents\n***************\n")
-
-
-@with_intl_app(buildername='text')
-def test_i18n_warnings_in_translation(app, status, warning):
- app.outdir.rmtree(True) # for warnings acceleration
- app.doctreedir.rmtree(True)
- app.builder.build(['warnings'])
- result = (app.outdir / 'warnings.txt').text(encoding='utf-8')
- expect = (u"\nI18N WITH REST WARNINGS"
- u"\n***********************\n"
- u"\nLINE OF >>``<
THIS IS THE SECOND PARAGRAPH OF DEPRECATED.
\n""") matched_content = get_content(result, "deprecated") - assert expect1 == matched_content + yield assert_equal, expect1, matched_content expect2 = ( u"""New in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONADDED.
\n""") matched_content = get_content(result, "versionadded") - assert expect2 == matched_content + yield assert_equal, expect2, matched_content expect3 = ( u"""Changed in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONCHANGED.
\n""") matched_content = get_content(result, "versionchanged") - assert expect3 == matched_content + yield assert_equal, expect3, matched_content + # --- docfields -@with_intl_app(buildername='text', freshenv=True) -def test_i18n_docfields(app, status, warning): - app.builder.build(['docfields']) - result = (app.outdir / 'docfields.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH DOCFIELDS" - u"\n*******************\n" - u"\nclass class Cls1\n" - u"\n Parameters:" - u"\n **param** -- DESCRIPTION OF PARAMETER param\n" - u"\nclass class Cls2\n" - u"\n Parameters:" - u"\n * **foo** -- DESCRIPTION OF PARAMETER foo\n" - u"\n * **bar** -- DESCRIPTION OF PARAMETER bar\n" - u"\nclass class Cls3(values)\n" - u"\n Raises ValueError:" - u"\n IF THE VALUES ARE OUT OF RANGE\n" - u"\nclass class Cls4(values)\n" - u"\n Raises:" - u"\n * **TypeError** -- IF THE VALUES ARE NOT VALID\n" - u"\n * **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n" - u"\nclass class Cls5\n" - u"\n Returns:" - u'\n A NEW "Cls3" INSTANCE\n') - assert result == expect - - -@with_intl_app(buildername='text', freshenv=True) -def test_i18n_admonitions(app, status, warning): - # #1206: gettext did not translate admonition directive's title - # seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions - app.builder.build(['admonitions']) - result = (app.outdir / 'admonitions.txt').text(encoding='utf-8') - directives = ( - "attention", "caution", "danger", "error", "hint", - "important", "note", "tip", "warning", "admonition",) - for d in directives: - assert d.upper() + " TITLE" in result - assert d.upper() + " BODY" in result - - -@with_intl_app(buildername='html', freshenv=True) -def test_i18n_docfields_html(app, status, warning): - app.builder.build(['docfields']) - (app.outdir / 'docfields.html').text(encoding='utf-8') # expect no error by build + (app.outdir / 'docfields.html').text(encoding='utf-8') + # --- gettext template -@with_intl_app(buildername='html') -def test_gettext_template(app, status, warning): - app.builder.build_all() result = (app.outdir / 'index.html').text(encoding='utf-8') - assert "WELCOME" in result - assert "SPHINX 2013.120" in result + yield assert_in, "WELCOME", result + yield assert_in, "SPHINX 2013.120", result + # --- rebuild by .mo mtime -@with_intl_app(buildername='html') -def test_rebuild_by_mo_mtime(app, status, warning): app.builder.build_update() _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) - assert count == 0 + yield assert_equal, count, 0 - mo = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').bytes() - (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').write_bytes(mo) + (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime(None) _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) - assert count == 1 + yield assert_equal, count, 1 + + +@gen_with_intl_app('xml', freshenv=True) +def test_xml_builder(app, status, warning): + app.builder.build_all() + + # --- footnotes: regression test for fix #955, #1176 + + et = ElementTree.parse(app.outdir / 'footnote.xml') + secs = et.findall('section') + + para0 = secs[0].findall('paragraph') + yield (assert_elem, + para0[0], + ['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS', + '2', '[ref]', '1', '100', '.'], + ['i18n-with-footnote', 'ref']) + + footnote0 = secs[0].findall('footnote') + yield (assert_elem, + footnote0[0], + ['1', 'THIS IS A AUTO NUMBERED FOOTNOTE.'], + None, + ['1']) + yield (assert_elem, + footnote0[1], + ['100', 'THIS IS A NUMBERED FOOTNOTE.'], + None, + ['100']) + yield (assert_elem, + footnote0[2], + ['2', 'THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'], + None, + ['named']) + + citation0 = secs[0].findall('citation') + yield (assert_elem, + citation0[0], + ['ref', 'THIS IS A NAMED FOOTNOTE.'], + None, + ['ref']) + + warnings = warning.getvalue().replace(os.sep, '/') + warning_expr = u'.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n' + yield not_re_search, warning_expr, warnings + + # --- footnote backlinks: i18n test for #1058 + + et = ElementTree.parse(app.outdir / 'footnote.xml') + secs = et.findall('section') + + para0 = secs[0].findall('paragraph') + refs0 = para0[0].findall('footnote_reference') + refid2id = dict([ + (r.attrib.get('refid'), r.attrib.get('ids')) for r in refs0]) + + footnote0 = secs[0].findall('footnote') + for footnote in footnote0: + ids = footnote.attrib.get('ids') + backrefs = footnote.attrib.get('backrefs') + yield assert_equal, refid2id[ids], backrefs + + # --- refs in the Python domain + + et = ElementTree.parse(app.outdir / 'refs_python_domain.xml') + secs = et.findall('section') + + # regression test for fix #1363 + para0 = secs[0].findall('paragraph') + yield (assert_elem, + para0[0], + ['SEE THIS DECORATOR:', 'sensitive_variables()', '.'], + ['sensitive.sensitive_variables']) + + # --- keep external links: regression test for #1044 + + et = ElementTree.parse(app.outdir / 'external_links.xml') + secs = et.findall('section') + + para0 = secs[0].findall('paragraph') + # external link check + yield (assert_elem, + para0[0], + ['EXTERNAL LINK TO', 'Python', '.'], + ['http://python.org/index.html']) + + # internal link check + yield (assert_elem, + para0[1], + ['EXTERNAL LINKS', 'IS INTERNAL LINK.'], + ['i18n-with-external-links']) + + # inline link check + yield (assert_elem, + para0[2], + ['INLINE LINK BY', 'THE SPHINX SITE', '.'], + ['http://sphinx-doc.org']) + + # unnamed link check + yield (assert_elem, + para0[3], + ['UNNAMED', 'LINK', '.'], + ['http://google.com']) + + # link target swapped translation + para1 = secs[1].findall('paragraph') + yield (assert_elem, + para1[0], + ['LINK TO', 'external2', 'AND', 'external1', '.'], + ['http://example.com/external2', + 'http://example.com/external1']) + yield (assert_elem, + para1[1], + ['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'], + ['http://python.org', 'http://sphinx-doc.org']) + + # multiple references in the same line + para2 = secs[2].findall('paragraph') + yield (assert_elem, + para2[0], + ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',', + 'THE SPHINX SITE', ',', 'UNNAMED', 'AND', + 'THE PYTHON SITE', '.'], + ['i18n-with-external-links', 'http://python.org/index.html', + 'http://sphinx-doc.org', 'http://google.com', + 'http://python.org']) + + # --- role xref: regression test for #1090, #1193 + + et = ElementTree.parse(app.outdir / 'role_xref.xml') + sec1, sec2 = et.findall('section') + + para1, = sec1.findall('paragraph') + yield (assert_elem, + para1, + ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',', + 'SOME NEW TERM', '.'], + ['i18n-role-xref', 'contents', + 'glossary_terms#term-some-term']) + + para2 = sec2.findall('paragraph') + yield (assert_elem, + para2[0], + ['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'], + ['glossary_terms#term-some-other-term', + 'glossary_terms#term-some-term']) + yield(assert_elem, + para2[1], + ['LINK TO', 'SAME TYPE LINKS', 'AND', + "I18N ROCK'N ROLE XREF", '.'], + ['same-type-links', 'i18n-role-xref']) + yield (assert_elem, + para2[2], + ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'], + ['glossary_terms', 'contents']) + yield (assert_elem, + para2[3], + ['LINK TO', '--module', 'AND', '-m', '.'], + ['cmdoption--module', 'cmdoption-m']) + yield (assert_elem, + para2[4], + ['LINK TO', 'env2', 'AND', 'env1', '.'], + ['envvar-env2', 'envvar-env1']) + yield (assert_elem, + para2[5], + ['LINK TO', 'token2', 'AND', 'token1', '.'], + []) # TODO: how do I link token role to productionlist? + yield (assert_elem, + para2[6], + ['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'], + ['same-type-links', 'i18n-role-xref']) + + # warnings + warnings = warning.getvalue().replace(os.sep, '/') + yield assert_not_in, 'term not in glossary', warnings + yield assert_not_in, 'undefined label', warnings + yield assert_not_in, 'unknown document', warnings + + # --- label targets: regression test for #1193, #1265 + + et = ElementTree.parse(app.outdir / 'label_target.xml') + secs = et.findall('section') + + para0 = secs[0].findall('paragraph') + yield (assert_elem, + para0[0], + ['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND', + 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'], + ['implicit-target', 'section-and-label']) + + para1 = secs[1].findall('paragraph') + yield (assert_elem, + para1[0], + ['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND', + 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1', + '.'], + ['explicit-target', 'id1']) + + para2 = secs[2].findall('paragraph') + yield (assert_elem, + para2[0], + ['X IMPLICIT SECTION NAME', 'POINT TO', + 'implicit-section-name', '.'], + ['implicit-section-name']) + + sec2 = secs[2].findall('section') + + para2_0 = sec2[0].findall('paragraph') + yield (assert_elem, + para2_0[0], + ['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'], + []) + + para3 = secs[3].findall('paragraph') + yield (assert_elem, + para3[0], + ['X', 'bridge label', + 'IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED ' + + 'SECTION TITLE.'], + ['label-bridged-target-section']) + yield (assert_elem, + para3[1], + ['X', 'bridge label', 'POINT TO', + 'LABEL BRIDGED TARGET SECTION', 'AND', 'bridge label2', + 'POINT TO', 'SECTION AND LABEL', '. THE SECOND APPEARED', + 'bridge label2', 'POINT TO CORRECT TARGET.'], + ['label-bridged-target-section', + 'section-and-label', + 'section-and-label'])