Complete test suite overhaul.

* rename a few test modules to make the names more consistent

* do not copy/use Sphinx from build/ (unnecessary without 2to3)

* use a temporary dir for *all* test projects, the source tree
  will stay pristine that way  (default is tests/build)

* speed up tests by ~3x by splitting up test projects and avoiding
  rebuilds
This commit is contained in:
Georg Brandl 2014-09-21 17:17:02 +02:00
parent c5dfd5c732
commit d47a7587f9
83 changed files with 1476 additions and 1757 deletions

View File

@ -7,6 +7,7 @@
^build/ ^build/
^dist/ ^dist/
^tests/.coverage ^tests/.coverage
^tests/build/
^sphinx/pycode/Grammar.*pickle ^sphinx/pycode/Grammar.*pickle
^Sphinx.egg-info/ ^Sphinx.egg-info/
^doc/_build/ ^doc/_build/
@ -18,5 +19,3 @@
~$ ~$
^utils/.*3\.py$ ^utils/.*3\.py$
^distribute- ^distribute-
^tests/root/_build/*
^tests/root/generated/*

View File

@ -48,10 +48,10 @@ reindent:
@$(PYTHON) utils/reindent.py -r -n . @$(PYTHON) utils/reindent.py -r -n .
endif endif
test: build test:
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
covertest: build covertest:
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \ @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \
--cover-package=sphinx $(TEST) --cover-package=sphinx $(TEST)

View File

@ -195,6 +195,9 @@ class path(text_type):
""" """
return self.__class__(os.path.join(self, *map(self.__class__, args))) return self.__class__(os.path.join(self, *map(self.__class__, args)))
def listdir(self):
return os.listdir(self)
__div__ = __truediv__ = joinpath __div__ = __truediv__ = joinpath
def __repr__(self): def __repr__(self):

View File

@ -3,11 +3,9 @@
import sys, os import sys, os
sys.path.append(os.path.abspath('.')) sys.path.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('..'))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo', extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo',
'sphinx.ext.coverage', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', 'sphinx.ext.doctest', 'sphinx.ext.extlinks',
'sphinx.ext.doctest', 'sphinx.ext.extlinks',
'sphinx.ext.viewcode', 'ext'] 'sphinx.ext.viewcode', 'ext']
jsmath_path = 'dummy.js' jsmath_path = 'dummy.js'
@ -18,7 +16,7 @@ master_doc = 'contents'
source_suffix = '.txt' source_suffix = '.txt'
project = 'Sphinx <Tests>' project = 'Sphinx <Tests>'
copyright = '2010, Georg Brandl & Team' copyright = '2010-2014, Georg Brandl & Team'
# If this is changed, remember to update the versionchanges! # If this is changed, remember to update the versionchanges!
version = '0.6' version = '0.6'
release = '0.6alpha1' release = '0.6alpha1'
@ -34,7 +32,8 @@ html_theme = 'testtheme'
html_theme_path = ['.'] html_theme_path = ['.']
html_theme_options = {'testopt': 'testoverride'} html_theme_options = {'testopt': 'testoverride'}
html_sidebars = {'**': 'customsb.html', html_sidebars = {'**': 'customsb.html',
'contents': ['contentssb.html', 'localtoc.html'] } 'contents': ['contentssb.html', 'localtoc.html',
'globaltoc.html']}
html_style = 'default.css' html_style = 'default.css'
html_static_path = ['_static', 'templated.css_t'] html_static_path = ['_static', 'templated.css_t']
html_extra_path = ['robots.txt'] html_extra_path = ['robots.txt']
@ -65,8 +64,6 @@ value_from_conf_py = 84
coverage_c_path = ['special/*.h'] coverage_c_path = ['special/*.h']
coverage_c_regexes = {'function': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'} coverage_c_regexes = {'function': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'}
autosummary_generate = ['autosummary']
extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '), extlinks = {'issue': ('http://bugs.python.org/issue%s', 'issue '),
'pyurl': ('http://python.org/%s', None)} 'pyurl': ('http://python.org/%s', None)}

View File

@ -21,15 +21,14 @@ Contents:
bom bom
math math
autodoc autodoc
autosummary
metadata metadata
extensions extensions
doctest
extensions extensions
versioning/index
footnote footnote
lists lists
http://sphinx-doc.org/
Latest reference <http://sphinx-doc.org/latest/>
Python <http://python.org/> Python <http://python.org/>
Indices and tables Indices and tables
@ -44,3 +43,13 @@ References
.. [Ref1] Reference target. .. [Ref1] Reference target.
.. [Ref_1] Reference target 2. .. [Ref_1] Reference target 2.
Test for issue #1157
====================
This used to crash:
.. toctree::
.. toctree::
:hidden:

View File

@ -0,0 +1,3 @@
:orphan:
here: »

View File

@ -1,3 +1,7 @@
import sys, os
sys.path.insert(0, os.path.abspath('.'))
extensions = ['sphinx.ext.autosummary'] extensions = ['sphinx.ext.autosummary']
# The suffix of source filenames. # The suffix of source filenames.

View File

@ -4,3 +4,4 @@
:toctree: :toctree:
dummy_module dummy_module
sphinx

View File

@ -0,0 +1,2 @@
master_doc = 'contents'
source_suffix = '.txt'

View File

@ -0,0 +1,8 @@
.. toctree::
maxwidth
lineblock
nonascii_title
nonascii_table
nonascii_maxwidth
table

View File

@ -0,0 +1,6 @@
* one
| line-block 1
| line-block 2
followed paragraph.

View File

@ -0,0 +1,6 @@
.. seealso:: ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham
* ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham
* ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham ham
spam egg

View File

@ -0,0 +1,5 @@
abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc
日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語 日本語
abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語 abc 日本語

View File

@ -0,0 +1,7 @@
.. list-table::
- - spam
- egg
- - 日本語
- 日本語

View File

@ -0,0 +1,2 @@
日本語
======

View File

@ -0,0 +1,7 @@
+-----+-----+
| XXX | XXX |
+-----+-----+
| | XXX |
+-----+-----+
| XXX | |
+-----+-----+

View File

View File

@ -0,0 +1,4 @@
.. toctree::
sub

View File

@ -0,0 +1,3 @@
.. toctree::
contents

View File

@ -1,22 +1,35 @@
Dedent Dedent
====== ======
Code blocks
-----------
.. code-block:: ruby
:linenos:
:dedent: 4
def ruby?
false
end
Literal Include Literal Include
--------------- ---------------
.. literalinclude:: literal.inc
:language: python
:lines: 10-11
:dedent: 0
.. literalinclude:: literal.inc
:language: python
:lines: 10-11
:dedent: 1
.. literalinclude:: literal.inc
:language: python
:lines: 10-11
:dedent: 2
.. literalinclude:: literal.inc
:language: python
:lines: 10-11
:dedent: 3
.. literalinclude:: literal.inc .. literalinclude:: literal.inc
:language: python :language: python
:lines: 10-11 :lines: 10-11
:dedent: 4 :dedent: 4
.. literalinclude:: literal.inc
:language: python
:lines: 10-11
:dedent: 1000

View File

@ -0,0 +1,53 @@
Dedent
======
Code blocks
-----------
.. code-block:: ruby
:linenos:
:dedent: 0
def ruby?
false
end
.. code-block:: ruby
:linenos:
:dedent: 1
def ruby?
false
end
.. code-block:: ruby
:linenos:
:dedent: 2
def ruby?
false
end
.. code-block:: ruby
:linenos:
:dedent: 3
def ruby?
false
end
.. code-block:: ruby
:linenos:
:dedent: 4
def ruby?
false
end
.. code-block:: ruby
:linenos:
:dedent: 1000
def ruby?
false
end

View File

@ -0,0 +1,5 @@
extensions = ['sphinx.ext.doctest']
project = 'test project for doctest'
master_doc = 'doctest.txt'
source_suffix = '.txt'

View File

@ -125,5 +125,5 @@ Special directives
.. testcleanup:: * .. testcleanup:: *
import test_doctest import test_ext_doctest
test_doctest.cleanup_call() test_ext_doctest.cleanup_call()

View File

@ -0,0 +1,5 @@
.. toctree::
:numbered:
sub

View File

@ -0,0 +1,3 @@
.. toctree::
contents

View File

@ -4,10 +4,4 @@ Autosummary templating test
.. autosummary:: .. autosummary::
:toctree: generated :toctree: generated
sphinx.application.Sphinx sphinx.application.TemplateBridge
.. currentmodule:: sphinx.application
.. autoclass:: TemplateBridge
.. automethod:: render

View File

@ -0,0 +1,3 @@
project = 'versioning test root'
master_doc = 'index'
source_suffix = '.txt'

View File

@ -11,47 +11,37 @@
""" """
from __future__ import print_function from __future__ import print_function
import os
import sys import sys
from os import path, chdir, listdir, environ import traceback
import shutil
from path import path
testroot = path.dirname(__file__) or '.' testroot = os.path.dirname(__file__) or '.'
if 'BUILD_TEST_PATH' in environ: sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir)))
# for tox testing
newroot = environ['BUILD_TEST_PATH']
# tox installs the sphinx package, no need for sys.path.insert
else:
newroot = path.join(testroot, path.pardir, 'build')
newroot = path.join(newroot, listdir(newroot)[0], 'tests')
shutil.rmtree(newroot, ignore_errors=True)
# just copying test directory to parallel testing
print('Copying sources to build/lib/tests...')
shutil.copytree(testroot, newroot)
# always test the sphinx package from build/lib/
sys.path.insert(0, path.abspath(path.join(newroot, path.pardir)))
# switch to the copy/converted dir so nose tests the right tests
chdir(newroot)
# check dependencies before testing
print('Checking dependencies...')
for modname in ('nose', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
'snowballstemmer', 'babel'):
try: try:
import nose __import__(modname)
except ImportError: except ImportError as err:
print('The nose package is needed to run the Sphinx test suite.') traceback.print_exc()
print('The %r package is needed to run the Sphinx test suite.' % modname)
sys.exit(1) sys.exit(1)
try: # find a temp dir for testing and clean it up now
import docutils os.environ['SPHINX_TEST_TEMPDIR'] = \
except ImportError: os.path.abspath(os.path.join(testroot, 'build')) \
print('Sphinx requires the docutils package to be installed.') if 'SPHINX_TEST_TEMPDIR' not in os.environ \
sys.exit(1) else os.path.abspath(os.environ['SPHINX_TEST_TEMPDIR'])
tempdir = path(os.environ['SPHINX_TEST_TEMPDIR'])
try: print('Temporary files will be placed in %s.' % tempdir)
import jinja2 if tempdir.exists():
except ImportError: tempdir.rmtree()
print('Sphinx requires the jinja2 package to be installed.') tempdir.makedirs()
sys.exit(1)
print('Running Sphinx test suite...') print('Running Sphinx test suite...')
import nose
nose.main() nose.main()

View File

@ -8,82 +8,57 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys import sys
from nose.tools import with_setup from util import with_app, rootdir
from util import with_app, test_roots
def setup_module(): def setup_module():
sys.path.insert(0, test_roots / 'test-api-set-translator') sys.path.insert(0, rootdir / 'roots' / 'test-api-set-translator')
def teardown_module(): def teardown_module():
sys.path.remove(test_roots / 'test-api-set-translator') sys.path.remove(rootdir / 'roots' / 'test-api-set-translator')
def teardown_websupport(): @with_app('html')
(test_roots / 'test-api-set-translator' / 'generated').rmtree(True) def test_html_translator(app, status, warning):
(test_roots / 'test-api-set-translator' / 'websupport').rmtree(True)
@with_app(
buildername='html',
srcdir=(test_roots / 'test-api-set-translator'),
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
)
def test_html_translator(app):
# no set_translator(), no html_translator_class # no set_translator(), no html_translator_class
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'SmartyPantsHTMLTranslator' assert translator_class.__name__ == 'SmartyPantsHTMLTranslator'
@with_app( @with_app('html', confoverrides={
buildername='html', 'html_translator_class': 'translator.ExtHTMLTranslator'})
srcdir=(test_roots / 'test-api-set-translator'), def test_html_with_html_translator_class(app, status, warning):
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
confoverrides={
'html_translator_class': 'translator.ExtHTMLTranslator'},
)
def test_html_with_html_translator_class(app):
# no set_translator(), but html_translator_class # no set_translator(), but html_translator_class
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ExtHTMLTranslator' assert translator_class.__name__ == 'ExtHTMLTranslator'
@with_app( @with_app('html',
buildername='html', confoverrides={'html_use_smartypants': False})
srcdir=(test_roots / 'test-api-set-translator'), def test_html_with_smartypants(app, status, warning):
confdir=(test_roots / 'test-api-set-translator' / 'nonext'),
confoverrides={'html_use_smartypants': False},
)
def test_html_with_smartypants(app):
# no set_translator(), html_use_smartypants=False # no set_translator(), html_use_smartypants=False
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'HTMLTranslator' assert translator_class.__name__ == 'HTMLTranslator'
@with_app( @with_app('html', testroot='api-set-translator')
buildername='html', def test_html_with_set_translator_for_html_(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_html_(app):
# use set_translator(), no html_translator_class # use set_translator(), no html_translator_class
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfHTMLTranslator' assert translator_class.__name__ == 'ConfHTMLTranslator'
@with_app( @with_app('html', testroot='api-set-translator',
buildername='html', confoverrides={'html_translator_class': 'ext.ExtHTMLTranslator'})
srcdir=(test_roots / 'test-api-set-translator'), def test_html_with_set_translator_for_html_and_html_translator_class(app, status, warning):
confoverrides={'html_translator_class': 'ext.ExtHTMLTranslator'},
)
def test_html_with_set_translator_for_html_and_html_translator_class(app):
# use set_translator() and html_translator_class. # use set_translator() and html_translator_class.
# set_translator() is given priority over html_translator_clas. # set_translator() is given priority over html_translator_clas.
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
@ -96,108 +71,70 @@ def test_html_with_set_translator_for_html_and_html_translator_class(app):
# buildername='dirhtml', # buildername='dirhtml',
# srcdir=(test_roots / 'test-api-set-translator'), # srcdir=(test_roots / 'test-api-set-translator'),
# ) # )
# def test_dirhtml_set_translator_for_dirhtml(app): # def test_dirhtml_set_translator_for_dirhtml(app, status, warning):
# translator_class = app.builder.translator_class # translator_class = app.builder.translator_class
# assert translator_class # assert translator_class
# assert translator_class.__name__ == 'ConfDirHTMLTranslator' # assert translator_class.__name__ == 'ConfDirHTMLTranslator'
@with_app( @with_app('singlehtml', testroot='api-set-translator')
buildername='singlehtml', def test_singlehtml_set_translator_for_singlehtml(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_singlehtml_set_translator_for_singlehtml(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfSingleHTMLTranslator' assert translator_class.__name__ == 'ConfSingleHTMLTranslator'
@with_app( @with_app('pickle', testroot='api-set-translator')
buildername='pickle', def test_pickle_set_translator_for_pickle(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_pickle_set_translator_for_pickle(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfPickleTranslator' assert translator_class.__name__ == 'ConfPickleTranslator'
@with_app( @with_app('json', testroot='api-set-translator')
buildername='json', def test_json_set_translator_for_json(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_json_set_translator_for_json(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfJsonTranslator' assert translator_class.__name__ == 'ConfJsonTranslator'
@with_app( @with_app('latex', testroot='api-set-translator')
buildername='latex', def test_html_with_set_translator_for_latex(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_latex(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfLaTeXTranslator' assert translator_class.__name__ == 'ConfLaTeXTranslator'
@with_app( @with_app('man', testroot='api-set-translator')
buildername='man', def test_html_with_set_translator_for_man(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_man(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfManualPageTranslator' assert translator_class.__name__ == 'ConfManualPageTranslator'
@with_app( @with_app('texinfo', testroot='api-set-translator')
buildername='texinfo', def test_html_with_set_translator_for_texinfo(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_texinfo(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfTexinfoTranslator' assert translator_class.__name__ == 'ConfTexinfoTranslator'
@with_app( @with_app('text', testroot='api-set-translator')
buildername='text', def test_html_with_set_translator_for_text(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_text(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfTextTranslator' assert translator_class.__name__ == 'ConfTextTranslator'
@with_setup(teardown=teardown_websupport) @with_app('xml', testroot='api-set-translator')
@with_app( def test_html_with_set_translator_for_xml(app, status, warning):
buildername='websupport',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_websupport(app):
translator_class = app.builder.translator_class
assert translator_class
assert translator_class.__name__ == 'ConfWebSupportTranslator'
@with_app(
buildername='xml',
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_xml(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfXMLTranslator' assert translator_class.__name__ == 'ConfXMLTranslator'
@with_app( @with_app('pseudoxml', testroot='api-set-translator')
buildername='pseudoxml', def test_html_with_set_translator_for_pseudoxml(app, status, warning):
srcdir=(test_roots / 'test-api-set-translator'),
)
def test_html_with_set_translator_for_pseudoxml(app):
translator_class = app.builder.translator_class translator_class = app.builder.translator_class
assert translator_class assert translator_class
assert translator_class.__name__ == 'ConfPseudoXMLTranslator' assert translator_class.__name__ == 'ConfPseudoXMLTranslator'

View File

@ -9,22 +9,21 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from six import StringIO
from docutils import nodes from docutils import nodes
from sphinx.application import ExtensionError from sphinx.application import ExtensionError
from sphinx.domains import Domain from sphinx.domains import Domain
from util import with_app, raises_msg, TestApp from util import with_app, raises_msg
@with_app() @with_app()
def test_events(app): def test_events(app, status, warning):
def empty(): pass def empty():
pass
raises_msg(ExtensionError, "Unknown event name: invalid", raises_msg(ExtensionError, "Unknown event name: invalid",
app.connect, "invalid", empty) app.connect, "invalid", empty)
app.add_event("my_event") app.add_event("my_event")
raises_msg(ExtensionError, "Event 'my_event' already present", raises_msg(ExtensionError, "Event 'my_event' already present",
app.add_event, "my_event") app.add_event, "my_event")
@ -43,15 +42,13 @@ def test_events(app):
@with_app() @with_app()
def test_emit_with_nonascii_name_node(app): def test_emit_with_nonascii_name_node(app, status, warning):
node = nodes.section(names=[u'\u65e5\u672c\u8a9e']) node = nodes.section(names=[u'\u65e5\u672c\u8a9e'])
app.emit('my_event', node) app.emit('my_event', node)
def test_output(): @with_app()
status, warnings = StringIO(), StringIO() def test_output(app, status, warning):
app = TestApp(status=status, warning=warnings)
try:
status.truncate(0) # __init__ writes to status status.truncate(0) # __init__ writes to status
status.seek(0) status.seek(0)
app.info("Nothing here...") app.info("Nothing here...")
@ -63,31 +60,27 @@ def test_output():
old_count = app._warncount old_count = app._warncount
app.warn("Bad news!") app.warn("Bad news!")
assert warnings.getvalue() == "WARNING: Bad news!\n" assert warning.getvalue() == "WARNING: Bad news!\n"
assert app._warncount == old_count + 1 assert app._warncount == old_count + 1
finally:
app.cleanup()
def test_extensions(): @with_app()
status, warnings = StringIO(), StringIO() def test_extensions(app, status, warning):
app = TestApp(status=status, warning=warnings)
try:
app.setup_extension('shutil') app.setup_extension('shutil')
assert warnings.getvalue().startswith("WARNING: extension 'shutil'") assert warning.getvalue().startswith("WARNING: extension 'shutil'")
finally:
app.cleanup()
def test_domain_override():
@with_app()
def test_domain_override(app, status, warning):
class A(Domain): class A(Domain):
name = 'foo' name = 'foo'
class B(A): class B(A):
name = 'foo' name = 'foo'
class C(Domain): class C(Domain):
name = 'foo' name = 'foo'
status, warnings = StringIO(), StringIO()
app = TestApp(status=status, warning=warnings)
try:
# No domain know named foo. # No domain know named foo.
raises_msg(ExtensionError, 'domain foo not yet registered', raises_msg(ExtensionError, 'domain foo not yet registered',
app.override_domain, A) app.override_domain, A)
@ -95,5 +88,3 @@ def test_domain_override():
assert app.override_domain(B) is None assert app.override_domain(B) is None
raises_msg(ExtensionError, 'new domain not a subclass of registered ' raises_msg(ExtensionError, 'new domain not a subclass of registered '
'foo domain', app.override_domain, C) 'foo domain', app.override_domain, C)
finally:
app.cleanup()

View File

@ -3,114 +3,86 @@
test_build test_build
~~~~~~~~~~ ~~~~~~~~~~
Test all builders that have no special checks. Test all builders.
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from util import with_app, test_root, path, SkipTest, TestApp from six import BytesIO
from textwrap import dedent from textwrap import dedent
from util import with_app, rootdir, tempdir, SkipTest, TestApp
try: try:
from docutils.writers.manpage import Writer as ManWriter from docutils.writers.manpage import Writer as ManWriter
except ImportError: except ImportError:
ManWriter = None ManWriter = None
def teardown_module(): class MockOpener(object):
(test_root / '_build').rmtree(True) def open(self, req, **kwargs):
class result(BytesIO):
headers = None
url = req.url
return result()
import sphinx.builders.linkcheck
sphinx.builders.linkcheck.opener = MockOpener()
def test_build(): def verify_build(buildername, srcdir):
for buildername in ('pickle', 'json', 'linkcheck', 'text', 'htmlhelp', if buildername == 'man' and ManWriter is None:
'qthelp', 'epub', 'changes', 'singlehtml', 'xml', raise SkipTest('man writer is not available')
'pseudoxml'): app = TestApp(buildername=buildername, srcdir=srcdir)
app = TestApp(buildername=buildername) try:
yield lambda app: app.builder.build_all(), app app.builder.build_all()
finally:
app.cleanup() app.cleanup()
@with_app(buildername='man') def test_build_all():
def test_man(app): # If supported, build in a non-ASCII source dir
if ManWriter is None: test_name = u'\u65e5\u672c\u8a9e'
raise SkipTest('man writer is not available')
app.builder.build_all()
assert (app.outdir / 'SphinxTests.1').exists()
def _test_nonascii_path(app):
srcdir = path(app.srcdir)
mb_name = u'\u65e5\u672c\u8a9e'
try: try:
(srcdir / mb_name).makedirs() srcdir = tempdir / test_name
(rootdir / 'root').copytree(tempdir / test_name)
except UnicodeEncodeError: except UnicodeEncodeError:
from path import FILESYSTEMENCODING srcdir = tempdir / 'all'
raise SkipTest( else:
'nonascii filename not supported on this filesystem encoding: ' # add a doc with a non-ASCII file name to the source dir
'%s', FILESYSTEMENCODING) (srcdir / (test_name + '.txt')).write_text(dedent("""
nonascii file name page
(srcdir / mb_name / (mb_name + '.txt')).write_text(dedent(""" =======================
multi byte file name page
==========================
""")) """))
master_doc = srcdir / 'contents.txt' master_doc = srcdir / 'contents.txt'
master_doc.write_bytes((master_doc.text() + dedent(""" master_doc.write_bytes((master_doc.text() + dedent("""
.. toctree:: .. toctree::
%(mb_name)s/%(mb_name)s %(test_name)s/%(test_name)s
""" % {'mb_name': mb_name}) """ % {'test_name': test_name})
).encode('utf-8')) ).encode('utf-8'))
# note: no 'html' - if it's ok with dirhtml it's ok with html
for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo',
'pickle', 'json', 'text', 'htmlhelp', 'qthelp', 'epub',
'changes', 'xml', 'pseudoxml', 'man', 'linkcheck']:
yield verify_build, buildername, srcdir
@with_app(buildername='text', testroot='circular')
def test_circular_toctree(app, status, warning):
app.builder.build_all() app.builder.build_all()
warnings = warning.getvalue()
def test_nonascii_path():
(test_root / '_build').rmtree(True) #keep this to build first gettext
builder_names = ['gettext', 'html', 'dirhtml', 'singlehtml', 'latex',
'texinfo', 'pickle', 'json', 'linkcheck', 'text',
'htmlhelp', 'qthelp', 'epub', 'changes', 'xml',
'pseudoxml']
if ManWriter is not None:
builder_names.append('man')
for buildername in builder_names:
app = TestApp(buildername=buildername, _copy_to_temp=True)
yield _test_nonascii_path, app
app.cleanup()
@with_app(buildername='text', srcdir='(empty)')
def test_circular_toctree(app):
contents = (".. toctree::\n"
"\n"
" sub\n")
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
contents = (".. toctree::\n"
"\n"
" contents\n")
(app.srcdir / 'sub.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
warnings = "".join(app._warning.content)
assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings
assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings
@with_app(buildername='text', srcdir='(empty)') @with_app(buildername='text', testroot='numbered-circular')
def test_numbered_circular_toctree(app): def test_numbered_circular_toctree(app, status, warning):
contents = (".. toctree::\n"
" :numbered:\n"
"\n"
" sub\n")
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
contents = (".. toctree::\n"
"\n"
" contents\n")
(app.srcdir / 'sub.rst').write_text(contents, encoding='utf-8')
app.builder.build_all() app.builder.build_all()
warnings = "\n".join(app._warning.content) warnings = warning.getvalue()
assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings
assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings

View File

@ -15,22 +15,17 @@ import os
import re import re
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from util import test_root, test_roots, with_app, SkipTest from util import with_app, SkipTest
def teardown_module(): @with_app('gettext')
(test_root / '_build').rmtree(True) def test_all(app, status, warning):
(test_roots / 'test-intl' / '_build').rmtree(True),
@with_app(buildername='gettext')
def test_all(app):
# Generic build; should fail only when the builder is horribly broken. # Generic build; should fail only when the builder is horribly broken.
app.builder.build_all() app.builder.build_all()
@with_app(buildername='gettext') @with_app('gettext')
def test_build(app): def test_build(app, status, warning):
# Do messages end up in the correct location? # Do messages end up in the correct location?
app.builder.build(['extapi', 'subdir/includes']) app.builder.build(['extapi', 'subdir/includes'])
# top-level documents end up in a message catalog # top-level documents end up in a message catalog
@ -39,16 +34,16 @@ def test_build(app):
assert (app.outdir / 'subdir.pot').isfile() assert (app.outdir / 'subdir.pot').isfile()
@with_app(buildername='gettext') @with_app('gettext')
def test_seealso(app): def test_seealso(app, status, warning):
# regression test for issue #960 # regression test for issue #960
app.builder.build(['markup']) app.builder.build(['markup'])
catalog = (app.outdir / 'markup.pot').text(encoding='utf-8') catalog = (app.outdir / 'markup.pot').text(encoding='utf-8')
assert 'msgid "something, something else, something more"' in catalog assert 'msgid "something, something else, something more"' in catalog
@with_app(buildername='gettext') @with_app('gettext')
def test_gettext(app): def test_gettext(app, status, warning):
app.builder.build(['markup']) app.builder.build(['markup'])
(app.outdir / 'en' / 'LC_MESSAGES').makedirs() (app.outdir / 'en' / 'LC_MESSAGES').makedirs()
@ -91,15 +86,14 @@ def test_gettext(app):
assert _("Testing various markup") == u"Testing various markup" assert _("Testing various markup") == u"Testing various markup"
@with_app(buildername='gettext', @with_app('gettext', testroot='intl',
srcdir=(test_roots / 'test-intl'),
doctreedir=(test_roots / 'test-intl' / '_build' / 'doctree'),
confoverrides={'gettext_compact': False}) confoverrides={'gettext_compact': False})
def test_gettext_index_entries(app): def test_gettext_index_entries(app, status, warning):
# regression test for #976 # regression test for #976
app.builder.build(['index_entries']) app.builder.build(['index_entries'])
_msgid_getter = re.compile(r'msgid "(.*)"').search _msgid_getter = re.compile(r'msgid "(.*)"').search
def msgid_getter(msgid): def msgid_getter(msgid):
m = _msgid_getter(msgid) m = _msgid_getter(msgid)
if m: if m:
@ -139,10 +133,8 @@ def test_gettext_index_entries(app):
assert msgids == [] assert msgids == []
@with_app(buildername='gettext', @with_app(buildername='gettext', testroot='intl')
srcdir=(test_roots / 'test-intl'), def test_gettext_template(app, status, warning):
doctreedir=(test_roots / 'test-intl' / '_build' / 'doctree'))
def test_gettext_template(app):
app.builder.build_all() app.builder.build_all()
assert (app.outdir / 'sphinx.pot').isfile() assert (app.outdir / 'sphinx.pot').isfile()

View File

@ -15,22 +15,11 @@ import re
from six import PY3, iteritems, StringIO from six import PY3, iteritems, StringIO
from six.moves import html_entities from six.moves import html_entities
try:
import pygments
except ImportError:
pygments = None
from sphinx import __version__ from sphinx import __version__
from util import test_root, test_roots, remove_unicode_literals, gen_with_app, with_app from util import remove_unicode_literals, gen_with_app
from etree13 import ElementTree as ET from etree13 import ElementTree as ET
def teardown_module():
(test_root / '_build').rmtree(True)
html_warnfile = StringIO()
ENV_WARNINGS = """\ ENV_WARNINGS = """\
%(root)s/autodoc_fodder.py:docstring of autodoc_fodder\\.MarkupError:2: \ %(root)s/autodoc_fodder.py:docstring of autodoc_fodder\\.MarkupError:2: \
WARNING: Explicit markup ends without a blank line; unexpected \ WARNING: Explicit markup ends without a blank line; unexpected \
@ -44,6 +33,8 @@ reading included file u'.*?wrongenc.inc' seems to be wrong, try giving an \
%(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png %(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png
%(root)s/markup.txt:\\d+: WARNING: Malformed :option: u'Python c option', does \ %(root)s/markup.txt:\\d+: WARNING: Malformed :option: u'Python c option', does \
not contain option marker - or -- or / or \\+ not contain option marker - or -- or / or \\+
%(root)s/undecodable.txt:3: WARNING: undecodable source characters, replacing \
with "\\?": b?'here: >>>\\\\xbb<<<'
""" """
HTML_WARNINGS = ENV_WARNINGS + """\ HTML_WARNINGS = ENV_WARNINGS + """\
@ -61,6 +52,7 @@ if PY3:
def tail_check(check): def tail_check(check):
rex = re.compile(check) rex = re.compile(check)
def checker(nodes): def checker(nodes):
for node in nodes: for node in nodes:
if node.tail and rex.search(node.tail): if node.tail and rex.search(node.tail):
@ -84,6 +76,8 @@ HTML_XPATH = {
(".//a[@href='../_downloads/img.png']", ''), (".//a[@href='../_downloads/img.png']", ''),
(".//img[@src='../_images/img.png']", ''), (".//img[@src='../_images/img.png']", ''),
(".//p", 'This is an include file.'), (".//p", 'This is an include file.'),
(".//pre/span", 'line 1'),
(".//pre/span", 'line 2'),
], ],
'includes.html': [ 'includes.html': [
(".//pre", u'Max Strauß'), (".//pre", u'Max Strauß'),
@ -91,6 +85,23 @@ HTML_XPATH = {
(".//a[@href='_downloads/img1.png']", ''), (".//a[@href='_downloads/img1.png']", ''),
(".//pre", u'"quotes"'), (".//pre", u'"quotes"'),
(".//pre", u"'included'"), (".//pre", u"'included'"),
(".//pre/span[@class='s']", u'üöä'),
(".//div[@class='inc-pyobj1 highlight-text']//pre",
r'^class Foo:\n pass\n\s*$'),
(".//div[@class='inc-pyobj2 highlight-text']//pre",
r'^ def baz\(\):\n pass\n\s*$'),
(".//div[@class='inc-lines highlight-text']//pre",
r'^class Foo:\n pass\nclass Bar:\n$'),
(".//div[@class='inc-startend highlight-text']//pre",
u'^foo = "Including Unicode characters: üöä"\\n$'),
(".//div[@class='inc-preappend highlight-text']//pre",
r'(?m)^START CODE$'),
(".//div[@class='inc-pyobj-dedent highlight-python']//span",
r'def'),
(".//div[@class='inc-tab3 highlight-text']//pre",
r'-| |-'),
(".//div[@class='inc-tab8 highlight-python']//pre/span",
r'-| |-'),
], ],
'autodoc.html': [ 'autodoc.html': [
(".//dt[@id='test_autodoc.Class']", ''), (".//dt[@id='test_autodoc.Class']", ''),
@ -215,12 +226,10 @@ HTML_XPATH = {
(".//h4", 'Custom sidebar'), (".//h4", 'Custom sidebar'),
# docfields # docfields
(".//td[@class='field-body']/strong", '^moo$'), (".//td[@class='field-body']/strong", '^moo$'),
(".//td[@class='field-body']/strong", (".//td[@class='field-body']/strong", tail_check(r'\(Moo\) .* Moo')),
tail_check(r'\(Moo\) .* Moo')),
(".//td[@class='field-body']/ul/li/strong", '^hour$'), (".//td[@class='field-body']/ul/li/strong", '^hour$'),
(".//td[@class='field-body']/ul/li/em", '^DuplicateType$'), (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
(".//td[@class='field-body']/ul/li/em", (".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')),
tail_check(r'.* Some parameter')),
], ],
'contents.html': [ 'contents.html': [
(".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc'][@content='hcval']", ''),
@ -241,6 +250,11 @@ HTML_XPATH = {
(".//h4", 'Contents sidebar'), (".//h4", 'Contents sidebar'),
# custom JavaScript # custom JavaScript
(".//script[@src='file://moo.js']", ''), (".//script[@src='file://moo.js']", ''),
# URL in contents
(".//a[@class='reference external'][@href='http://sphinx-doc.org/']",
'http://sphinx-doc.org/'),
(".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']",
'Latest reference'),
], ],
'bom.html': [ 'bom.html': [
(".//title", " File with UTF-8 BOM"), (".//title", " File with UTF-8 BOM"),
@ -260,33 +274,19 @@ HTML_XPATH = {
(".//a/strong", "Other"), (".//a/strong", "Other"),
(".//a", "entry"), (".//a", "entry"),
(".//dt/a", "double"), (".//dt/a", "double"),
] ],
'footnote.html': [
(".//a[@class='footnote-reference'][@href='#id5'][@id='id1']", r"\[1\]"),
(".//a[@class='footnote-reference'][@href='#id6'][@id='id2']", r"\[2\]"),
(".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
(".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
(".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
(".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
(".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
(".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
],
} }
if pygments:
HTML_XPATH['includes.html'].extend([
(".//pre/span[@class='s']", u'üöä'),
(".//div[@class='inc-pyobj1 highlight-text']//pre",
r'^class Foo:\n pass\n\s*$'),
(".//div[@class='inc-pyobj2 highlight-text']//pre",
r'^ def baz\(\):\n pass\n\s*$'),
(".//div[@class='inc-lines highlight-text']//pre",
r'^class Foo:\n pass\nclass Bar:\n$'),
(".//div[@class='inc-startend highlight-text']//pre",
u'^foo = "Including Unicode characters: üöä"\\n$'),
(".//div[@class='inc-preappend highlight-text']//pre",
r'(?m)^START CODE$'),
(".//div[@class='inc-pyobj-dedent highlight-python']//span",
r'def'),
(".//div[@class='inc-tab3 highlight-text']//pre",
r'-| |-'),
(".//div[@class='inc-tab8 highlight-python']//pre/span",
r'-| |-'),
])
HTML_XPATH['subdir/includes.html'].extend([
(".//pre/span", 'line 1'),
(".//pre/span", 'line 2'),
])
class NslessParser(ET.XMLParser): class NslessParser(ET.XMLParser):
"""XMLParser that throws away namespaces in tag names.""" """XMLParser that throws away namespaces in tag names."""
@ -322,6 +322,7 @@ def check_xpath(etree, fname, path, check, be_found=True):
'path %s in %s: %r' % (check, path, fname, 'path %s in %s: %r' % (check, path, fname,
[node.text for node in nodes])) [node.text for node in nodes]))
def check_static_entries(outdir): def check_static_entries(outdir):
staticdir = outdir / '_static' staticdir = outdir / '_static'
assert staticdir.isdir() assert staticdir.isdir()
@ -335,15 +336,17 @@ def check_static_entries(outdir):
# a file from _static, but matches exclude_patterns # a file from _static, but matches exclude_patterns
assert not (staticdir / 'excluded.css').exists() assert not (staticdir / 'excluded.css').exists()
def check_extra_entries(outdir): def check_extra_entries(outdir):
assert (outdir / 'robots.txt').isfile() assert (outdir / 'robots.txt').isfile()
@gen_with_app(buildername='html', warning=html_warnfile, cleanenv=True,
@gen_with_app(buildername='html', freshenv=True,
confoverrides={'html_context.hckey_co': 'hcval_co'}, confoverrides={'html_context.hckey_co': 'hcval_co'},
tags=['testtag']) tags=['testtag'])
def test_html(app): def test_html_output(app, status, warning):
app.builder.build_all() app.builder.build_all()
html_warnings = html_warnfile.getvalue().replace(os.sep, '/') html_warnings = warning.getvalue().replace(os.sep, '/')
html_warnings_exp = HTML_WARNINGS % { html_warnings_exp = HTML_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(html_warnings_exp + '$', html_warnings), \ assert re.match(html_warnings_exp + '$', html_warnings), \
@ -365,23 +368,9 @@ def test_html(app):
check_static_entries(app.builder.outdir) check_static_entries(app.builder.outdir)
check_extra_entries(app.builder.outdir) check_extra_entries(app.builder.outdir)
@with_app(buildername='html', srcdir='(empty)',
confoverrides={'html_sidebars': {'*': ['globaltoc.html']}},
)
def test_html_with_globaltoc_and_hidden_toctree(app):
# issue #1157: combination of 'globaltoc.html' and hidden toctree cause
# exception.
(app.srcdir / 'contents.rst').write_text(
'\n.. toctree::'
'\n'
'\n.. toctree::'
'\n :hidden:'
'\n')
app.builder.build_all()
@gen_with_app(buildername='html', testroot='tocdepth')
@gen_with_app(buildername='html', srcdir=(test_roots / 'test-tocdepth')) def test_tocdepth(app, status, warning):
def test_tocdepth(app):
# issue #1251 # issue #1251
app.builder.build_all() app.builder.build_all()
@ -423,8 +412,8 @@ def test_tocdepth(app):
yield check_xpath, etree, fname, xpath, check, be_found yield check_xpath, etree, fname, xpath, check, be_found
@gen_with_app(buildername='singlehtml', srcdir=(test_roots / 'test-tocdepth')) @gen_with_app(buildername='singlehtml', testroot='tocdepth')
def test_tocdepth_singlehtml(app): def test_tocdepth_singlehtml(app, status, warning):
app.builder.build_all() app.builder.build_all()
expects = { expects = {
@ -466,18 +455,3 @@ def test_tocdepth_singlehtml(app):
for xpath, check, be_found in paths: for xpath, check, be_found in paths:
yield check_xpath, etree, fname, xpath, check, be_found yield check_xpath, etree, fname, xpath, check, be_found
@with_app(buildername='html', srcdir='(empty)')
def test_url_in_toctree(app):
contents = (".. toctree::\n"
"\n"
" http://sphinx-doc.org/\n"
" Latest reference <http://sphinx-doc.org/latest/>\n")
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert '<a class="reference external" href="http://sphinx-doc.org/">http://sphinx-doc.org/</a>' in result
assert '<a class="reference external" href="http://sphinx-doc.org/latest/">Latest reference</a>' in result

View File

@ -14,20 +14,14 @@ import os
import re import re
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from six import PY3, StringIO from six import PY3
from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.latex import LaTeXTranslator
from util import test_root, SkipTest, remove_unicode_literals, with_app from util import SkipTest, remove_unicode_literals, with_app
from test_build_html import ENV_WARNINGS from test_build_html import ENV_WARNINGS
def teardown_module():
(test_root / '_build').rmtree(True)
latex_warnfile = StringIO()
LATEX_WARNINGS = ENV_WARNINGS + """\ LATEX_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: citation not found: missing None:None: WARNING: citation not found: missing
None:None: WARNING: no matching candidate for image URI u'foo.\\*' None:None: WARNING: no matching candidate for image URI u'foo.\\*'
@ -39,11 +33,11 @@ if PY3:
LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS)
@with_app(buildername='latex', warning=latex_warnfile, cleanenv=True) @with_app(buildername='latex', freshenv=True)
def test_latex(app): def test_latex(app, status, warning):
LaTeXTranslator.ignore_missing_images = True LaTeXTranslator.ignore_missing_images = True
app.builder.build_all() app.builder.build_all()
latex_warnings = latex_warnfile.getvalue().replace(os.sep, '/') latex_warnings = warning.getvalue().replace(os.sep, '/')
latex_warnings_exp = LATEX_WARNINGS % { latex_warnings_exp = LATEX_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(latex_warnings_exp + '$', latex_warnings), \ assert re.match(latex_warnings_exp + '$', latex_warnings), \

View File

@ -14,20 +14,14 @@ import os
import re import re
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from six import PY3, StringIO from six import PY3
from sphinx.writers.texinfo import TexinfoTranslator from sphinx.writers.texinfo import TexinfoTranslator
from util import test_root, SkipTest, remove_unicode_literals, with_app from util import SkipTest, remove_unicode_literals, with_app
from test_build_html import ENV_WARNINGS from test_build_html import ENV_WARNINGS
def teardown_module():
(test_root / '_build').rmtree(True)
texinfo_warnfile = StringIO()
TEXINFO_WARNINGS = ENV_WARNINGS + """\ TEXINFO_WARNINGS = ENV_WARNINGS + """\
None:None: WARNING: citation not found: missing None:None: WARNING: citation not found: missing
None:None: WARNING: no matching candidate for image URI u'foo.\\*' None:None: WARNING: no matching candidate for image URI u'foo.\\*'
@ -38,11 +32,11 @@ if PY3:
TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS)
@with_app(buildername='texinfo', warning=texinfo_warnfile, cleanenv=True) @with_app('texinfo', freshenv=True)
def test_texinfo(app): def test_texinfo(app, status, warning):
TexinfoTranslator.ignore_missing_images = True TexinfoTranslator.ignore_missing_images = True
app.builder.build_all() app.builder.build_all()
texinfo_warnings = texinfo_warnfile.getvalue().replace(os.sep, '/') texinfo_warnings = warning.getvalue().replace(os.sep, '/')
texinfo_warnings_exp = TEXINFO_WARNINGS % { texinfo_warnings_exp = TEXINFO_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))} 'root': re.escape(app.srcdir.replace(os.sep, '/'))}
assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \ assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \

View File

@ -18,29 +18,16 @@ from util import with_app
def with_text_app(*args, **kw): def with_text_app(*args, **kw):
default_kw = { default_kw = {
'buildername': 'text', 'buildername': 'text',
'srcdir': '(empty)', 'testroot': 'build-text',
'confoverrides': {
'project': 'text',
'master_doc': 'contents',
},
} }
default_kw.update(kw) default_kw.update(kw)
return with_app(*args, **default_kw) return with_app(*args, **default_kw)
@with_text_app() @with_text_app()
def test_maxwitdh_with_prefix(app): def test_maxwitdh_with_prefix(app, status, warning):
long_string = u' '.join([u"ham"] * 30) app.builder.build_update()
contents = ( result = (app.outdir / 'maxwidth.txt').text(encoding='utf-8')
u".. seealso:: %(long_string)s\n\n"
u"* %(long_string)s\n"
u"* %(long_string)s\n"
u"\nspam egg\n"
) % locals()
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
lines = result.splitlines() lines = result.splitlines()
line_widths = [column_width(line) for line in lines] line_widths = [column_width(line) for line in lines]
@ -58,21 +45,10 @@ def test_maxwitdh_with_prefix(app):
@with_text_app() @with_text_app()
def test_lineblock(app): def test_lineblock(app, status, warning):
# regression test for #1109: need empty line after line block # regression test for #1109: need empty line after line block
contents = ( app.builder.build_update()
u"* one\n" result = (app.outdir / 'lineblock.txt').text(encoding='utf-8')
u"\n"
u" | line-block 1\n"
u" | line-block 2\n"
u"\n"
u"followed paragraph.\n"
)
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
expect = ( expect = (
u"* one\n" u"* one\n"
u"\n" u"\n"
@ -81,82 +57,40 @@ def test_lineblock(app):
u"\n" u"\n"
u"followed paragraph.\n" u"followed paragraph.\n"
) )
assert result == expect assert result == expect
@with_text_app() @with_text_app()
def test_nonascii_title_line(app): def test_nonascii_title_line(app, status, warning):
title = u'\u65e5\u672c\u8a9e' app.builder.build_update()
underline = u'=' * column_width(title) result = (app.outdir / 'nonascii_title.txt').text(encoding='utf-8')
content = u'\n'.join((title, underline, u'')) expect_underline = '******'
(app.srcdir / 'contents.rst').write_text(content, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
expect_underline = underline.replace('=', '*')
result_underline = result.splitlines()[2].strip() result_underline = result.splitlines()[2].strip()
assert expect_underline == result_underline assert expect_underline == result_underline
@with_text_app() @with_text_app()
def test_nonascii_table(app): def test_nonascii_table(app, status, warning):
text = u'\u65e5\u672c\u8a9e' app.builder.build_update()
contents = (u"\n.. list-table::" result = (app.outdir / 'nonascii_table.txt').text(encoding='utf-8')
"\n"
"\n - - spam"
"\n - egg"
"\n"
"\n - - %(text)s"
"\n - %(text)s"
"\n" % locals())
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
lines = [line.strip() for line in result.splitlines() if line.strip()] lines = [line.strip() for line in result.splitlines() if line.strip()]
line_widths = [column_width(line) for line in lines] line_widths = [column_width(line) for line in lines]
assert len(set(line_widths)) == 1 # same widths assert len(set(line_widths)) == 1 # same widths
@with_text_app() @with_text_app()
def test_nonascii_maxwidth(app): def test_nonascii_maxwidth(app, status, warning):
sb_text = u'abc' #length=3 app.builder.build_update()
mb_text = u'\u65e5\u672c\u8a9e' #length=3 result = (app.outdir / 'nonascii_maxwidth.txt').text(encoding='utf-8')
sb_line = ' '.join([sb_text] * int(MAXWIDTH / 3))
mb_line = ' '.join([mb_text] * int(MAXWIDTH / 3))
mix_line = ' '.join([sb_text, mb_text] * int(MAXWIDTH / 6))
contents = u'\n\n'.join((sb_line, mb_line, mix_line))
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
lines = [line.strip() for line in result.splitlines() if line.strip()] lines = [line.strip() for line in result.splitlines() if line.strip()]
line_widths = [column_width(line) for line in lines] line_widths = [column_width(line) for line in lines]
assert max(line_widths) < MAXWIDTH assert max(line_widths) < MAXWIDTH
@with_text_app() @with_text_app()
def test_table_with_empty_cell(app): def test_table_with_empty_cell(app, status, warning):
contents = (u""" app.builder.build_update()
+-----+-----+ result = (app.outdir / 'table.txt').text(encoding='utf-8')
| XXX | XXX |
+-----+-----+
| | XXX |
+-----+-----+
| XXX | |
+-----+-----+
""")
(app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
app.builder.build_all()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
lines = [line.strip() for line in result.splitlines() if line.strip()] lines = [line.strip() for line in result.splitlines() if line.strip()]
assert lines[0] == "+-------+-------+" assert lines[0] == "+-------+-------+"
assert lines[1] == "| XXX | XXX |" assert lines[1] == "| XXX | XXX |"

View File

@ -12,16 +12,17 @@ import shutil
from nose.tools import with_setup from nose.tools import with_setup
from util import test_roots, with_app, find_files from util import with_app, find_files, rootdir, tempdir
root = test_roots / 'test-intl' root = tempdir / 'test-intl'
build_dir = root / '_build' build_dir = root / '_build'
locale_dir = build_dir / 'locale' locale_dir = build_dir / 'locale'
def setup_test(): def setup_test():
# Delete remnants left over after failed build # delete remnants left over after failed build
locale_dir.rmtree(True) root.rmtree(True)
(rootdir / 'roots' / 'test-intl').copytree(root)
# copy all catalogs into locale layout directory # copy all catalogs into locale layout directory
for po in find_files(root, '.po'): for po in find_files(root, '.po'):
copy_po = (locale_dir / 'en' / 'LC_MESSAGES' / po) copy_po = (locale_dir / 'en' / 'LC_MESSAGES' / po)
@ -31,13 +32,13 @@ def setup_test():
def teardown_test(): def teardown_test():
build_dir.rmtree(True), build_dir.rmtree(True)
@with_setup(setup_test, teardown_test) @with_setup(setup_test, teardown_test)
@with_app(buildername='html', srcdir=root, @with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]}) confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_all_catalogs(app): def test_compile_all_catalogs(app, status, warning):
app.builder.compile_all_catalogs() app.builder.compile_all_catalogs()
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
@ -51,9 +52,9 @@ def test_compile_all_catalogs(app):
@with_setup(setup_test, teardown_test) @with_setup(setup_test, teardown_test)
@with_app(buildername='html', srcdir=root, @with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]}) confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_specific_catalogs(app): def test_compile_specific_catalogs(app, status, warning):
app.builder.compile_specific_catalogs(['admonitions']) app.builder.compile_specific_catalogs(['admonitions'])
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
@ -62,9 +63,9 @@ def test_compile_specific_catalogs(app):
@with_setup(setup_test, teardown_test) @with_setup(setup_test, teardown_test)
@with_app(buildername='html', srcdir=root, @with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]}) confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_update_catalogs(app): def test_compile_update_catalogs(app, status, warning):
app.builder.compile_update_catalogs() app.builder.compile_update_catalogs()
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES' catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'

View File

@ -20,7 +20,7 @@ from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
@with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True', @with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True',
'latex_elements.docclass': 'scrartcl', 'latex_elements.docclass': 'scrartcl',
'modindex_common_prefix': 'path1,path2'}) 'modindex_common_prefix': 'path1,path2'})
def test_core_config(app): def test_core_config(app, status, warning):
cfg = app.config cfg = app.config
# simple values # simple values
@ -36,7 +36,7 @@ def test_core_config(app):
# simple default values # simple default values
assert 'locale_dirs' not in cfg.__dict__ assert 'locale_dirs' not in cfg.__dict__
assert cfg.locale_dirs == [] assert cfg.locale_dirs == []
assert cfg.trim_footnote_reference_space == False assert cfg.trim_footnote_reference_space is False
# complex default values # complex default values
assert 'html_title' not in cfg.__dict__ assert 'html_title' not in cfg.__dict__
@ -68,7 +68,7 @@ def test_core_config(app):
@with_app() @with_app()
def test_extension_values(app): def test_extension_values(app, status, warning):
cfg = app.config cfg = app.config
# default value # default value

View File

@ -12,17 +12,11 @@
import re import re
from xml.etree import ElementTree from xml.etree import ElementTree
from util import with_app, test_roots from util import with_app
def teardown_module(): @with_app('xml', testroot='directive-code')
(test_roots / 'test-directive-code' / '_build').rmtree(True) def test_code_block(app, status, warning):
@with_app(buildername='xml',
srcdir=(test_roots / 'test-directive-code'),
_copy_to_temp=True)
def test_code_block(app):
app.builder.build('index') app.builder.build('index')
et = ElementTree.parse(app.outdir / 'index.xml') et = ElementTree.parse(app.outdir / 'index.xml')
secs = et.findall('./section/section') secs = et.findall('./section/section')
@ -37,34 +31,14 @@ def test_code_block(app):
assert actual == expect assert actual == expect
@with_app(buildername='xml', @with_app('xml', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_code_block_dedent(app, status, warning):
_copy_to_temp=True) app.builder.build(['dedent_code'])
def test_code_block_dedent(app): et = ElementTree.parse(app.outdir / 'dedent_code.xml')
outdir = app.outdir blocks = et.findall('./section/section/literal_block')
def get_dedent_actual(dedent):
dedent_text = (app.srcdir / 'dedent.rst').text(encoding='utf-8')
dedent_text = re.sub(
r':dedent: \d', ':dedent: %d' % dedent, dedent_text)
(app.srcdir / 'dedent.rst').write_text(dedent_text, encoding='utf-8')
# use another output dir to force rebuild
app.outdir = outdir / str(dedent)
app._init_env(freshenv=True)
app._init_builder(app.builder.name)
app.builder.build(['dedent'], method='specific')
et = ElementTree.parse(app.outdir / 'dedent.xml')
secs = et.findall('./section/section')
code_block = secs[0].findall('literal_block')
assert len(code_block) > 0
actual = code_block[0].text
return actual
for i in range(5): # 0-4 for i in range(5): # 0-4
actual = get_dedent_actual(i) actual = blocks[i].text
indent = " " * (4 - i) indent = " " * (4 - i)
expect = ( expect = (
indent + "def ruby?\n" + indent + "def ruby?\n" +
@ -73,36 +47,29 @@ def test_code_block_dedent(app):
) )
assert (i, actual) == (i, expect) assert (i, actual) == (i, expect)
actual = get_dedent_actual(1000) assert blocks[5].text == '\n\n' # dedent: 1000
assert actual == '\n\n'
@with_app(buildername='html', @with_app('html', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_code_block_caption_html(app, status, warning):
_copy_to_temp=True) app.builder.build(['caption'])
def test_code_block_caption_html(app):
app.builder.build('index')
html = (app.outdir / 'caption.html').text() html = (app.outdir / 'caption.html').text()
caption = '<div class="code-block-caption"><code>caption-test.rb</code></div>' caption = '<div class="code-block-caption"><code>caption-test.rb</code></div>'
assert caption in html assert caption in html
@with_app(buildername='latex', @with_app('latex', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_code_block_caption_latex(app, status, warning):
_copy_to_temp=True) app.builder.build_all()
def test_code_block_caption_latex(app):
app.builder.build('index')
latex = (app.outdir / 'Python.tex').text() latex = (app.outdir / 'Python.tex').text()
caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]' caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]'
'{\\small\\texttt{caption-test.rb}}}}') '{\\small\\texttt{caption-test.rb}}}}')
assert caption in latex assert caption in latex
@with_app(buildername='xml', @with_app('xml', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_literal_include(app, status, warning):
_copy_to_temp=True) app.builder.build(['index'])
def test_literal_include(app):
app.builder.build('index')
et = ElementTree.parse(app.outdir / 'index.xml') et = ElementTree.parse(app.outdir / 'index.xml')
secs = et.findall('./section/section') secs = et.findall('./section/section')
literal_include = secs[1].findall('literal_block') literal_include = secs[1].findall('literal_block')
@ -112,60 +79,34 @@ def test_literal_include(app):
assert actual == literal_src assert actual == literal_src
@with_app(buildername='xml', @with_app('xml', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_literal_include_dedent(app, status, warning):
_copy_to_temp=True)
def test_literal_include_dedent(app):
outdir = app.outdir
literal_src = (app.srcdir / 'literal.inc').text(encoding='utf-8') literal_src = (app.srcdir / 'literal.inc').text(encoding='utf-8')
literal_lines = [l[4:] for l in literal_src.split('\n')[9:11]] literal_lines = [l[4:] for l in literal_src.split('\n')[9:11]]
def get_dedent_actual(dedent):
dedent_text = (app.srcdir / 'dedent.rst').text(encoding='utf-8')
dedent_text = re.sub(
r':dedent: \d', ':dedent: %d' % dedent, dedent_text)
(app.srcdir / 'dedent.rst').write_text(dedent_text, encoding='utf-8')
# use another output dir to force rebuild
app.outdir = outdir / str(dedent)
app._init_env(freshenv=True)
app._init_builder(app.builder.name)
app.builder.build(['dedent']) app.builder.build(['dedent'])
et = ElementTree.parse(app.outdir / 'dedent.xml') et = ElementTree.parse(app.outdir / 'dedent.xml')
secs = et.findall('./section/section') blocks = et.findall('./section/section/literal_block')
literal_include = secs[1].findall('literal_block')
assert len(literal_include) > 0
actual = literal_include[0].text
return actual
for i in range(5): # 0-4 for i in range(5): # 0-4
actual = get_dedent_actual(i) actual = blocks[i].text
indent = " " * (4 - i) indent = ' ' * (4 - i)
expect = '\n'.join(indent + l for l in literal_lines) + '\n' expect = '\n'.join(indent + l for l in literal_lines) + '\n'
assert (i, actual) == (i, expect) assert (i, actual) == (i, expect)
assert blocks[5].text == '\n\n' # dedent: 1000
actual = get_dedent_actual(1000)
assert actual == '\n\n'
@with_app(buildername='html', @with_app('html', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_literalinclude_caption_html(app, status, warning):
_copy_to_temp=True)
def test_literalinclude_caption_html(app):
app.builder.build('index') app.builder.build('index')
html = (app.outdir / 'caption.html').text() html = (app.outdir / 'caption.html').text()
caption = '<div class="code-block-caption"><code>caption-test.py</code></div>' caption = '<div class="code-block-caption"><code>caption-test.py</code></div>'
assert caption in html assert caption in html
@with_app(buildername='latex', @with_app('latex', testroot='directive-code')
srcdir=(test_roots / 'test-directive-code'), def test_literalinclude_caption_latex(app, status, warning):
_copy_to_temp=True)
def test_literalinclude_caption_latex(app):
app.builder.build('index') app.builder.build('index')
latex = (app.outdir / 'Python.tex').text() latex = (app.outdir / 'Python.tex').text()
caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]' caption = ('{\\colorbox[rgb]{0.9,0.9,0.9}{\\makebox[\\textwidth][l]'

View File

@ -13,15 +13,11 @@ import re
from docutils import nodes from docutils import nodes
from util import with_app, test_roots from util import with_app
def teardown_module(): @with_app('text', testroot='directive-only')
(test_roots / 'test-directive-only' / '_build').rmtree(True) def test_sectioning(app, status, warning):
@with_app(buildername='text', srcdir=(test_roots / 'test-directive-only'))
def test_sectioning(app):
def getsects(section): def getsects(section):
if not isinstance(section, nodes.section): if not isinstance(section, nodes.section):

View File

@ -9,50 +9,17 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
import re import re
from functools import wraps
from six import StringIO from util import with_app, path, SkipTest
from util import test_roots, TestApp, path, SkipTest
html_warnfile = StringIO()
root = test_roots / 'test-docutilsconf'
# need cleanenv to rebuild everytime.
# docutils.conf change did not effect to rebuild.
def with_conf_app(docutilsconf='', *args, **kwargs):
default_kw = {
'srcdir': root,
'cleanenv': True,
}
default_kw.update(kwargs)
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
app = TestApp(*args, **default_kw)
(app.srcdir / 'docutils.conf').write_text(docutilsconf)
try:
cwd = os.getcwd()
os.chdir(app.srcdir)
func(app, *args2, **kwargs2)
finally:
os.chdir(cwd)
# don't execute cleanup if test failed
app.cleanup()
return deco
return generator
def regex_count(expr, result): def regex_count(expr, result):
return len(re.findall(expr, result)) return len(re.findall(expr, result))
@with_conf_app(buildername='html') @with_app('html', testroot='docutilsconf', freshenv=True, docutilsconf='')
def test_html_with_default_docutilsconf(app): def test_html_with_default_docutilsconf(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8') result = (app.outdir / 'contents.html').text(encoding='utf-8')
@ -62,13 +29,13 @@ def test_html_with_default_docutilsconf(app):
assert regex_count(r'<td class="option-group" colspan="2">', result) == 1 assert regex_count(r'<td class="option-group" colspan="2">', result) == 1
@with_conf_app(buildername='html', docutilsconf=( @with_app('html', testroot='docutilsconf', freshenv=True, docutilsconf=(
'\n[html4css1 writer]' '\n[html4css1 writer]'
'\noption-limit:1' '\noption-limit:1'
'\nfield-name-limit:1' '\nfield-name-limit:1'
'\n') '\n')
) )
def test_html_with_docutilsconf(app): def test_html_with_docutilsconf(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8') result = (app.outdir / 'contents.html').text(encoding='utf-8')
@ -78,41 +45,32 @@ def test_html_with_docutilsconf(app):
assert regex_count(r'<td class="option-group" colspan="2">', result) == 2 assert regex_count(r'<td class="option-group" colspan="2">', result) == 2
@with_conf_app(buildername='html', warning=html_warnfile) @with_app('html', testroot='docutilsconf')
def test_html(app): def test_html(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
assert html_warnfile.getvalue() == '' assert warning.getvalue() == ''
@with_conf_app(buildername='latex', warning=html_warnfile) @with_app('latex', testroot='docutilsconf')
def test_latex(app): def test_latex(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
assert html_warnfile.getvalue() == '' assert warning.getvalue() == ''
@with_conf_app(buildername='man', warning=html_warnfile) @with_app('man', testroot='docutilsconf')
def test_man(app): def test_man(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
assert html_warnfile.getvalue() == '' assert warning.getvalue() == ''
@with_conf_app(buildername='texinfo', warning=html_warnfile) @with_app('texinfo', testroot='docutilsconf')
def test_texinfo(app): def test_texinfo(app, status, warning):
app.builder.build(['contents']) app.builder.build(['contents'])
@with_conf_app(buildername='html', srcdir='(empty)', @with_app('html', testroot='docutilsconf',
docutilsconf='[general]\nsource_link=true\n') docutilsconf='[general]\nsource_link=true\n')
def test_docutils_source_link(app): def test_docutils_source_link_with_nonascii_file(app, status, warning):
srcdir = path(app.srcdir)
(srcdir / 'conf.py').write_text('')
(srcdir / 'contents.rst').write_text('')
app.builder.build_all()
@with_conf_app(buildername='html', srcdir='(empty)',
docutilsconf='[general]\nsource_link=true\n')
def test_docutils_source_link_with_nonascii_file(app):
srcdir = path(app.srcdir) srcdir = path(app.srcdir)
mb_name = u'\u65e5\u672c\u8a9e' mb_name = u'\u65e5\u672c\u8a9e'
try: try:
@ -123,7 +81,4 @@ def test_docutils_source_link_with_nonascii_file(app):
'nonascii filename not supported on this filesystem encoding: ' 'nonascii filename not supported on this filesystem encoding: '
'%s', FILESYSTEMENCODING) '%s', FILESYSTEMENCODING)
(srcdir / 'conf.py').write_text('')
(srcdir / 'contents.rst').write_text('')
app.builder.build_all() app.builder.build_all()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
test_py_domain test_domain_py
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
Tests the Python Domain Tests the Python Domain

View File

@ -8,9 +8,12 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys
from six import PY3 from six import PY3
from util import TestApp, remove_unicode_literals, path, with_app from util import TestApp, remove_unicode_literals, path
from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder from sphinx.builders.latex import LaTeXBuilder
@ -18,21 +21,25 @@ from sphinx.builders.latex import LaTeXBuilder
app = env = None app = env = None
warnings = [] warnings = []
def setup_module(): def setup_module():
global app, env global app, env
app = TestApp(freshenv=True, _copy_to_temp=True) app = TestApp(srcdir='env-test')
env = app.env env = app.env
env.set_warnfunc(lambda *args: warnings.append(args)) env.set_warnfunc(lambda *args: warnings.append(args))
def teardown_module(): def teardown_module():
app.cleanup() app.cleanup()
def warning_emitted(file, text): def warning_emitted(file, text):
for warning in warnings: for warning in warnings:
if len(warning) == 2 and file in warning[1] and text in warning[0]: if len(warning) == 2 and file in warning[1] and text in warning[0]:
return True return True
return False return False
# Tests are run in the order they appear in the file, therefore we can # Tests are run in the order they appear in the file, therefore we can
# afford to not run update() in the setup but in its own test # afford to not run update() in the setup but in its own test
@ -46,6 +53,7 @@ def test_first_update():
# test if exclude_patterns works ok # test if exclude_patterns works ok
assert 'subdir/excluded' not in env.found_docs assert 'subdir/excluded' not in env.found_docs
def test_images(): def test_images():
assert warning_emitted('images', 'image file not readable: foo.png') assert warning_emitted('images', 'image file not readable: foo.png')
assert warning_emitted('images', 'nonlocal image URI found: ' assert warning_emitted('images', 'nonlocal image URI found: '
@ -75,6 +83,7 @@ def test_images():
assert set(latexbuilder.images.values()) == \ assert set(latexbuilder.images.values()) == \
set(['img.pdf', 'img.png', 'img1.png', 'simg.png', 'svgimg.pdf']) set(['img.pdf', 'img.png', 'img1.png', 'simg.png', 'svgimg.pdf'])
def test_second_update(): def test_second_update():
# delete, add and "edit" (change saved mtime) some files and update again # delete, add and "edit" (change saved mtime) some files and update again
env.all_docs['contents'] = 0 env.all_docs['contents'] = 0
@ -96,19 +105,6 @@ def test_second_update():
assert 'autodoc' not in env.found_docs assert 'autodoc' not in env.found_docs
@with_app(srcdir='(empty)')
def test_undecodable_source_reading_emit_warnings(app):
# issue #1524
warnings[:] = []
app.env.set_warnfunc(lambda *args: warnings.append(args))
(app.srcdir / 'contents.rst').write_bytes(b'1\xbb2')
_, _, it = app.env.update(app.config, app.srcdir, app.doctreedir, app)
list(it) # the generator does all the work
assert warning_emitted(
'contents', 'undecodable source characters, replacing with "?":'
)
def test_object_inventory(): def test_object_inventory():
refs = env.domaindata['py']['objects'] refs = env.domaindata['py']['objects']

View File

@ -8,49 +8,24 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys
from functools import wraps
from six import iteritems, StringIO from six import iteritems, StringIO
from sphinx.ext.autosummary import mangle_signature from sphinx.ext.autosummary import mangle_signature
from util import test_roots, TestApp from util import with_app
html_warnfile = StringIO() html_warnfile = StringIO()
def with_autosummary_app(*args, **kw):
default_kw = { default_kw = {
'srcdir': (test_roots / 'test-autosummary'), 'testroot': 'autosummary',
'confoverrides': { 'confoverrides': {
'extensions': ['sphinx.ext.autosummary'], 'extensions': ['sphinx.ext.autosummary'],
'autosummary_generate': True, 'autosummary_generate': True,
'source_suffix': '.rst' 'source_suffix': '.rst'
} }
} }
default_kw.update(kw)
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
# Now, modify the python path...
srcdir = default_kw['srcdir']
sys.path.insert(0, srcdir)
try:
app = TestApp(*args, **default_kw)
func(app, *args2, **kwargs2)
finally:
if srcdir in sys.path:
sys.path.remove(srcdir)
# remove the auto-generated dummy_module.rst
dummy_rst = srcdir / 'dummy_module.rst'
if dummy_rst.isfile():
dummy_rst.unlink()
# don't execute cleanup if test failed
app.cleanup()
return deco
return generator
def test_mangle_signature(): def test_mangle_signature():
@ -79,10 +54,8 @@ def test_mangle_signature():
assert res == outp, (u"'%s' -> '%s' != '%s'" % (inp, res, outp)) assert res == outp, (u"'%s' -> '%s' != '%s'" % (inp, res, outp))
@with_autosummary_app(buildername='html', warning=html_warnfile) @with_app(buildername='html', **default_kw)
def test_get_items_summary(app): def test_get_items_summary(app, status, warning):
app.builddir.rmtree(True)
# monkey-patch Autosummary.get_items so we can easily get access to it's # monkey-patch Autosummary.get_items so we can easily get access to it's
# results.. # results..
import sphinx.ext.autosummary import sphinx.ext.autosummary
@ -96,13 +69,17 @@ def test_get_items_summary(app):
autosummary_items[name] = result autosummary_items[name] = result
return results return results
def handler(app, what, name, obj, options, lines):
assert isinstance(lines, list)
app.connect('autodoc-process-docstring', handler)
sphinx.ext.autosummary.Autosummary.get_items = new_get_items sphinx.ext.autosummary.Autosummary.get_items = new_get_items
try: try:
app.builder.build_all() app.builder.build_all()
finally: finally:
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
html_warnings = html_warnfile.getvalue() html_warnings = warning.getvalue()
assert html_warnings == '' assert html_warnings == ''
expected_values = { expected_values = {
@ -118,13 +95,3 @@ def test_get_items_summary(app):
for key, expected in iteritems(expected_values): for key, expected in iteritems(expected_values):
assert autosummary_items[key][2] == expected, 'Summary for %s was %r -'\ assert autosummary_items[key][2] == expected, 'Summary for %s was %r -'\
' expected %r' % (key, autosummary_items[key], expected) ' expected %r' % (key, autosummary_items[key], expected)
@with_autosummary_app(buildername='html')
def test_process_doc_event(app):
app.builddir.rmtree(True)
def handler(app, what, name, obj, options, lines):
assert isinstance(lines, list)
app.connect('autodoc-process-docstring', handler)
app.builder.build_all()

View File

@ -15,7 +15,7 @@ from util import with_app
@with_app(buildername='coverage') @with_app(buildername='coverage')
def test_build(app): def test_build(app, status, warning):
app.builder.build_all() app.builder.build_all()
py_undoc = (app.outdir / 'python.txt').text() py_undoc = (app.outdir / 'python.txt').text()

View File

@ -12,26 +12,24 @@ from __future__ import print_function
import sys import sys
from six import StringIO
from util import with_app from util import with_app
status = StringIO()
cleanup_called = 0 cleanup_called = 0
@with_app(buildername='doctest', status=status)
def test_build(app): @with_app(buildername='doctest', testroot='doctest')
def test_build(app, status, warning):
global cleanup_called global cleanup_called
cleanup_called = 0 cleanup_called = 0
app.builder.build_all() app.builder.build_all()
if app.statuscode != 0: if app.statuscode != 0:
print(status.getvalue(), file=sys.stderr) assert False, 'failures in doctests:' + status.getvalue()
assert False, 'failures in doctests'
# in doctest.txt, there are two named groups and the default group, # in doctest.txt, there are two named groups and the default group,
# so the cleanup function must be called three times # so the cleanup function must be called three times
assert cleanup_called == 3, 'testcleanup did not get executed enough times' assert cleanup_called == 3, 'testcleanup did not get executed enough times'
def cleanup_call(): def cleanup_call():
global cleanup_called global cleanup_called
cleanup_called += 1 cleanup_called += 1

View File

@ -80,7 +80,7 @@ def test_read_inventory_v2():
@with_app() @with_app()
@with_tempdir @with_tempdir
def test_missing_reference(tempdir, app): def test_missing_reference(tempdir, app, status, warning):
inv_file = tempdir / 'inventory' inv_file = tempdir / 'inventory'
inv_file.write_bytes(inventory_v2) inv_file.write_bytes(inventory_v2)
app.config.intersphinx_mapping = { app.config.intersphinx_mapping = {
@ -156,7 +156,7 @@ def test_missing_reference(tempdir, app):
@with_app() @with_app()
@with_tempdir @with_tempdir
def test_load_mappings_warnings(tempdir, app): def test_load_mappings_warnings(tempdir, app, status, warning):
""" """
load_mappings issues a warning if new-style mapping load_mappings issues a warning if new-style mapping
identifiers are not alphanumeric identifiers are not alphanumeric
@ -174,4 +174,4 @@ def test_load_mappings_warnings(tempdir, app):
app.config.intersphinx_cache_limit = 0 app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly # load the inventory and check if it's done correctly
load_mappings(app) load_mappings(app)
assert len(app._warning.content) == 2 assert warning.getvalue().count('\n') == 2

View File

@ -9,13 +9,12 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
from util import with_app from util import with_app
@with_app(buildername='html', tags=['test_linkcode'], _copy_to_temp=True) @with_app('html', tags=['test_linkcode'])
def test_html(app): def test_html(app, status, warning):
app.builder.build_all() app.builder.build(['objects'])
stuff = (app.outdir / 'objects.html').text(encoding='utf-8') stuff = (app.outdir / 'objects.html').text(encoding='utf-8')

View File

@ -11,25 +11,14 @@
import re import re
from six import StringIO from util import with_app
from util import test_roots, with_app
warnfile = StringIO() @with_app(testroot='ext-viewcode')
root = test_roots / 'test-ext-viewcode' def test_simple(app, status, warning):
doctreedir = root / '_build' / 'doctree'
def teardown_module():
(root / '_build').rmtree(True)
@with_app(srcdir=root, warning=warnfile)
def test_simple(app):
app.builder.build_all() app.builder.build_all()
warnings = re.sub(r'\\+', '/', warnfile.getvalue()) warnings = re.sub(r'\\+', '/', warning.getvalue())
assert re.findall( assert re.findall(
r"index.rst:\d+: WARNING: Object named 'func1' not found in include " + r"index.rst:\d+: WARNING: Object named 'func1' not found in include " +
r"file .*/spam/__init__.py'", r"file .*/spam/__init__.py'",

View File

@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
"""
test_footnote
~~~~~~~~~~~~~
Test for footnote and citation.
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from util import test_root, with_app
def teardown_module():
(test_root / '_build').rmtree(True)
@with_app(buildername='html')
def test_html(app):
app.builder.build(['footnote'])
result = (app.outdir / 'footnote.html').text(encoding='utf-8')
expects = [
'<a class="footnote-reference" href="#id5" id="id1">[1]</a>',
'<a class="footnote-reference" href="#id6" id="id2">[2]</a>',
'<a class="footnote-reference" href="#foo" id="id3">[3]</a>',
'<a class="reference internal" href="#bar" id="id4">[bar]</a>',
'<a class="fn-backref" href="#id1">[1]</a>',
'<a class="fn-backref" href="#id2">[2]</a>',
'<a class="fn-backref" href="#id3">[3]</a>',
'<a class="fn-backref" href="#id4">[bar]</a>',
]
for expect in expects:
matches = re.findall(re.escape(expect), result)
assert len(matches) == 1

View File

@ -15,12 +15,7 @@ from pygments.formatters.html import HtmlFormatter
from sphinx.highlighting import PygmentsBridge from sphinx.highlighting import PygmentsBridge
from util import with_app, SkipTest from util import with_app
try:
import pygments
except ImportError:
raise SkipTest('pygments not available')
class MyLexer(RegexLexer): class MyLexer(RegexLexer):
@ -46,13 +41,14 @@ class ComplainOnUnhighlighted(PygmentsBridge):
@with_app() @with_app()
def test_add_lexer(app): def test_add_lexer(app, status, warning):
app.add_lexer('test', MyLexer()) app.add_lexer('test', MyLexer())
bridge = PygmentsBridge('html') bridge = PygmentsBridge('html')
ret = bridge.highlight_block('ab', 'test') ret = bridge.highlight_block('ab', 'test')
assert '<span class="n">a</span>b' in ret assert '<span class="n">a</span>b' in ret
def test_detect_interactive(): def test_detect_interactive():
bridge = ComplainOnUnhighlighted('html') bridge = ComplainOnUnhighlighted('html')
blocks = [ blocks = [
@ -65,6 +61,7 @@ def test_detect_interactive():
ret = bridge.highlight_block(block.lstrip(), 'python') ret = bridge.highlight_block(block.lstrip(), 'python')
assert ret.startswith("<div class=\"highlight\">") assert ret.startswith("<div class=\"highlight\">")
def test_set_formatter(): def test_set_formatter():
PygmentsBridge.html_formatter = MyFormatter PygmentsBridge.html_formatter = MyFormatter
try: try:
@ -74,6 +71,7 @@ def test_set_formatter():
finally: finally:
PygmentsBridge.html_formatter = HtmlFormatter PygmentsBridge.html_formatter = HtmlFormatter
def test_trim_doctest_flags(): def test_trim_doctest_flags():
PygmentsBridge.html_formatter = MyFormatter PygmentsBridge.html_formatter = MyFormatter
try: try:

View File

@ -13,5 +13,5 @@ from util import with_app
@with_app(confoverrides={'language': 'de'}) @with_app(confoverrides={'language': 'de'})
def test_i18n(app): def test_i18n(app, status, warning):
app.builder.build_all() app.builder.build_all()

View File

@ -16,20 +16,17 @@ import re
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from xml.etree import ElementTree from xml.etree import ElementTree
from six import StringIO, string_types from six import string_types
from util import test_roots, path, with_app, SkipTest from util import tempdir, rootdir, path, with_app, SkipTest
warnfile = StringIO() root = tempdir / 'test-intl'
root = test_roots / 'test-intl'
doctreedir = root / '_build' / 'doctree'
def with_intl_app(*args, **kw): def with_intl_app(*args, **kw):
default_kw = { default_kw = {
'srcdir': root, 'testroot': 'intl',
'doctreedir': doctreedir,
'confoverrides': { 'confoverrides': {
'language': 'xx', 'locale_dirs': ['.'], 'language': 'xx', 'locale_dirs': ['.'],
'gettext_compact': False, 'gettext_compact': False,
@ -40,9 +37,9 @@ def with_intl_app(*args, **kw):
def setup_module(): def setup_module():
if not root.exists():
(rootdir / 'roots' / 'test-intl').copytree(root)
# Delete remnants left over after failed build # Delete remnants left over after failed build
(root / 'xx').rmtree(True)
(root / 'xx' / 'LC_MESSAGES').makedirs()
# Compile all required catalogs into binary format (*.mo). # Compile all required catalogs into binary format (*.mo).
for dirpath, dirs, files in os.walk(root): for dirpath, dirs, files in os.walk(root):
dirpath = path(dirpath) dirpath = path(dirpath)
@ -67,11 +64,6 @@ def setup_module():
assert mo.isfile(), 'msgfmt failed' assert mo.isfile(), 'msgfmt failed'
def teardown_module():
(root / '_build').rmtree(True)
(root / 'xx').rmtree(True)
def elem_gettexts(elem): def elem_gettexts(elem):
def itertext(self): def itertext(self):
# this function copied from Python-2.7 'ElementTree.itertext'. # this function copied from Python-2.7 'ElementTree.itertext'.
@ -106,7 +98,7 @@ def assert_elem(elem, texts=None, refs=None, names=None):
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_simple(app): def test_simple(app, status, warning):
app.builder.build(['bom']) app.builder.build(['bom'])
result = (app.outdir / 'bom.txt').text(encoding='utf-8') result = (app.outdir / 'bom.txt').text(encoding='utf-8')
expect = (u"\nDatei mit UTF-8" expect = (u"\nDatei mit UTF-8"
@ -116,15 +108,16 @@ def test_simple(app):
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_subdir(app): def test_subdir(app, status, warning):
app.builder.build(['subdir/contents']) app.builder.build(['subdir/contents'])
result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8') result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8')
assert result.startswith(u"\nsubdir contents\n***************\n") assert result.startswith(u"\nsubdir contents\n***************\n")
@with_intl_app(buildername='text', warning=warnfile) @with_intl_app(buildername='text')
def test_i18n_warnings_in_translation(app): def test_i18n_warnings_in_translation(app, status, warning):
app.builddir.rmtree(True) app.outdir.rmtree(True) # for warnings acceleration
app.doctreedir.rmtree(True)
app.builder.build(['warnings']) app.builder.build(['warnings'])
result = (app.outdir / 'warnings.txt').text(encoding='utf-8') result = (app.outdir / 'warnings.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH REST WARNINGS" expect = (u"\nI18N WITH REST WARNINGS"
@ -133,24 +126,24 @@ def test_i18n_warnings_in_translation(app):
assert result == expect assert result == expect
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
warning_expr = u'.*/warnings.txt:4: ' \ warning_expr = u'.*/warnings.txt:4: ' \
u'WARNING: Inline literal start-string without end-string.\n' u'WARNING: Inline literal start-string without end-string.\n'
assert re.search(warning_expr, warnings) assert re.search(warning_expr, warnings)
@with_intl_app(buildername='html', cleanenv=True) @with_intl_app(buildername='html', freshenv=True)
def test_i18n_footnote_break_refid(app): def test_i18n_footnote_break_refid(app, status, warning):
"""test for #955 cant-build-html-with-footnotes-when-using""" # test for #955 cant-build-html-with-footnotes-when-using
app.builder.build(['footnote']) app.builder.build(['footnote'])
result = (app.outdir / 'footnote.html').text(encoding='utf-8') (app.outdir / 'footnote.html').text(encoding='utf-8')
# expect no error by build # expect no error by build
@with_intl_app(buildername='xml', warning=warnfile) @with_intl_app(buildername='xml')
def test_i18n_footnote_regression(app): def test_i18n_footnote_regression(app, status, warning):
# regression test for fix #955, #1176 # regression test for fix #955, #1176
app.builddir.rmtree(True) #app.builddir.rmtree(True)
app.builder.build(['footnote']) app.builder.build(['footnote'])
et = ElementTree.parse(app.outdir / 'footnote.xml') et = ElementTree.parse(app.outdir / 'footnote.xml')
secs = et.findall('section') secs = et.findall('section')
@ -182,13 +175,13 @@ def test_i18n_footnote_regression(app):
texts=['ref', 'THIS IS A NAMED FOOTNOTE.'], texts=['ref', 'THIS IS A NAMED FOOTNOTE.'],
names=['ref']) names=['ref'])
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
warning_expr = u'.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n' warning_expr = u'.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n'
assert not re.search(warning_expr, warnings) assert not re.search(warning_expr, warnings)
@with_intl_app(buildername='xml', cleanenv=True) @with_intl_app(buildername='xml', freshenv=True)
def test_i18n_footnote_backlink(app): def test_i18n_footnote_backlink(app, status, warning):
# i18n test for #1058 # i18n test for #1058
app.builder.build(['footnote']) app.builder.build(['footnote'])
et = ElementTree.parse(app.outdir / 'footnote.xml') et = ElementTree.parse(app.outdir / 'footnote.xml')
@ -206,8 +199,8 @@ def test_i18n_footnote_backlink(app):
assert refid2id[ids] == backrefs assert refid2id[ids] == backrefs
@with_intl_app(buildername='xml', warning=warnfile) @with_intl_app(buildername='xml')
def test_i18n_refs_python_domain(app): def test_i18n_refs_python_domain(app, status, warning):
app.builder.build(['refs_python_domain']) app.builder.build(['refs_python_domain'])
et = ElementTree.parse(app.outdir / 'refs_python_domain.xml') et = ElementTree.parse(app.outdir / 'refs_python_domain.xml')
secs = et.findall('section') secs = et.findall('section')
@ -220,9 +213,9 @@ def test_i18n_refs_python_domain(app):
refs=['sensitive.sensitive_variables']) refs=['sensitive.sensitive_variables'])
@with_intl_app(buildername='text', warning=warnfile, cleanenv=True) @with_intl_app(buildername='text', freshenv=True)
def test_i18n_warn_for_number_of_references_inconsistency(app): def test_i18n_warn_for_number_of_references_inconsistency(app, status, warning):
app.builddir.rmtree(True) #app.builddir.rmtree(True)
app.builder.build(['refs_inconsistency']) app.builder.build(['refs_inconsistency'])
result = (app.outdir / 'refs_inconsistency.txt').text(encoding='utf-8') result = (app.outdir / 'refs_inconsistency.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH REFS INCONSISTENCY" expect = (u"\nI18N WITH REFS INCONSISTENCY"
@ -235,7 +228,7 @@ def test_i18n_warn_for_number_of_references_inconsistency(app):
u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n") u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n")
assert result == expect assert result == expect
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
warning_fmt = u'.*/refs_inconsistency.txt:\\d+: ' \ warning_fmt = u'.*/refs_inconsistency.txt:\\d+: ' \
u'WARNING: inconsistent %s in translated message\n' u'WARNING: inconsistent %s in translated message\n'
expected_warning_expr = ( expected_warning_expr = (
@ -245,8 +238,8 @@ def test_i18n_warn_for_number_of_references_inconsistency(app):
assert re.search(expected_warning_expr, warnings) assert re.search(expected_warning_expr, warnings)
@with_intl_app(buildername='html', cleanenv=True) @with_intl_app(buildername='html', freshenv=True)
def test_i18n_link_to_undefined_reference(app): def test_i18n_link_to_undefined_reference(app, status, warning):
app.builder.build(['refs_inconsistency']) app.builder.build(['refs_inconsistency'])
result = (app.outdir / 'refs_inconsistency.html').text(encoding='utf-8') result = (app.outdir / 'refs_inconsistency.html').text(encoding='utf-8')
@ -264,8 +257,8 @@ def test_i18n_link_to_undefined_reference(app):
assert len(re.findall(expected_expr, result)) == 1 assert len(re.findall(expected_expr, result)) == 1
@with_intl_app(buildername='xml', cleanenv=True) @with_intl_app(buildername='xml', freshenv=True)
def test_i18n_keep_external_links(app): def test_i18n_keep_external_links(app, status, warning):
# regression test for #1044 # regression test for #1044
app.builder.build(['external_links']) app.builder.build(['external_links'])
et = ElementTree.parse(app.outdir / 'external_links.xml') et = ElementTree.parse(app.outdir / 'external_links.xml')
@ -321,9 +314,9 @@ def test_i18n_keep_external_links(app):
'http://python.org']) 'http://python.org'])
@with_intl_app(buildername='text', warning=warnfile, cleanenv=True) @with_intl_app(buildername='text', freshenv=True)
def test_i18n_literalblock_warning(app): def test_i18n_literalblock_warning(app, status, warning):
app.builddir.rmtree(True) #for warnings acceleration #app.builddir.rmtree(True) # for warnings acceleration
app.builder.build(['literalblock']) app.builder.build(['literalblock'])
result = (app.outdir / 'literalblock.txt').text(encoding='utf-8') result = (app.outdir / 'literalblock.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH LITERAL BLOCK" expect = (u"\nI18N WITH LITERAL BLOCK"
@ -335,14 +328,14 @@ def test_i18n_literalblock_warning(app):
u"\n<SYSTEM MESSAGE:") u"\n<SYSTEM MESSAGE:")
assert result.startswith(expect) assert result.startswith(expect)
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
expected_warning_expr = u'.*/literalblock.txt:\\d+: ' \ expected_warning_expr = u'.*/literalblock.txt:\\d+: ' \
u'WARNING: Literal block expected; none found.' u'WARNING: Literal block expected; none found.'
assert re.search(expected_warning_expr, warnings) assert re.search(expected_warning_expr, warnings)
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_i18n_definition_terms(app): def test_i18n_definition_terms(app, status, warning):
# regression test for #975 # regression test for #975
app.builder.build(['definition_terms']) app.builder.build(['definition_terms'])
result = (app.outdir / 'definition_terms.txt').text(encoding='utf-8') result = (app.outdir / 'definition_terms.txt').text(encoding='utf-8')
@ -356,10 +349,10 @@ def test_i18n_definition_terms(app):
assert result == expect assert result == expect
@with_intl_app(buildername='text', warning=warnfile) @with_intl_app(buildername='text')
def test_i18n_glossary_terms(app): def test_i18n_glossary_terms(app, status, warning):
# regression test for #1090 # regression test for #1090
app.builddir.rmtree(True) #for warnings acceleration #app.builddir.rmtree(True) # for warnings acceleration
app.builder.build(['glossary_terms']) app.builder.build(['glossary_terms'])
result = (app.outdir / 'glossary_terms.txt').text(encoding='utf-8') result = (app.outdir / 'glossary_terms.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH GLOSSARY TERMS" expect = (u"\nI18N WITH GLOSSARY TERMS"
@ -371,14 +364,14 @@ def test_i18n_glossary_terms(app):
u"\nLINK TO *SOME NEW TERM*.\n") u"\nLINK TO *SOME NEW TERM*.\n")
assert result == expect assert result == expect
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
assert 'term not in glossary' not in warnings assert 'term not in glossary' not in warnings
@with_intl_app(buildername='xml', warning=warnfile) @with_intl_app(buildername='xml')
def test_i18n_role_xref(app): def test_i18n_role_xref(app, status, warning):
# regression test for #1090, #1193 # regression test for #1090, #1193
app.builddir.rmtree(True) #for warnings acceleration #app.builddir.rmtree(True) # for warnings acceleration
app.builder.build(['role_xref']) app.builder.build(['role_xref'])
et = ElementTree.parse(app.outdir / 'role_xref.xml') et = ElementTree.parse(app.outdir / 'role_xref.xml')
sec1, sec2 = et.findall('section') sec1, sec2 = et.findall('section')
@ -426,14 +419,14 @@ def test_i18n_role_xref(app):
refs=['same-type-links', 'i18n-role-xref']) refs=['same-type-links', 'i18n-role-xref'])
# warnings # warnings
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
assert 'term not in glossary' not in warnings assert 'term not in glossary' not in warnings
assert 'undefined label' not in warnings assert 'undefined label' not in warnings
assert 'unknown document' not in warnings assert 'unknown document' not in warnings
@with_intl_app(buildername='xml', warning=warnfile) @with_intl_app(buildername='xml')
def test_i18n_label_target(app): def test_i18n_label_target(app, status, warning):
# regression test for #1193, #1265 # regression test for #1193, #1265
app.builder.build(['label_target']) app.builder.build(['label_target'])
et = ElementTree.parse(app.outdir / 'label_target.xml') et = ElementTree.parse(app.outdir / 'label_target.xml')
@ -487,19 +480,19 @@ def test_i18n_label_target(app):
'section-and-label']) 'section-and-label'])
@with_intl_app(buildername='text', warning=warnfile) @with_intl_app(buildername='text')
def test_i18n_glossary_terms_inconsistency(app): def test_i18n_glossary_terms_inconsistency(app, status, warning):
# regression test for #1090 # regression test for #1090
app.builddir.rmtree(True) #for warnings acceleration app.outdir.rmtree(True) # for warnings acceleration
app.doctreedir.rmtree(True) # for warnings acceleration
app.builder.build(['glossary_terms_inconsistency']) app.builder.build(['glossary_terms_inconsistency'])
result = (app.outdir / 'glossary_terms_inconsistency.txt' result = (app.outdir / 'glossary_terms_inconsistency.txt').text(encoding='utf-8')
).text(encoding='utf-8')
expect = (u"\nI18N WITH GLOSSARY TERMS INCONSISTENCY" expect = (u"\nI18N WITH GLOSSARY TERMS INCONSISTENCY"
u"\n**************************************\n" u"\n**************************************\n"
u"\n1. LINK TO *SOME NEW TERM*.\n") u"\n1. LINK TO *SOME NEW TERM*.\n")
assert result == expect assert result == expect
warnings = warnfile.getvalue().replace(os.sep, '/') warnings = warning.getvalue().replace(os.sep, '/')
expected_warning_expr = ( expected_warning_expr = (
u'.*/glossary_terms_inconsistency.txt:\\d+: ' u'.*/glossary_terms_inconsistency.txt:\\d+: '
u'WARNING: inconsistent term references in translated message\n') u'WARNING: inconsistent term references in translated message\n')
@ -507,7 +500,7 @@ def test_i18n_glossary_terms_inconsistency(app):
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_seealso(app): def test_seealso(app, status, warning):
app.builder.build(['seealso']) app.builder.build(['seealso'])
result = (app.outdir / 'seealso.txt').text(encoding='utf-8') result = (app.outdir / 'seealso.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH SEEALSO" expect = (u"\nI18N WITH SEEALSO"
@ -520,7 +513,7 @@ def test_seealso(app):
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_i18n_figure_caption(app): def test_i18n_figure_caption(app, status, warning):
# regression test for #940 # regression test for #940
app.builder.build(['figure_caption']) app.builder.build(['figure_caption'])
result = (app.outdir / 'figure_caption.txt').text(encoding='utf-8') result = (app.outdir / 'figure_caption.txt').text(encoding='utf-8')
@ -541,7 +534,7 @@ def test_i18n_figure_caption(app):
@with_intl_app(buildername='text') @with_intl_app(buildername='text')
def test_i18n_rubric(app): def test_i18n_rubric(app, status, warning):
# regression test for pull request #190 # regression test for pull request #190
app.builder.build(['rubric']) app.builder.build(['rubric'])
result = (app.outdir / 'rubric.txt').text(encoding='utf-8') result = (app.outdir / 'rubric.txt').text(encoding='utf-8')
@ -558,7 +551,7 @@ def test_i18n_rubric(app):
@with_intl_app(buildername='html') @with_intl_app(buildername='html')
def test_i18n_index_entries(app): def test_i18n_index_entries(app, status, warning):
# regression test for #976 # regression test for #976
app.builder.build(['index_entries']) app.builder.build(['index_entries'])
result = (app.outdir / 'genindex.html').text(encoding='utf-8') result = (app.outdir / 'genindex.html').text(encoding='utf-8')
@ -589,8 +582,8 @@ def test_i18n_index_entries(app):
assert re.search(expr, result, re.M) assert re.search(expr, result, re.M)
@with_intl_app(buildername='html', cleanenv=True) @with_intl_app(buildername='html', freshenv=True)
def test_versionchange(app): def test_versionchange(app, status, warning):
app.builder.build(['versionchange']) app.builder.build(['versionchange'])
result = (app.outdir / 'versionchange.html').text(encoding='utf-8') result = (app.outdir / 'versionchange.html').text(encoding='utf-8')
@ -622,8 +615,8 @@ def test_versionchange(app):
assert expect3 == matched_content assert expect3 == matched_content
@with_intl_app(buildername='text', cleanenv=True) @with_intl_app(buildername='text', freshenv=True)
def test_i18n_docfields(app): def test_i18n_docfields(app, status, warning):
app.builder.build(['docfields']) app.builder.build(['docfields'])
result = (app.outdir / 'docfields.txt').text(encoding='utf-8') result = (app.outdir / 'docfields.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH DOCFIELDS" expect = (u"\nI18N WITH DOCFIELDS"
@ -648,8 +641,8 @@ def test_i18n_docfields(app):
assert result == expect assert result == expect
@with_intl_app(buildername='text', cleanenv=True) @with_intl_app(buildername='text', freshenv=True)
def test_i18n_admonitions(app): def test_i18n_admonitions(app, status, warning):
# #1206: gettext did not translate admonition directive's title # #1206: gettext did not translate admonition directive's title
# seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions # seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions
app.builder.build(['admonitions']) app.builder.build(['admonitions'])
@ -662,15 +655,15 @@ def test_i18n_admonitions(app):
assert d.upper() + " BODY" in result assert d.upper() + " BODY" in result
@with_intl_app(buildername='html', cleanenv=True) @with_intl_app(buildername='html', freshenv=True)
def test_i18n_docfields_html(app): def test_i18n_docfields_html(app, status, warning):
app.builder.build(['docfields']) app.builder.build(['docfields'])
result = (app.outdir / 'docfields.html').text(encoding='utf-8') (app.outdir / 'docfields.html').text(encoding='utf-8')
# expect no error by build # expect no error by build
@with_intl_app(buildername='html') @with_intl_app(buildername='html')
def test_gettext_template(app): def test_gettext_template(app, status, warning):
app.builder.build_all() app.builder.build_all()
result = (app.outdir / 'index.html').text(encoding='utf-8') result = (app.outdir / 'index.html').text(encoding='utf-8')
assert "WELCOME" in result assert "WELCOME" in result
@ -678,7 +671,7 @@ def test_gettext_template(app):
@with_intl_app(buildername='html') @with_intl_app(buildername='html')
def test_rebuild_by_mo_mtime(app): def test_rebuild_by_mo_mtime(app, status, warning):
app.builder.build_update() app.builder.build_update()
_, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app)
assert count == 0 assert count == 0

View File

@ -23,10 +23,11 @@ from util import TestApp
app = settings = parser = None app = settings = parser = None
def setup_module(): def setup_module():
global app, settings, parser global app, settings, parser
texescape.init() # otherwise done by the latex builder texescape.init() # otherwise done by the latex builder
app = TestApp(cleanenv=True) app = TestApp()
optparser = frontend.OptionParser( optparser = frontend.OptionParser(
components=(rst.Parser, HTMLWriter, LaTeXWriter)) components=(rst.Parser, HTMLWriter, LaTeXWriter))
settings = optparser.get_default_values() settings = optparser.get_default_values()
@ -35,6 +36,7 @@ def setup_module():
settings.env.temp_data['docname'] = 'dummy' settings.env.temp_data['docname'] = 'dummy'
parser = rst.Parser() parser = rst.Parser()
def teardown_module(): def teardown_module():
app.cleanup() app.cleanup()
@ -42,12 +44,15 @@ def teardown_module():
class ForgivingTranslator: class ForgivingTranslator:
def visit_pending_xref(self, node): def visit_pending_xref(self, node):
pass pass
def depart_pending_xref(self, node): def depart_pending_xref(self, node):
pass pass
class ForgivingHTMLTranslator(SmartyPantsHTMLTranslator, ForgivingTranslator): class ForgivingHTMLTranslator(SmartyPantsHTMLTranslator, ForgivingTranslator):
pass pass
class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator): class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
pass pass

View File

@ -12,27 +12,13 @@
# adapted from an example of bibliographic metadata at # adapted from an example of bibliographic metadata at
# http://docutils.sourceforge.net/docs/user/rst/demo.txt # http://docutils.sourceforge.net/docs/user/rst/demo.txt
from util import TestApp from util import with_app
from nose.tools import assert_equal from nose.tools import assert_equal
app = env = None @with_app('pseudoxml')
warnings = [] def test_docinfo(app, status, warning):
def setup_module():
# Is there a better way of generating this doctree than manually iterating?
global app, env
app = TestApp(_copy_to_temp=True)
env = app.env
msg, num, it = env.update(app.config, app.srcdir, app.doctreedir, app)
for docname in it:
pass
def teardown_module():
app.cleanup()
def test_docinfo():
""" """
Inspect the 'docinfo' metadata stored in the first node of the document. Inspect the 'docinfo' metadata stored in the first node of the document.
Note this doesn't give us access to data stored in subsequence blocks Note this doesn't give us access to data stored in subsequence blocks
@ -40,6 +26,8 @@ def test_docinfo():
'dedication' blocks, or the 'meta' role. Doing otherwise is probably more 'dedication' blocks, or the 'meta' role. Doing otherwise is probably more
messing with the internals of sphinx than this rare use case merits. messing with the internals of sphinx than this rare use case merits.
""" """
app.builder.build(['metadata'])
env = app.env
exampledocinfo = env.metadata['metadata'] exampledocinfo = env.metadata['metadata']
expecteddocinfo = { expecteddocinfo = {
'author': u'David Goodger', 'author': u'David Goodger',

View File

@ -29,8 +29,10 @@ warnfile = StringIO()
def setup_module(): def setup_module():
nocolor() nocolor()
def mock_input(answers, needanswer=False): def mock_input(answers, needanswer=False):
called = set() called = set()
def input_(prompt): def input_(prompt):
if prompt in called: if prompt in called:
raise AssertionError('answer for %r missing and no default ' raise AssertionError('answer for %r missing and no default '
@ -50,8 +52,10 @@ def mock_input(answers, needanswer=False):
return '' return ''
return input_ return input_
real_input = input real_input = input
def teardown_module(): def teardown_module():
qs.term_input = real_input qs.term_input = real_input
qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
@ -214,7 +218,7 @@ def test_quickstart_all_answers(tempdir):
assert ns['texinfo_documents'] == [ assert ns['texinfo_documents'] == [
('contents', 'STASI', u'STASI™ Documentation', ('contents', 'STASI', u'STASI™ Documentation',
u'Wolfgang Schäuble & G\'Beckstein', 'STASI', u'Wolfgang Schäuble & G\'Beckstein', 'STASI',
'One line description of project.', 'Miscellaneous'),] 'One line description of project.', 'Miscellaneous')]
assert (tempdir / 'build').isdir() assert (tempdir / 'build').isdir()
assert (tempdir / 'source' / '.static').isdir() assert (tempdir / 'source' / '.static').isdir()

View File

@ -9,33 +9,24 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
from six import StringIO from six import StringIO
from sphinx.websupport import WebSupport from sphinx.websupport import WebSupport
from test_websupport import sqlalchemy_missing from test_websupport import sqlalchemy_missing
from util import test_root, skip_if, skip_unless_importable from util import rootdir, tempdir, skip_if, skip_unless_importable
def clear_builddir():
(test_root / 'websupport').rmtree(True)
def teardown_module(): def teardown_module():
(test_root / 'generated').rmtree(True) (tempdir / 'websupport').rmtree(True)
clear_builddir()
def search_adapter_helper(adapter): def search_adapter_helper(adapter):
clear_builddir() settings = {'srcdir': rootdir / 'root',
'builddir': tempdir / 'websupport',
settings = {'builddir': os.path.join(test_root, 'websupport'),
'status': StringIO(), 'status': StringIO(),
'warning': StringIO()} 'warning': StringIO(),
settings.update({'srcdir': test_root, 'search': adapter}
'search': adapter})
support = WebSupport(**settings) support = WebSupport(**settings)
support.build() support.build()
@ -63,7 +54,7 @@ def search_adapter_helper(adapter):
'%s search adapter returned %s search result(s), should have been 1'\ '%s search adapter returned %s search result(s), should have been 1'\
% (adapter, len(results)) % (adapter, len(results))
# Make sure it works through the WebSupport API # Make sure it works through the WebSupport API
html = support.get_search_results(u'SomeLongRandomWord') support.get_search_results(u'SomeLongRandomWord')
@skip_unless_importable('xapian', 'needs xapian bindings installed') @skip_unless_importable('xapian', 'needs xapian bindings installed')

View File

@ -16,11 +16,16 @@ from functools import wraps
import tempfile import tempfile
import sphinx import sphinx
from util import with_tempdir, test_roots, SkipTest from util import rootdir, tempdir, SkipTest
from path import path from path import path
from textwrap import dedent from textwrap import dedent
root = test_roots / 'test-setup' root = tempdir / 'test-setup'
def setup_module():
if not root.exists():
(rootdir / 'roots' / 'test-setup').copytree(root)
def with_setup_command(root, *args, **kwds): def with_setup_command(root, *args, **kwds):

View File

@ -9,28 +9,23 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from util import test_roots, with_app from util import with_app
def teardown_module(): @with_app('html', testroot='templating')
(test_roots / 'test-templating' / '_build').rmtree(True), def test_layout_overloading(app, status, warning):
app.builder.build_update()
@with_app(buildername='html', srcdir=(test_roots / 'test-templating'))
def test_layout_overloading(app):
app.builder.build_all()
result = (app.outdir / 'contents.html').text(encoding='utf-8') result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert '<!-- layout overloading -->' in result assert '<!-- layout overloading -->' in result
@with_app(buildername='html', srcdir=(test_roots / 'test-templating')) @with_app('html', testroot='templating')
def test_autosummary_class_template_overloading(app): def test_autosummary_class_template_overloading(app, status, warning):
app.builder.build_all() app.builder.build_update()
result = (app.outdir / 'generated' / 'sphinx.application.Sphinx.html').text( result = (app.outdir / 'generated' / 'sphinx.application.TemplateBridge.html').text(
encoding='utf-8') encoding='utf-8')
assert 'autosummary/class.rst method block overloading' in result assert 'autosummary/class.rst method block overloading' in result

View File

@ -19,7 +19,7 @@ from util import with_app, raises
@with_app(confoverrides={'html_theme': 'ziptheme', @with_app(confoverrides={'html_theme': 'ziptheme',
'html_theme_options.testopt': 'foo'}) 'html_theme_options.testopt': 'foo'})
def test_theme_api(app): def test_theme_api(app, status, warning):
cfg = app.config cfg = app.config
# test Theme class API # test Theme class API
@ -56,14 +56,15 @@ def test_theme_api(app):
theme.cleanup() theme.cleanup()
assert not os.path.exists(themedir) assert not os.path.exists(themedir)
@with_app(buildername='html')
def test_js_source(app): @with_app()
def test_js_source(app, status, warning):
# Now sphinx provides non-minified JS files for jquery.js and underscore.js # Now sphinx provides non-minified JS files for jquery.js and underscore.js
# to clarify the source of the minified files. see also #1434. # to clarify the source of the minified files. see also #1434.
# If you update the version of the JS file, please update the source of the # If you update the version of the JS file, please update the source of the
# JS file and version number in this test. # JS file and version number in this test.
app.builder.build_all() app.builder.build(['contents'])
v = '1.8.3' v = '1.8.3'
msg = 'jquery.js version does not match to {v}'.format(v=v) msg = 'jquery.js version does not match to {v}'.format(v=v)

View File

@ -16,39 +16,46 @@ from docutils.parsers.rst.directives.html import MetaBody
from sphinx import addnodes from sphinx import addnodes
from sphinx.versioning import add_uids, merge_doctrees, get_ratio from sphinx.versioning import add_uids, merge_doctrees, get_ratio
from util import test_root, TestApp from util import TestApp
app = original = original_uids = None app = original = original_uids = None
def setup_module(): def setup_module():
global app, original, original_uids global app, original, original_uids
app = TestApp() app = TestApp(testroot='versioning')
app.builder.env.app = app app.builder.env.app = app
app.connect('doctree-resolved', on_doctree_resolved) app.connect('doctree-resolved', on_doctree_resolved)
app.build() app.build()
original = doctrees['versioning/original'] original = doctrees['original']
original_uids = [n.uid for n in add_uids(original, is_paragraph)] original_uids = [n.uid for n in add_uids(original, is_paragraph)]
def teardown_module(): def teardown_module():
app.cleanup() app.cleanup()
(test_root / '_build').rmtree(True)
doctrees = {} doctrees = {}
def on_doctree_resolved(app, doctree, docname): def on_doctree_resolved(app, doctree, docname):
doctrees[docname] = doctree doctrees[docname] = doctree
def is_paragraph(node): def is_paragraph(node):
return node.__class__.__name__ == 'paragraph' return node.__class__.__name__ == 'paragraph'
def test_get_ratio(): def test_get_ratio():
assert get_ratio('', 'a') assert get_ratio('', 'a')
assert get_ratio('a', '') assert get_ratio('a', '')
def test_add_uids(): def test_add_uids():
assert len(original_uids) == 3 assert len(original_uids) == 3
def test_picklablility(): def test_picklablility():
# we have to modify the doctree so we can pickle it # we have to modify the doctree so we can pickle it
copy = original.copy() copy = original.copy()
@ -62,44 +69,50 @@ def test_picklablility():
loaded = pickle.loads(pickle.dumps(copy, pickle.HIGHEST_PROTOCOL)) loaded = pickle.loads(pickle.dumps(copy, pickle.HIGHEST_PROTOCOL))
assert all(getattr(n, 'uid', False) for n in loaded.traverse(is_paragraph)) assert all(getattr(n, 'uid', False) for n in loaded.traverse(is_paragraph))
def test_modified(): def test_modified():
modified = doctrees['versioning/modified'] modified = doctrees['modified']
new_nodes = list(merge_doctrees(original, modified, is_paragraph)) new_nodes = list(merge_doctrees(original, modified, is_paragraph))
uids = [n.uid for n in modified.traverse(is_paragraph)] uids = [n.uid for n in modified.traverse(is_paragraph)]
assert not new_nodes assert not new_nodes
assert original_uids == uids assert original_uids == uids
def test_added(): def test_added():
added = doctrees['versioning/added'] added = doctrees['added']
new_nodes = list(merge_doctrees(original, added, is_paragraph)) new_nodes = list(merge_doctrees(original, added, is_paragraph))
uids = [n.uid for n in added.traverse(is_paragraph)] uids = [n.uid for n in added.traverse(is_paragraph)]
assert len(new_nodes) == 1 assert len(new_nodes) == 1
assert original_uids == uids[:-1] assert original_uids == uids[:-1]
def test_deleted(): def test_deleted():
deleted = doctrees['versioning/deleted'] deleted = doctrees['deleted']
new_nodes = list(merge_doctrees(original, deleted, is_paragraph)) new_nodes = list(merge_doctrees(original, deleted, is_paragraph))
uids = [n.uid for n in deleted.traverse(is_paragraph)] uids = [n.uid for n in deleted.traverse(is_paragraph)]
assert not new_nodes assert not new_nodes
assert original_uids[::2] == uids assert original_uids[::2] == uids
def test_deleted_end(): def test_deleted_end():
deleted_end = doctrees['versioning/deleted_end'] deleted_end = doctrees['deleted_end']
new_nodes = list(merge_doctrees(original, deleted_end, is_paragraph)) new_nodes = list(merge_doctrees(original, deleted_end, is_paragraph))
uids = [n.uid for n in deleted_end.traverse(is_paragraph)] uids = [n.uid for n in deleted_end.traverse(is_paragraph)]
assert not new_nodes assert not new_nodes
assert original_uids[:-1] == uids assert original_uids[:-1] == uids
def test_insert(): def test_insert():
insert = doctrees['versioning/insert'] insert = doctrees['insert']
new_nodes = list(merge_doctrees(original, insert, is_paragraph)) new_nodes = list(merge_doctrees(original, insert, is_paragraph))
uids = [n.uid for n in insert.traverse(is_paragraph)] uids = [n.uid for n in insert.traverse(is_paragraph)]
assert len(new_nodes) == 1 assert len(new_nodes) == 1
assert original_uids[0] == uids[0] assert original_uids[0] == uids[0]
assert original_uids[1:] == uids[2:] assert original_uids[1:] == uids[2:]
def test_insert_beginning(): def test_insert_beginning():
insert_beginning = doctrees['versioning/insert_beginning'] insert_beginning = doctrees['insert_beginning']
new_nodes = list(merge_doctrees(original, insert_beginning, is_paragraph)) new_nodes = list(merge_doctrees(original, insert_beginning, is_paragraph))
uids = [n.uid for n in insert_beginning.traverse(is_paragraph)] uids = [n.uid for n in insert_beginning.traverse(is_paragraph)]
assert len(new_nodes) == 1 assert len(new_nodes) == 1
@ -107,8 +120,9 @@ def test_insert_beginning():
assert original_uids == uids[1:] assert original_uids == uids[1:]
assert original_uids[0] != uids[0] assert original_uids[0] != uids[0]
def test_insert_similar(): def test_insert_similar():
insert_similar = doctrees['versioning/insert_similar'] insert_similar = doctrees['insert_similar']
new_nodes = list(merge_doctrees(original, insert_similar, is_paragraph)) new_nodes = list(merge_doctrees(original, insert_similar, is_paragraph))
uids = [n.uid for n in insert_similar.traverse(is_paragraph)] uids = [n.uid for n in insert_similar.traverse(is_paragraph)]
assert len(new_nodes) == 1 assert len(new_nodes) == 1

View File

@ -9,7 +9,6 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
from functools import wraps from functools import wraps
from six import StringIO from six import StringIO
@ -27,16 +26,16 @@ try:
except ImportError: except ImportError:
sqlalchemy_missing = True sqlalchemy_missing = True
from util import test_root, raises, skip_if from util import rootdir, tempdir, raises, skip_if
default_settings = {'builddir': os.path.join(test_root, 'websupport'), default_settings = {'builddir': tempdir / 'websupport',
'status': StringIO(), 'status': StringIO(),
'warning': StringIO()} 'warning': StringIO()}
def teardown_module(): def teardown_module():
(test_root / 'generated').rmtree(True) (tempdir / 'websupport').rmtree(True)
(test_root / 'websupport').rmtree(True)
def with_support(*args, **kwargs): def with_support(*args, **kwargs):
@ -59,12 +58,12 @@ class NullStorage(StorageBackend):
@with_support(storage=NullStorage()) @with_support(storage=NullStorage())
def test_no_srcdir(support): def test_no_srcdir(support):
"""Make sure the correct exception is raised if srcdir is not given.""" # make sure the correct exception is raised if srcdir is not given.
raises(RuntimeError, support.build) raises(RuntimeError, support.build)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support(srcdir=test_root) @with_support(srcdir=rootdir / 'root')
def test_build(support): def test_build(support):
support.build() support.build()
@ -173,7 +172,7 @@ def test_proposals(support):
source = data['source'] source = data['source']
proposal = source[:5] + source[10:15] + 'asdf' + source[15:] proposal = source[:5] + source[10:15] + 'asdf' + source[15:]
comment = support.add_comment('Proposal comment', support.add_comment('Proposal comment',
node_id=node.id, node_id=node.id,
proposal=proposal) proposal=proposal)
@ -234,6 +233,8 @@ def test_update_username(support):
called = False called = False
def moderation_callback(comment): def moderation_callback(comment):
global called global called
called = True called = True
@ -251,7 +252,7 @@ def test_moderation(support):
deleted = support.add_comment('Comment to delete', node_id=node.id, deleted = support.add_comment('Comment to delete', node_id=node.id,
displayed=False) displayed=False)
# Make sure the moderation_callback is called. # Make sure the moderation_callback is called.
assert called == True assert called
# Make sure the user must be a moderator. # Make sure the user must be a moderator.
raises(UserNotAuthorizedError, support.accept_comment, accepted['id']) raises(UserNotAuthorizedError, support.accept_comment, accepted['id'])
raises(UserNotAuthorizedError, support.delete_comment, deleted['id']) raises(UserNotAuthorizedError, support.delete_comment, deleted['id'])

View File

@ -8,22 +8,22 @@
""" """
import os import os
import re
import sys import sys
import tempfile import tempfile
import shutil
import re
from functools import wraps from functools import wraps
from six import StringIO from six import StringIO
from nose import tools, SkipTest
from sphinx import application from sphinx import application
from sphinx.theming import Theme from sphinx.theming import Theme
from sphinx.ext.autodoc import AutoDirective from sphinx.ext.autodoc import AutoDirective
from sphinx.pycode import ModuleAnalyzer
from path import path from path import path
from nose import tools, SkipTest
try: try:
# Python >=3.3 # Python >=3.3
from unittest import mock from unittest import mock
@ -32,7 +32,7 @@ except ImportError:
__all__ = [ __all__ = [
'test_root', 'test_roots', 'raises', 'raises_msg', 'rootdir', 'tempdir', 'raises', 'raises_msg',
'skip_if', 'skip_unless', 'skip_unless_importable', 'Struct', 'skip_if', 'skip_unless', 'skip_unless_importable', 'Struct',
'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'ListOutput', 'TestApp', 'with_app', 'gen_with_app',
'path', 'with_tempdir', 'path', 'with_tempdir',
@ -41,8 +41,8 @@ __all__ = [
] ]
test_root = path(__file__).parent.joinpath('root').abspath() rootdir = path(os.path.dirname(__file__) or '.').abspath()
test_roots = path(__file__).parent.joinpath('roots').abspath() tempdir = path(os.environ['SPHINX_TEST_TEMPDIR']).abspath()
def _excstr(exc): def _excstr(exc):
@ -50,11 +50,9 @@ def _excstr(exc):
return str(tuple(map(_excstr, exc))) return str(tuple(map(_excstr, exc)))
return exc.__name__ return exc.__name__
def raises(exc, func, *args, **kwds): def raises(exc, func, *args, **kwds):
""" """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
Raise :exc:`AssertionError` if ``func(*args, **kwds)`` does not
raise *exc*.
"""
try: try:
func(*args, **kwds) func(*args, **kwds)
except exc: except exc:
@ -63,10 +61,10 @@ def raises(exc, func, *args, **kwds):
raise AssertionError('%s did not raise %s' % raise AssertionError('%s did not raise %s' %
(func.__name__, _excstr(exc))) (func.__name__, _excstr(exc)))
def raises_msg(exc, msg, func, *args, **kwds): def raises_msg(exc, msg, func, *args, **kwds):
""" """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*,
Raise :exc:`AssertionError` if ``func(*args, **kwds)`` does not and check if the message contains *msg*.
raise *exc*, and check if the message contains *msg*.
""" """
try: try:
func(*args, **kwds) func(*args, **kwds)
@ -76,6 +74,7 @@ def raises_msg(exc, msg, func, *args, **kwds):
raise AssertionError('%s did not raise %s' % raise AssertionError('%s did not raise %s' %
(func.__name__, _excstr(exc))) (func.__name__, _excstr(exc)))
def skip_if(condition, msg=None): def skip_if(condition, msg=None):
"""Decorator to skip test if condition is true.""" """Decorator to skip test if condition is true."""
def deco(test): def deco(test):
@ -87,10 +86,12 @@ def skip_if(condition, msg=None):
return skipper return skipper
return deco return deco
def skip_unless(condition, msg=None): def skip_unless(condition, msg=None):
"""Decorator to skip test if condition is false.""" """Decorator to skip test if condition is false."""
return skip_if(not condition, msg) return skip_if(not condition, msg)
def skip_unless_importable(module, msg=None): def skip_unless_importable(module, msg=None):
"""Decorator to skip test if module is not importable.""" """Decorator to skip test if module is not importable."""
try: try:
@ -127,62 +128,48 @@ class TestApp(application.Sphinx):
better default values for the initialization parameters. better default values for the initialization parameters.
""" """
def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None, def __init__(self, buildername='html', testroot=None, srcdir=None,
buildername='html', confoverrides=None, freshenv=False, confoverrides=None, status=None, warning=None,
status=None, warning=None, freshenv=None, tags=None, docutilsconf=None):
warningiserror=None, tags=None, if testroot is None:
confname='conf.py', cleanenv=False, defaultsrcdir = 'root'
_copy_to_temp=False, testroot = rootdir / 'root'
):
application.CONFIG_FILENAME = confname
self.cleanup_trees = [test_root / 'generated']
if srcdir is None:
srcdir = test_root
elif srcdir == '(empty)':
tempdir = path(tempfile.mkdtemp())
self.cleanup_trees.append(tempdir)
temproot = tempdir / 'root'
temproot.makedirs()
(temproot / 'conf.py').write_text('')
srcdir = temproot
else: else:
srcdir = path(srcdir) defaultsrcdir = 'test-' + testroot
testroot = rootdir / 'roots' / ('test-' + testroot)
if srcdir is None:
srcdir = tempdir / defaultsrcdir
else:
srcdir = tempdir / srcdir
if _copy_to_temp: if not srcdir.exists():
tempdir = path(tempfile.mkdtemp()) testroot.copytree(srcdir)
self.cleanup_trees.append(tempdir)
temproot = tempdir / srcdir.basename()
srcdir.copytree(temproot)
srcdir = temproot
self.builddir = srcdir.joinpath('_build') if docutilsconf is not None:
if confdir is None: (srcdir / 'docutils.conf').write_text(docutilsconf)
builddir = srcdir / '_build'
# if confdir is None:
confdir = srcdir confdir = srcdir
if outdir is None: # if outdir is None:
outdir = srcdir.joinpath(self.builddir, buildername) outdir = builddir.joinpath(buildername)
if not outdir.isdir(): if not outdir.isdir():
outdir.makedirs() outdir.makedirs()
self.cleanup_trees.insert(0, outdir) # if doctreedir is None:
if doctreedir is None: doctreedir = builddir.joinpath('doctrees')
doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees')
if not doctreedir.isdir(): if not doctreedir.isdir():
doctreedir.makedirs() doctreedir.makedirs()
if cleanenv:
self.cleanup_trees.insert(0, doctreedir)
if confoverrides is None: if confoverrides is None:
confoverrides = {} confoverrides = {}
if status is None: if status is None:
status = StringIO() status = StringIO()
if warning is None: if warning is None:
warning = ListOutput('stderr') warning = ListOutput('stderr')
if freshenv is None: # if warningiserror is None:
freshenv = False
if warningiserror is None:
warningiserror = False warningiserror = False
self._saved_path = sys.path[:]
application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir, application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir,
buildername, confoverrides, status, warning, buildername, confoverrides, status, warning,
freshenv, warningiserror, tags) freshenv, warningiserror, tags)
@ -190,8 +177,9 @@ class TestApp(application.Sphinx):
def cleanup(self, doctrees=False): def cleanup(self, doctrees=False):
Theme.themes.clear() Theme.themes.clear()
AutoDirective._registry.clear() AutoDirective._registry.clear()
for tree in self.cleanup_trees: ModuleAnalyzer.cache.clear()
shutil.rmtree(tree, True) sys.path[:] = self._saved_path
sys.modules.pop('autodoc_fodder', None)
def __repr__(self): def __repr__(self):
return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name) return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name)
@ -205,9 +193,13 @@ def with_app(*args, **kwargs):
def generator(func): def generator(func):
@wraps(func) @wraps(func)
def deco(*args2, **kwargs2): def deco(*args2, **kwargs2):
status, warning = StringIO(), StringIO()
kwargs['status'] = status
kwargs['warning'] = warning
app = TestApp(*args, **kwargs) app = TestApp(*args, **kwargs)
func(app, *args2, **kwargs2) try:
# don't execute cleanup if test failed func(app, status, warning, *args2, **kwargs2)
finally:
app.cleanup() app.cleanup()
return deco return deco
return generator return generator
@ -221,10 +213,14 @@ def gen_with_app(*args, **kwargs):
def generator(func): def generator(func):
@wraps(func) @wraps(func)
def deco(*args2, **kwargs2): def deco(*args2, **kwargs2):
status, warning = StringIO(), StringIO()
kwargs['status'] = status
kwargs['warning'] = warning
app = TestApp(*args, **kwargs) app = TestApp(*args, **kwargs)
for item in func(app, *args2, **kwargs2): try:
for item in func(app, status, warning, *args2, **kwargs2):
yield item yield item
# don't execute cleanup if test failed finally:
app.cleanup() app.cleanup()
return deco return deco
return generator return generator
@ -232,9 +228,9 @@ def gen_with_app(*args, **kwargs):
def with_tempdir(func): def with_tempdir(func):
def new_func(*args, **kwds): def new_func(*args, **kwds):
tempdir = path(tempfile.mkdtemp()) new_tempdir = path(tempfile.mkdtemp(dir=tempdir))
func(tempdir, *args, **kwds) func(new_tempdir, *args, **kwds)
tempdir.rmtree() new_tempdir.rmtree() # not when test fails...
new_func.__name__ = func.__name__ new_func.__name__ = func.__name__
return new_func return new_func
@ -242,7 +238,10 @@ def with_tempdir(func):
def sprint(*args): def sprint(*args):
sys.stderr.write(' '.join(map(str, args)) + '\n') sys.stderr.write(' '.join(map(str, args)) + '\n')
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')') _unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
def remove_unicode_literals(s): def remove_unicode_literals(s):
return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)

View File

@ -7,7 +7,7 @@ deps=
sqlalchemy sqlalchemy
whoosh whoosh
setenv = setenv =
BUILD_TEST_PATH = {envdir}/tests SPHINX_TEST_TEMPDIR = {envdir}/testbuild
commands= commands=
{envpython} tests/run.py {posargs} {envpython} tests/run.py {posargs}
sphinx-build -q -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html sphinx-build -q -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html