diff options
Diffstat (limited to 'tests')
128 files changed, 2338 insertions, 437 deletions
diff --git a/tests/coverage.py b/tests/coverage.py index f9341d8ba..cd36e218a 100755 --- a/tests/coverage.py +++ b/tests/coverage.py @@ -472,10 +472,9 @@ class coverage: def save(self): if self.usecache and self.cache: self.canonicalize_filenames() - cache = open(self.cache, 'wb') import marshal - marshal.dump(self.cexecuted, cache) - cache.close() + with open(self.cache, 'wb') as cache: + marshal.dump(self.cexecuted, cache) # restore(). Restore coverage data from the coverage cache (if it exists). @@ -488,10 +487,9 @@ class coverage: def restore_file(self, file_name): try: - cache = open(file_name, 'rb') import marshal - cexecuted = marshal.load(cache) - cache.close() + with open(file_name, 'rb') as cache: + cexecuted = marshal.load(cache) if isinstance(cexecuted, dict): return cexecuted else: @@ -614,8 +612,8 @@ class coverage: ) filename = filename[:-1] if not source: - sourcef = open(filename, 'rU') - source = sourcef.read() + with open(filename, 'rU') as sourcef: + source = sourcef.read() try: lines, excluded_lines, line_map = self.find_executable_statements( source, exclude=self.exclude_re @@ -625,8 +623,6 @@ class coverage: "Couldn't parse '%s' as Python source: '%s' at line %d" % (filename, synerr.msg, synerr.lineno) ) - if sourcef: - sourcef.close() result = filename, lines, excluded_lines, line_map self.analysis_cache[morf] = result return result diff --git a/tests/root/Makefile b/tests/root/Makefile index 7954bc7cb..7d5162fe7 100644 --- a/tests/root/Makefile +++ b/tests/root/Makefile @@ -17,7 +17,7 @@ help: @echo "Please use \`make <target>' where <target> is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" - @echo " htmlhelp to make HTML files and a HTML help project" + @echo " htmlhelp to make HTML files and an HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" diff --git a/tests/root/autodoc_missing_imports.py b/tests/root/autodoc_missing_imports.py index 7a7173452..0901ce8e2 100644 --- a/tests/root/autodoc_missing_imports.py +++ b/tests/root/autodoc_missing_imports.py @@ -5,5 +5,14 @@ import missing_package1.missing_module1 from missing_package2 import missing_module2 from missing_package3.missing_module3 import missing_name +@missing_name +def decoratedFunction(): + """decoratedFunction docstring""" + return None + class TestAutodoc(object): """TestAutodoc docstring.""" + @missing_name + def decoratedMethod(self): + """TestAutodoc::decoratedMethod docstring""" + return None diff --git a/tests/root/img.foo.png b/tests/root/img.foo.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/root/img.foo.png +++ b/tests/root/img.foo.png diff --git a/tests/root/img.png b/tests/root/img.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/root/img.png +++ b/tests/root/img.png diff --git a/tests/root/objects.txt b/tests/root/objects.txt index cd711070f..1fc23567a 100644 --- a/tests/root/objects.txt +++ b/tests/root/objects.txt @@ -93,7 +93,7 @@ Referring to :func:`nothing <>`. * expression :returns: a new :class:`Time` instance :rtype: Time - :raises ValueError: if the values are out of range + :raises Error: if the values are out of range :ivar int hour: like *hour* :ivar minute: like *minute* :vartype minute: int @@ -172,9 +172,13 @@ Others .. option:: +p +.. option:: --plugin.option + +.. option:: create-auth-token + .. option:: arg -Link to :option:`perl +p` and :option:`arg` +Link to :option:`perl +p`, :option:`--plugin.option`, :option:`create-auth-token` and :option:`arg` .. program:: hg diff --git a/tests/root/pep_0420/a/b/c/__init__.py b/tests/root/pep_0420/a/b/c/__init__.py new file mode 100644 index 000000000..619273942 --- /dev/null +++ b/tests/root/pep_0420/a/b/c/__init__.py @@ -0,0 +1 @@ +"Package C"
\ No newline at end of file diff --git a/tests/root/pep_0420/a/b/c/d.py b/tests/root/pep_0420/a/b/c/d.py new file mode 100644 index 000000000..6b0b45d90 --- /dev/null +++ b/tests/root/pep_0420/a/b/c/d.py @@ -0,0 +1 @@ +"Module d"
\ No newline at end of file diff --git a/tests/root/pep_0420/a/b/x/y.py b/tests/root/pep_0420/a/b/x/y.py new file mode 100644 index 000000000..8b49b2079 --- /dev/null +++ b/tests/root/pep_0420/a/b/x/y.py @@ -0,0 +1 @@ +"Module y"
\ No newline at end of file diff --git a/tests/root/rimg.png b/tests/root/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/root/rimg.png +++ b/tests/root/rimg.png diff --git a/tests/root/subdir/img.png b/tests/root/subdir/img.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/root/subdir/img.png +++ b/tests/root/subdir/img.png diff --git a/tests/root/subdir/simg.png b/tests/root/subdir/simg.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/root/subdir/simg.png +++ b/tests/root/subdir/simg.png diff --git a/tests/root/testtheme/static/staticimg.png b/tests/root/testtheme/static/staticimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/root/testtheme/static/staticimg.png +++ b/tests/root/testtheme/static/staticimg.png diff --git a/tests/roots/test-add_enumerable_node/rimg.png b/tests/roots/test-add_enumerable_node/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-add_enumerable_node/rimg.png +++ b/tests/roots/test-add_enumerable_node/rimg.png diff --git a/tests/roots/test-config/conf.py b/tests/roots/test-config/conf.py index b6075e5cb..1e583d1e0 100644 --- a/tests/roots/test-config/conf.py +++ b/tests/roots/test-config/conf.py @@ -1,4 +1,4 @@ -from sphinx.config import string_classes +from sphinx.config import string_classes, ENUM value1 = 123 # wrong type value2 = 123 # lambda with wrong type @@ -45,3 +45,4 @@ def setup(app): app.add_config_value('value14', None, False, string_classes) app.add_config_value('value15', u'unicode', False) app.add_config_value('value16', u'unicode', False) + app.add_config_value('value17', 'default', False, ENUM('default', 'one', 'two')) diff --git a/tests/roots/test-directive-code/lineno_match.rst b/tests/roots/test-directive-code/lineno_match.rst index 1015c8df7..42987609a 100644 --- a/tests/roots/test-directive-code/lineno_match.rst +++ b/tests/roots/test-directive-code/lineno_match.rst @@ -16,5 +16,11 @@ Literal Includes with Line Numbers Matching :start-after: pass :lineno-match: +.. literalinclude:: literal.inc + :language: python + :start-at: class Bar: + :end-at: pass + :lineno-match: + .. literalinclude:: empty.inc :lineno-match: diff --git a/tests/roots/test-ext-autodoc/autodoc_dummy_module.py b/tests/roots/test-ext-autodoc/autodoc_dummy_module.py new file mode 100644 index 000000000..c05d96e0d --- /dev/null +++ b/tests/roots/test-ext-autodoc/autodoc_dummy_module.py @@ -0,0 +1,6 @@ +from dummy import * + + +def test(): + """Dummy function using dummy.*""" + dummy_function() diff --git a/tests/roots/test-ext-autodoc/conf.py b/tests/roots/test-ext-autodoc/conf.py new file mode 100644 index 000000000..01e6dcc75 --- /dev/null +++ b/tests/roots/test-ext-autodoc/conf.py @@ -0,0 +1,12 @@ +import sys, os + +sys.path.insert(0, os.path.abspath('.')) + +extensions = ['sphinx.ext.autodoc'] + +# The suffix of source filenames. +source_suffix = '.rst' + +autodoc_mock_imports = [ + 'dummy' +] diff --git a/tests/roots/test-ext-autodoc/contents.rst b/tests/roots/test-ext-autodoc/contents.rst new file mode 100644 index 000000000..b808eafda --- /dev/null +++ b/tests/roots/test-ext-autodoc/contents.rst @@ -0,0 +1,3 @@ + +.. automodule:: autodoc_dummy_module + :members: diff --git a/tests/roots/test-ext-graphviz/index.rst b/tests/roots/test-ext-graphviz/index.rst index b778307e3..ab86e2a5a 100644 --- a/tests/roots/test-ext-graphviz/index.rst +++ b/tests/roots/test-ext-graphviz/index.rst @@ -19,3 +19,9 @@ Hello |graph| graphviz world .. graphviz:: graph.dot + +.. digraph:: bar + :align: right + :caption: on right + + foo -> bar diff --git a/tests/roots/test-ext-inheritance_diagram/index.rst b/tests/roots/test-ext-inheritance_diagram/index.rst index 876996ca8..777192bd7 100644 --- a/tests/roots/test-ext-inheritance_diagram/index.rst +++ b/tests/roots/test-ext-inheritance_diagram/index.rst @@ -3,3 +3,6 @@ test-ext-inheritance_diagram ============================ .. inheritance-diagram:: test.Foo + +.. inheritance-diagram:: test.Foo + :caption: Test Foo! diff --git a/tests/roots/test-ext-math/index.rst b/tests/roots/test-ext-math/index.rst index 02f50c20a..9d16824f6 100644 --- a/tests/roots/test-ext-math/index.rst +++ b/tests/roots/test-ext-math/index.rst @@ -1,6 +1,10 @@ Test Math ========= +.. toctree:: + + math + .. math:: a^2+b^2=c^2 Inline :math:`E=mc^2` diff --git a/tests/roots/test-ext-todo/bar.rst b/tests/roots/test-ext-todo/bar.rst new file mode 100644 index 000000000..6804a68c1 --- /dev/null +++ b/tests/roots/test-ext-todo/bar.rst @@ -0,0 +1,4 @@ +bar +=== + +.. todo:: todo in bar diff --git a/tests/roots/test-ext-todo/conf.py b/tests/roots/test-ext-todo/conf.py new file mode 100644 index 000000000..c67a86c5a --- /dev/null +++ b/tests/roots/test-ext-todo/conf.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +extensions = ['sphinx.ext.todo'] +master_doc = 'index' diff --git a/tests/roots/test-ext-todo/foo.rst b/tests/roots/test-ext-todo/foo.rst new file mode 100644 index 000000000..269199977 --- /dev/null +++ b/tests/roots/test-ext-todo/foo.rst @@ -0,0 +1,4 @@ +foo +=== + +.. todo:: todo in foo diff --git a/tests/roots/test-ext-todo/index.rst b/tests/roots/test-ext-todo/index.rst new file mode 100644 index 000000000..6b95f73fd --- /dev/null +++ b/tests/roots/test-ext-todo/index.rst @@ -0,0 +1,9 @@ +test for sphinx.ext.todo +======================== + +.. toctree:: + + foo + bar + +.. todolist:: diff --git a/tests/roots/test-footnotes/index.rst b/tests/roots/test-footnotes/index.rst index 3a8bc25c5..a714f9e22 100644 --- a/tests/roots/test-footnotes/index.rst +++ b/tests/roots/test-footnotes/index.rst @@ -76,15 +76,17 @@ Footnote in term [#]_ .. [#] Foot note in table -.. list-table:: footnote [#]_ in caption of longtable +.. list-table:: footnote [#]_ in caption [#]_ of longtable :widths: 1 1 :header-rows: 1 * - name - desc - * - a - - b - * - a + * - This is a reference to the code-block in the footnote: + :ref:`codeblockinfootnote` + - This is one more footnote with some code in it [#]_. + * - This is a reference to the other code block: + :ref:`codeblockinanotherfootnote` - b * - a - b @@ -148,3 +150,21 @@ Footnote in term [#]_ - b .. [#] Foot note in longtable + +.. [#] Second footnote in caption of longtable + + .. code-block:: python + :caption: I am in a footnote + :name: codeblockinfootnote + + def foo(x,y): + return x+y + +.. [#] Third footnote in longtable + + .. code-block:: python + :caption: I am also in a footnote + :name: codeblockinanotherfootnote + + def bar(x,y): + return x+y diff --git a/tests/roots/test-footnotes/rimg.png b/tests/roots/test-footnotes/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-footnotes/rimg.png +++ b/tests/roots/test-footnotes/rimg.png diff --git a/tests/roots/test-html_extra_path/conf.py b/tests/roots/test-html_assets/conf.py index 53ee62197..a17e417a3 100644 --- a/tests/roots/test-html_extra_path/conf.py +++ b/tests/roots/test-html_assets/conf.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- master_doc = 'index' +project = 'Sphinx' +version = '1.4.4' +html_static_path = ['static', 'subdir'] html_extra_path = ['extra', 'subdir'] exclude_patterns = ['**/_build', '**/.htpasswd'] diff --git a/tests/roots/test-html_extra_path/extra/.htaccess b/tests/roots/test-html_assets/extra/.htaccess index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-html_extra_path/extra/.htaccess +++ b/tests/roots/test-html_assets/extra/.htaccess diff --git a/tests/roots/test-html_extra_path/extra/.htpasswd b/tests/roots/test-html_assets/extra/.htpasswd index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-html_extra_path/extra/.htpasswd +++ b/tests/roots/test-html_assets/extra/.htpasswd diff --git a/tests/roots/test-html_assets/extra/API.html_t b/tests/roots/test-html_assets/extra/API.html_t new file mode 100644 index 000000000..34ecd9df1 --- /dev/null +++ b/tests/roots/test-html_assets/extra/API.html_t @@ -0,0 +1 @@ +{{ project }}-{{ version }} diff --git a/tests/roots/test-html_extra_path/extra/css/style.css b/tests/roots/test-html_assets/extra/css/style.css index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-html_extra_path/extra/css/style.css +++ b/tests/roots/test-html_assets/extra/css/style.css diff --git a/tests/roots/test-html_assets/extra/rimg.png b/tests/roots/test-html_assets/extra/rimg.png Binary files differnew file mode 100644 index 000000000..fda6cd29e --- /dev/null +++ b/tests/roots/test-html_assets/extra/rimg.png diff --git a/tests/roots/test-html_extra_path/extra/API.html_t b/tests/roots/test-html_assets/extra/subdir/.htaccess index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-html_extra_path/extra/API.html_t +++ b/tests/roots/test-html_assets/extra/subdir/.htaccess diff --git a/tests/roots/test-html_extra_path/subdir/_build/index.html b/tests/roots/test-html_assets/extra/subdir/.htpasswd index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-html_extra_path/subdir/_build/index.html +++ b/tests/roots/test-html_assets/extra/subdir/.htpasswd diff --git a/tests/roots/test-html_extra_path/index.rst b/tests/roots/test-html_assets/index.rst index 6d5619455..6d5619455 100644 --- a/tests/roots/test-html_extra_path/index.rst +++ b/tests/roots/test-html_assets/index.rst diff --git a/tests/roots/test-html_assets/static/.htaccess b/tests/roots/test-html_assets/static/.htaccess new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/static/.htaccess diff --git a/tests/roots/test-html_assets/static/.htpasswd b/tests/roots/test-html_assets/static/.htpasswd new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/static/.htpasswd diff --git a/tests/roots/test-html_assets/static/API.html_t b/tests/roots/test-html_assets/static/API.html_t new file mode 100644 index 000000000..34ecd9df1 --- /dev/null +++ b/tests/roots/test-html_assets/static/API.html_t @@ -0,0 +1 @@ +{{ project }}-{{ version }} diff --git a/tests/roots/test-html_assets/static/css/style.css b/tests/roots/test-html_assets/static/css/style.css new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/static/css/style.css diff --git a/tests/roots/test-html_assets/static/rimg.png b/tests/roots/test-html_assets/static/rimg.png Binary files differnew file mode 100644 index 000000000..fda6cd29e --- /dev/null +++ b/tests/roots/test-html_assets/static/rimg.png diff --git a/tests/roots/test-html_assets/static/subdir/.htaccess b/tests/roots/test-html_assets/static/subdir/.htaccess new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/static/subdir/.htaccess diff --git a/tests/roots/test-html_assets/static/subdir/.htpasswd b/tests/roots/test-html_assets/static/subdir/.htpasswd new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/static/subdir/.htpasswd diff --git a/tests/roots/test-html_assets/subdir/_build/index.html b/tests/roots/test-html_assets/subdir/_build/index.html new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/roots/test-html_assets/subdir/_build/index.html diff --git a/tests/roots/test-html_assets/subdir/background.png b/tests/roots/test-html_assets/subdir/background.png Binary files differnew file mode 100644 index 000000000..fda6cd29e --- /dev/null +++ b/tests/roots/test-html_assets/subdir/background.png diff --git a/tests/roots/test-html_extra_path/extra/rimg.png b/tests/roots/test-html_extra_path/extra/rimg.png Binary files differdeleted file mode 100644 index 1081dc143..000000000 --- a/tests/roots/test-html_extra_path/extra/rimg.png +++ /dev/null diff --git a/tests/roots/test-html_extra_path/subdir/background.png b/tests/roots/test-html_extra_path/subdir/background.png Binary files differdeleted file mode 100644 index 1081dc143..000000000 --- a/tests/roots/test-html_extra_path/subdir/background.png +++ /dev/null diff --git a/tests/roots/test-image-glob/img.ja.png b/tests/roots/test-image-glob/img.ja.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-image-glob/img.ja.png +++ b/tests/roots/test-image-glob/img.ja.png diff --git a/tests/roots/test-image-glob/img.png b/tests/roots/test-image-glob/img.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-image-glob/img.png +++ b/tests/roots/test-image-glob/img.png diff --git a/tests/roots/test-image-glob/img.zh.png b/tests/roots/test-image-glob/img.zh.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-image-glob/img.zh.png +++ b/tests/roots/test-image-glob/img.zh.png diff --git a/tests/roots/test-image-glob/rimg.png b/tests/roots/test-image-glob/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-image-glob/rimg.png +++ b/tests/roots/test-image-glob/rimg.png diff --git a/tests/roots/test-image-glob/rimg.xx.png b/tests/roots/test-image-glob/rimg.xx.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-image-glob/rimg.xx.png +++ b/tests/roots/test-image-glob/rimg.xx.png diff --git a/tests/roots/test-image-glob/subdir/rimg.png b/tests/roots/test-image-glob/subdir/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-image-glob/subdir/rimg.png +++ b/tests/roots/test-image-glob/subdir/rimg.png diff --git a/tests/roots/test-image-glob/subdir/rimg.xx.png b/tests/roots/test-image-glob/subdir/rimg.xx.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-image-glob/subdir/rimg.xx.png +++ b/tests/roots/test-image-glob/subdir/rimg.xx.png diff --git a/tests/roots/test-image-glob/testimäge.png b/tests/roots/test-image-glob/testimäge.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-image-glob/testimäge.png +++ b/tests/roots/test-image-glob/testimäge.png diff --git a/tests/roots/test-image-in-section/pic.png b/tests/roots/test-image-in-section/pic.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-image-in-section/pic.png +++ b/tests/roots/test-image-in-section/pic.png diff --git a/tests/roots/test-intl/contents.po b/tests/roots/test-intl/contents.po new file mode 100644 index 000000000..76ef049f0 --- /dev/null +++ b/tests/roots/test-intl/contents.po @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2010, Georg Brandl & Team +# This file is distributed under the same license as the Sphinx <Tests> package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Sphinx <Tests> 0.6\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-12-16 14:11+0000\n" +"PO-Revision-Date: 2012-12-18 06:14+0900\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Table of Contents" +msgstr "TABLE OF CONTENTS" + +msgid "testdata for i18n" +msgstr "TESTDATA FOR I18N" + +msgid "i18n, sphinx, markup" +msgstr "I18N, SPHINX, MARKUP" diff --git a/tests/roots/test-intl/contents.txt b/tests/roots/test-intl/contents.txt index 8882137f3..e2336856c 100644 --- a/tests/roots/test-intl/contents.txt +++ b/tests/roots/test-intl/contents.txt @@ -1,9 +1,14 @@ CONTENTS ======== +.. meta:: + :description: testdata for i18n + :keywords: i18n, sphinx, markup + .. toctree:: :maxdepth: 2 :numbered: + :caption: Table of Contents subdir/contents bom diff --git a/tests/roots/test-intl/i18n.png b/tests/roots/test-intl/i18n.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-intl/i18n.png +++ b/tests/roots/test-intl/i18n.png diff --git a/tests/roots/test-intl/img.png b/tests/roots/test-intl/img.png Binary files differindex 4c8f89929..a97e86d66 100644 --- a/tests/roots/test-intl/img.png +++ b/tests/roots/test-intl/img.png diff --git a/tests/roots/test-intl/only.po b/tests/roots/test-intl/only.po new file mode 100644 index 000000000..43eb7d60f --- /dev/null +++ b/tests/roots/test-intl/only.po @@ -0,0 +1,29 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2010, Georg Brandl & Team +# This file is distributed under the same license as the Sphinx <Tests> package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Sphinx <Tests> 0.6\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-02-04 13:06+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Only directive" +msgstr "ONLY DIRECTIVE" + +msgid "In HTML." +msgstr "IN HTML." + +msgid "In LaTeX." +msgstr "IN LATEX." + +msgid "In both." +msgstr "IN BOTH." diff --git a/tests/roots/test-intl/only.txt b/tests/roots/test-intl/only.txt new file mode 100644 index 000000000..2c8990e5f --- /dev/null +++ b/tests/roots/test-intl/only.txt @@ -0,0 +1,14 @@ +Only directive +-------------- + +.. only:: html + + In HTML. + +.. only:: latex + + In LaTeX. + +.. only:: html or latex + + In both. diff --git a/tests/roots/test-keep_warnings/conf.py b/tests/roots/test-keep_warnings/conf.py new file mode 100644 index 000000000..d0db3db83 --- /dev/null +++ b/tests/roots/test-keep_warnings/conf.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' +keep_warnings = True diff --git a/tests/roots/test-keep_warnings/index.rst b/tests/roots/test-keep_warnings/index.rst new file mode 100644 index 000000000..1e2d5977f --- /dev/null +++ b/tests/roots/test-keep_warnings/index.rst @@ -0,0 +1,2 @@ +keep_warnings +===== diff --git a/tests/roots/test-linkcheck/conf.py b/tests/roots/test-linkcheck/conf.py new file mode 100644 index 000000000..ae8ef24b7 --- /dev/null +++ b/tests/roots/test-linkcheck/conf.py @@ -0,0 +1,4 @@ +master_doc = 'links' +source_suffix = '.txt' +exclude_patterns = ['_build'] +linkcheck_anchors = True diff --git a/tests/roots/test-linkcheck/links.txt b/tests/roots/test-linkcheck/links.txt new file mode 100644 index 000000000..36376bef4 --- /dev/null +++ b/tests/roots/test-linkcheck/links.txt @@ -0,0 +1,11 @@ +This is from CPython documentation. + +* Also, if there is a `default namespace <https://www.w3.org/TR/2006/REC-xml-names-20060816/#defaulting>`__, that full URI gets prepended to all of the non-prefixed tags. + +* The URL having anchor: `http://www.sphinx-doc.org/en/1.4.8/tutorial.html#install-sphinx`_ + +Some additional anchors to exercise ignore code + +* `Example Bar invalid <http://example.com/#!bar>`_ +* `Example Bar invalid <http://example.com#!bar>`_ tests that default ignore anchor of #! does not need to be prefixed with / +* `Example Bar invalid <http://example.com/#top>`_ diff --git a/tests/roots/test-maxlistdepth/conf.py b/tests/roots/test-maxlistdepth/conf.py new file mode 100644 index 000000000..5a43b67bf --- /dev/null +++ b/tests/roots/test-maxlistdepth/conf.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' +html_theme = 'classic' +exclude_patterns = ['_build'] + +latex_documents = [ + ('index', 'SphinxTests.tex', 'Testing maxlistdepth=10', + 'Georg Brandl', 'howto'), + ] + +latex_elements = { + 'maxlistdepth': '10', +} diff --git a/tests/roots/test-maxlistdepth/index.rst b/tests/roots/test-maxlistdepth/index.rst new file mode 100644 index 000000000..5d9bc2193 --- /dev/null +++ b/tests/roots/test-maxlistdepth/index.rst @@ -0,0 +1,57 @@ +test-maxlistdepth +================= + + +1. 1 + + 1. 2 + + 1. 3 + + 1. 4 + + 1. 5 + + 1. 6 + + 1. 7 + + 1. 8 + + 1. 9 + + 10a + + - 10b + + .. code-block:: python + + def foo(): + + +- 1 + + - 2 + + - 3 + + - 4 + + - 5 + + - 6 + + - 7 + + - 8 + + 1. 9 + + 10a + + 1. 10b + + .. code-block:: python + + def foo(): + diff --git a/tests/roots/test-numfig/bar.rst b/tests/roots/test-numfig/bar.rst index f86e7475a..c4367c5b8 100644 --- a/tests/roots/test-numfig/bar.rst +++ b/tests/roots/test-numfig/bar.rst @@ -1,7 +1,11 @@ +.. _bar: + === Bar === +.. _bar_a: + Bar A ===== @@ -37,9 +41,13 @@ Bar A print('hello world') +.. _bar_b: + Bar B ===== +.. _bar_b1: + Bar B1 ------ diff --git a/tests/roots/test-numfig/baz.rst b/tests/roots/test-numfig/baz.rst index 42fcb06d1..3ac684b43 100644 --- a/tests/roots/test-numfig/baz.rst +++ b/tests/roots/test-numfig/baz.rst @@ -1,3 +1,5 @@ +.. _baz_a: + Baz A ----- diff --git a/tests/roots/test-numfig/foo.rst b/tests/roots/test-numfig/foo.rst index ef713574a..6b6a8651c 100644 --- a/tests/roots/test-numfig/foo.rst +++ b/tests/roots/test-numfig/foo.rst @@ -1,3 +1,5 @@ +.. _foo: + === Foo === @@ -16,6 +18,8 @@ Foo print('hello world') +.. _foo_a: + Foo A ===== @@ -47,12 +51,18 @@ Foo A print('hello world') +.. _foo_a1: + Foo A1 ------ +.. _foo_b: + Foo B ===== +.. _foo_b1: + Foo B1 ------ diff --git a/tests/roots/test-numfig/index.rst b/tests/roots/test-numfig/index.rst index 6dd39a93a..939903839 100644 --- a/tests/roots/test-numfig/index.rst +++ b/tests/roots/test-numfig/index.rst @@ -1,3 +1,5 @@ +.. _index: + test-tocdepth ============= @@ -48,5 +50,10 @@ test-tocdepth * Table.2.2 is :numref:`Table:%s <table22>` * List.1 is :numref:`CODE_1` * List.2.2 is :numref:`Code-%s <CODE22>` +* Section.1 is :numref:`foo` +* Section.2.1 is :numref:`bar_a` +* Unnumbered section is :numref:`index` * Invalid numfig_format 01: :numref:`invalid <fig1>` * Invalid numfig_format 02: :numref:`Fig %s %s <fig1>` +* Fig.1 is :numref:`Fig.{number} {name} <fig1>` +* Section.1 is :numref:`Sect.{number} {name} <foo>` diff --git a/tests/roots/test-numfig/rimg.png b/tests/roots/test-numfig/rimg.png Binary files differindex 1081dc143..fda6cd29e 100644 --- a/tests/roots/test-numfig/rimg.png +++ b/tests/roots/test-numfig/rimg.png diff --git a/tests/roots/test-refonly_bullet_list/conf.py b/tests/roots/test-refonly_bullet_list/conf.py new file mode 100644 index 000000000..68357c9a4 --- /dev/null +++ b/tests/roots/test-refonly_bullet_list/conf.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' +html_compact_lists = False + +latex_documents = [ + (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report') +] diff --git a/tests/roots/test-refonly_bullet_list/index.rst b/tests/roots/test-refonly_bullet_list/index.rst new file mode 100644 index 000000000..9d8539dba --- /dev/null +++ b/tests/roots/test-refonly_bullet_list/index.rst @@ -0,0 +1,14 @@ +test-refonly_bullet_list +======================== + +List A: + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +List B: + +* Hello +* Sphinx +* World diff --git a/tests/roots/test-search/conf.py b/tests/roots/test-search/conf.py new file mode 100644 index 000000000..38b8b28c5 --- /dev/null +++ b/tests/roots/test-search/conf.py @@ -0,0 +1,3 @@ +master_doc = 'index' +exclude_patterns = ['_build'] +html_search_language = 'en' diff --git a/tests/roots/test-search/index.rst b/tests/roots/test-search/index.rst new file mode 100644 index 000000000..1e0dd93de --- /dev/null +++ b/tests/roots/test-search/index.rst @@ -0,0 +1,30 @@ +meta keywords +============= + +.. meta:: + :keywords lang=en: findthiskey, thistoo, notgerman + :keywords: thisonetoo + :keywords lang=de: onlygerman, onlytoogerman + :description: thisnoteither + +Stemmer +======= + +zfs +findthisstemmedkey + +textinheading + +International + +.. toctree:: + + tocitem + +.. raw:: html + + <span class="raw">rawword"</span> + +.. raw:: latex + + latex_keyword diff --git a/tests/roots/test-search/tocitem.rst b/tests/roots/test-search/tocitem.rst new file mode 100644 index 000000000..61c42a242 --- /dev/null +++ b/tests/roots/test-search/tocitem.rst @@ -0,0 +1,10 @@ +heading 1 +========= + +lorem ipsum + + +textinheading +============= + +lorem ipsum
\ No newline at end of file diff --git a/tests/roots/test-toctree-glob/index.rst b/tests/roots/test-toctree-glob/index.rst index 079cd6027..a3c198ce3 100644 --- a/tests/roots/test-toctree-glob/index.rst +++ b/tests/roots/test-toctree-glob/index.rst @@ -1,8 +1,24 @@ test-toctree-glob ================= +normal order +------------ + +.. toctree:: + :glob: + + foo + bar/index + bar/* + baz + qux/index + +reversed order +------------- + .. toctree:: :glob: + :reversed: foo bar/index diff --git a/tests/roots/test-toctree-maxdepth/qux.rst b/tests/roots/test-toctree-maxdepth/qux.rst new file mode 100644 index 000000000..35e9ac127 --- /dev/null +++ b/tests/roots/test-toctree-maxdepth/qux.rst @@ -0,0 +1,9 @@ +test-toctree-max-depth +====================== + +.. toctree:: + :numbered: + :maxdepth: 4 + + foo + bar diff --git a/tests/roots/test-toctree/bar.rst b/tests/roots/test-toctree/bar.rst new file mode 100644 index 000000000..1cccd3cb7 --- /dev/null +++ b/tests/roots/test-toctree/bar.rst @@ -0,0 +1,2 @@ +bar +=== diff --git a/tests/roots/test-toctree/baz.rst b/tests/roots/test-toctree/baz.rst new file mode 100644 index 000000000..52e2e72ac --- /dev/null +++ b/tests/roots/test-toctree/baz.rst @@ -0,0 +1,2 @@ +baz +=== diff --git a/tests/roots/test-toctree/conf.py b/tests/roots/test-toctree/conf.py new file mode 100644 index 000000000..31e7a6ed4 --- /dev/null +++ b/tests/roots/test-toctree/conf.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' + +latex_documents = [ + (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report') +] diff --git a/tests/roots/test-toctree/foo.rst b/tests/roots/test-toctree/foo.rst new file mode 100644 index 000000000..49f4d4b97 --- /dev/null +++ b/tests/roots/test-toctree/foo.rst @@ -0,0 +1,15 @@ +foo +=== + +.. toctree:: + + quux + +foo.1 +----- + +foo.1-1 +^^^^^^^ + +foo.2 +----- diff --git a/tests/roots/test-toctree/index.rst b/tests/roots/test-toctree/index.rst new file mode 100644 index 000000000..dc7fd2e4a --- /dev/null +++ b/tests/roots/test-toctree/index.rst @@ -0,0 +1,54 @@ +.. Sphinx Tests documentation master file, created by sphinx-quickstart on Wed Jun 4 23:49:58 2008. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Sphinx Tests's documentation! +======================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + :numbered: + :caption: Table of Contents + :name: mastertoc + + foo + bar + http://sphinx-doc.org/ + +.. only:: html + + Section for HTML + ---------------- + + .. toctree:: + + baz + +---------- +subsection +---------- + +subsubsection +------------- + +Test for issue #1157 +==================== + +This used to crash: + +.. toctree:: + +.. toctree:: + :hidden: + + Latest reference <http://sphinx-doc.org/latest/> + Python <http://python.org/> + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/tests/roots/test-toctree/quux.rst b/tests/roots/test-toctree/quux.rst new file mode 100644 index 000000000..07dd0a0a3 --- /dev/null +++ b/tests/roots/test-toctree/quux.rst @@ -0,0 +1,2 @@ +quux +==== diff --git a/tests/roots/test-toctree/qux.rst b/tests/roots/test-toctree/qux.rst new file mode 100644 index 000000000..26176b947 --- /dev/null +++ b/tests/roots/test-toctree/qux.rst @@ -0,0 +1 @@ +qux.rst has no section title diff --git a/tests/roots/test-toctree/tocdepth.rst b/tests/roots/test-toctree/tocdepth.rst new file mode 100644 index 000000000..1069b4cb4 --- /dev/null +++ b/tests/roots/test-toctree/tocdepth.rst @@ -0,0 +1,15 @@ +:tocdepth: 2 + +======= +level 1 +======= + +level 2 +======= + +------- +level 3 +------- + +level 4 +------- diff --git a/tests/run.py b/tests/run.py index 0ca6ad1ae..d2d3b9fc8 100755 --- a/tests/run.py +++ b/tests/run.py @@ -24,7 +24,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir))) # check dependencies before testing print('Checking dependencies...') for modname in ('nose', 'mock', 'six', 'docutils', 'jinja2', 'pygments', - 'snowballstemmer', 'babel'): + 'snowballstemmer', 'babel', 'html5lib'): try: __import__(modname) except ImportError as err: @@ -48,4 +48,4 @@ tempdir.makedirs() print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0]) sys.stdout.flush() -nose.main() +nose.main(argv=sys.argv) diff --git a/tests/test_api_translator.py b/tests/test_api_translator.py index e8cfcb458..7a70fd4c8 100644 --- a/tests/test_api_translator.py +++ b/tests/test_api_translator.py @@ -57,7 +57,7 @@ def test_html_with_set_translator_for_html_(app, status, warning): @with_app('html', testroot='api-set-translator', - confoverrides={'html_translator_class': 'ext.ExtHTMLTranslator'}) + confoverrides={'html_translator_class': 'translator.ExtHTMLTranslator'}) def test_html_with_set_translator_for_html_and_html_translator_class( app, status, warning): # use set_translator() and html_translator_class. diff --git a/tests/test_apidoc.py b/tests/test_apidoc.py index 596890041..ff6a147ca 100644 --- a/tests/test_apidoc.py +++ b/tests/test_apidoc.py @@ -44,6 +44,94 @@ def test_simple(tempdir): @with_tempdir +def test_pep_0420_enabled(tempdir): + codedir = rootdir / 'root' / 'pep_0420' + outdir = tempdir / 'out' + args = ['sphinx-apidoc', '-o', outdir, '-F', codedir, "--implicit-namespaces"] + apidoc.main(args) + + assert (outdir / 'conf.py').isfile() + assert (outdir / 'a.b.c.rst').isfile() + assert (outdir / 'a.b.x.rst').isfile() + + with open(outdir / 'a.b.c.rst') as f: + rst = f.read() + assert "a.b.c package\n" in rst + assert "automodule:: a.b.c.d\n" in rst + assert "automodule:: a.b.c\n" in rst + + with open(outdir / 'a.b.x.rst') as f: + rst = f.read() + assert "a.b.x namespace\n" in rst + assert "automodule:: a.b.x.y\n" in rst + assert "automodule:: a.b.x\n" not in rst + + @with_app('text', srcdir=outdir) + def assert_build(app, status, warning): + app.build() + print(status.getvalue()) + print(warning.getvalue()) + + sys.path.append(codedir) + try: + assert_build() + finally: + sys.path.remove(codedir) + + +@with_tempdir +def test_pep_0420_disabled(tempdir): + codedir = rootdir / 'root' / 'pep_0420' + outdir = tempdir / 'out' + args = ['sphinx-apidoc', '-o', outdir, '-F', codedir] + apidoc.main(args) + + assert (outdir / 'conf.py').isfile() + assert not (outdir / 'a.b.c.rst').exists() + assert not (outdir / 'a.b.x.rst').exists() + + @with_app('text', srcdir=outdir) + def assert_build(app, status, warning): + app.build() + print(status.getvalue()) + print(warning.getvalue()) + + sys.path.append(codedir) + try: + assert_build() + finally: + sys.path.remove(codedir) + +@with_tempdir +def test_pep_0420_disabled_top_level_verify(tempdir): + codedir = rootdir / 'root' / 'pep_0420' / 'a' / 'b' + outdir = tempdir / 'out' + args = ['sphinx-apidoc', '-o', outdir, '-F', codedir] + apidoc.main(args) + + assert (outdir / 'conf.py').isfile() + assert (outdir / 'c.rst').isfile() + assert not (outdir / 'x.rst').exists() + + with open(outdir / 'c.rst') as f: + rst = f.read() + assert "c package\n" in rst + assert "automodule:: c.d\n" in rst + assert "automodule:: c\n" in rst + + @with_app('text', srcdir=outdir) + def assert_build(app, status, warning): + app.build() + print(status.getvalue()) + print(warning.getvalue()) + + sys.path.append(codedir) + try: + assert_build() + finally: + sys.path.remove(codedir) + +@with_tempdir def test_multibyte_parameters(tempdir): codedir = rootdir / 'root' outdir = tempdir / 'out' diff --git a/tests/test_application.py b/tests/test_application.py index 420680451..ad4f84870 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -14,7 +14,7 @@ from docutils import nodes from sphinx.application import ExtensionError from sphinx.domains import Domain -from util import with_app, raises_msg +from util import with_app, raises_msg, strip_escseq @with_app() @@ -60,20 +60,21 @@ def test_output(app, status, warning): old_count = app._warncount app.warn("Bad news!") - assert warning.getvalue() == "WARNING: Bad news!\n" + assert strip_escseq(warning.getvalue()) == "WARNING: Bad news!\n" assert app._warncount == old_count + 1 @with_app() def test_extensions(app, status, warning): app.setup_extension('shutil') - assert warning.getvalue().startswith("WARNING: extension 'shutil'") + assert strip_escseq(warning.getvalue()).startswith("WARNING: extension 'shutil'") @with_app() def test_extension_in_blacklist(app, status, warning): app.setup_extension('sphinxjp.themecore') - assert warning.getvalue().startswith("WARNING: the extension 'sphinxjp.themecore' was") + msg = strip_escseq(warning.getvalue()) + assert msg.startswith("WARNING: the extension 'sphinxjp.themecore' was") @with_app() diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 711e5e807..d2ba95608 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -14,6 +14,7 @@ from util import TestApp, Struct, raises, SkipTest # NOQA from nose.tools import with_setup, eq_ +import enum from six import StringIO from docutils.statemachine import ViewList @@ -825,6 +826,26 @@ def test_generate(): del directive.env.temp_data['autodoc:module'] del directive.env.ref_context['py:module'] + # test members with enum attributes + directive.env.ref_context['py:module'] = 'test_autodoc' + options.inherited_members = False + options.undoc_members = False + options.members = ALL + assert_processes([ + ('class', 'test_autodoc.EnumCls'), + ('attribute', 'test_autodoc.EnumCls.val1'), + ('attribute', 'test_autodoc.EnumCls.val2'), + ('attribute', 'test_autodoc.EnumCls.val3'), + ], 'class', 'EnumCls') + assert_result_contains( + ' :annotation: = 12', 'attribute', 'EnumCls.val1') + assert_result_contains( + ' :annotation: = 23', 'attribute', 'EnumCls.val2') + assert_result_contains( + ' :annotation: = 34', 'attribute', 'EnumCls.val3') + del directive.env.temp_data['autodoc:class'] + del directive.env.temp_data['autodoc:module'] + # test descriptor class documentation options.members = ['CustomDataDescriptor'] assert_result_contains('.. py:class:: CustomDataDescriptor(doc)', @@ -832,6 +853,17 @@ def test_generate(): assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()', 'module', 'test_autodoc') + # test mocked module imports + options.members = ['TestAutodoc'] + options.undoc_members = False + assert_result_contains('.. py:class:: TestAutodoc', + 'module', 'autodoc_missing_imports') + assert_result_contains(' .. py:method:: TestAutodoc.decoratedMethod()', + 'module', 'autodoc_missing_imports') + options.members = ['decoratedFunction'] + assert_result_contains('.. py:function:: decoratedFunction()', + 'module', 'autodoc_missing_imports') + # --- generate fodder ------------ __all__ = ['Class'] @@ -1020,12 +1052,24 @@ class InstAttCls(object): """Docstring for instance attribute InstAttCls.ia2.""" +class EnumCls(enum.Enum): + """ + this is enum class + """ + + #: doc for val1 + val1 = 12 + val2 = 23 #: doc for val2 + val3 = 34 + """doc for val3""" + + def test_type_hints(): from sphinx.ext.autodoc import formatargspec from sphinx.util.inspect import getargspec try: - from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10 + from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11 except (ImportError, SyntaxError): raise SkipTest('Cannot import Python code with function annotations') @@ -1052,16 +1096,19 @@ def test_type_hints(): # Keyword-only arguments verify_arg_spec(f5, '(x: int, *, y: str, z: str) -> None') + # Keyword-only arguments with varargs + verify_arg_spec(f6, '(x: int, *args, y: str, z: str) -> None') + # Space around '=' for defaults - verify_arg_spec(f6, '(x: int = None, y: dict = {}) -> None') + verify_arg_spec(f7, '(x: int = None, y: dict = {}) -> None') # Callable types - verify_arg_spec(f7, '(x: typing.Callable[[int, str], int]) -> None') - verify_arg_spec(f8, '(x: typing.Callable) -> None') + verify_arg_spec(f8, '(x: typing.Callable[[int, str], int]) -> None') + verify_arg_spec(f9, '(x: typing.Callable) -> None') # Tuple types - verify_arg_spec(f9, '(x: typing.Tuple[int, str],' - ' y: typing.Tuple[int, ...]) -> None') + verify_arg_spec(f10, '(x: typing.Tuple[int, str],' + ' y: typing.Tuple[int, ...]) -> None') # Instance annotations - verify_arg_spec(f10, '(x: CustomAnnotation, y: 123) -> None') + verify_arg_spec(f11, '(x: CustomAnnotation, y: 123) -> None') diff --git a/tests/test_build.py b/tests/test_build.py index 507a1cab3..cc34de2c1 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -13,6 +13,7 @@ from six import BytesIO import pickle from docutils import nodes +import mock from textwrap import dedent from sphinx.errors import SphinxError import sphinx.builders.linkcheck @@ -25,14 +26,11 @@ except ImportError: ManWriter = None -class MockOpener(object): - def open(self, req, **kwargs): - class result(BytesIO): - headers = None - url = req.url - return result() - -sphinx.builders.linkcheck.opener = MockOpener() +def request_session_head(url, **kwargs): + response = mock.Mock() + response.status_code = 200 + response.url = url + return response def verify_build(buildername, srcdir): @@ -68,12 +66,15 @@ def test_build_all(): """ % {'test_name': test_name}) ) - # 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', 'epub3', - 'applehelp', 'changes', 'xml', 'pseudoxml', 'man', - 'linkcheck']: - yield verify_build, buildername, srcdir + with mock.patch('sphinx.builders.linkcheck.requests') as requests: + requests.head = request_session_head + + # 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', 'epub2', 'epub', + 'applehelp', 'changes', 'xml', 'pseudoxml', 'man', + 'linkcheck']: + yield verify_build, buildername, srcdir @with_tempdir diff --git a/tests/test_build_html.py b/tests/test_build_html.py index d77867c0e..d8aff88ab 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -13,13 +13,16 @@ import os import re from six import PY3, iteritems -from six.moves import html_entities from sphinx import __display_version__ -from util import remove_unicode_literals, gen_with_app, with_app -from etree13 import ElementTree as ET +from util import remove_unicode_literals, gen_with_app, with_app, strip_escseq +from etree13 import ElementTree +from html5lib import getTreeBuilder, HTMLParser +TREE_BUILDER = getTreeBuilder('etree', implementation=ElementTree) +HTML_PARSER = HTMLParser(TREE_BUILDER, namespaceHTMLElements=False) + ENV_WARNINGS = """\ (%(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \ WARNING: duplicate object description of autodoc_fodder.MarkupError, other \ @@ -65,6 +68,7 @@ HTML_XPATH = { (".//img[@src='_images/img1.png']", ''), (".//img[@src='_images/simg.png']", ''), (".//img[@src='_images/svgimg.svg']", ''), + (".//a[@href='_sources/images.txt']", ''), ], 'subdir/images.html': [ (".//img[@src='../_images/img1.png']", ''), @@ -174,7 +178,7 @@ HTML_XPATH = { # ``seealso`` directive (".//div/p[@class='first admonition-title']", 'See also'), # a ``hlist`` directive - (".//table[@class='hlist']/tr/td/ul/li", '^This$'), + (".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'), # a ``centered`` directive (".//p[@class='centered']/strong", 'LICENSE'), # a glossary @@ -203,6 +207,7 @@ HTML_XPATH = { # docfields (".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'), (".//a[@class='reference internal'][@href='#Time']", 'Time'), + (".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'), # C references (".//span[@class='pre']", 'CFunction()'), (".//a[@href='#c.Sphinx_DoSomething']", ''), @@ -235,10 +240,15 @@ HTML_XPATH = { (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'), (".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')), # others - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-+p']/code/span", + (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span", 'perl'), - (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-+p']/code/span", + (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span", '\+p'), + (".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span", + '--plugin.option'), + (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']" + "/code/span", + 'create-auth-token'), (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span", 'arg'), (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span", @@ -297,7 +307,7 @@ HTML_XPATH = { (".//a/strong", "[1]"), (".//a/strong", "Other"), (".//a", "entry"), - (".//dt/a", "double"), + (".//li/a", "double"), ], 'footnote.html': [ (".//a[@class='footnote-reference'][@href='#id7'][@id='id1']", r"\[1\]"), @@ -315,25 +325,11 @@ HTML_XPATH = { ], 'otherext.html': [ (".//h1", "Generated section"), + (".//a[@href='_sources/otherext.foo.txt']", ''), ] } -class NslessParser(ET.XMLParser): - """XMLParser that throws away namespaces in tag names.""" - - def _fixname(self, key): - try: - return self._names[key] - except KeyError: - name = key - br = name.find('}') - if br > 0: - name = name[br+1:] - self._names[key] = name = self._fixtext(name) - return name - - def check_xpath(etree, fname, path, check, be_found=True): nodes = list(etree.findall(path)) if check is None: @@ -394,8 +390,7 @@ def check_extra_entries(outdir): @with_app(buildername='html', testroot='warnings', freshenv=True) def test_html_warnings(app, status, warning): app.builder.build_all() - - html_warnings = warning.getvalue().replace(os.sep, '/') + html_warnings = strip_escseq(warning.getvalue().replace(os.sep, '/')) html_warnings_exp = HTML_WARNINGS % { 'root': re.escape(app.srcdir.replace(os.sep, '/'))} assert re.match(html_warnings_exp + '$', html_warnings), \ @@ -409,10 +404,8 @@ def test_html_warnings(app, status, warning): def test_html_output(app, status, warning): app.builder.build_all() for fname, paths in iteritems(HTML_XPATH): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for path, check in paths: yield check_xpath, etree, fname, path, check @@ -459,10 +452,8 @@ def test_tocdepth(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -501,10 +492,8 @@ def test_tocdepth_singlehtml(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -514,10 +503,11 @@ def test_tocdepth_singlehtml(app, status, warning): def test_numfig_disabled(app, status, warning): app.builder.build_all() - assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' - in warning.getvalue()) - assert 'index.rst:51: WARNING: invalid numfig_format: invalid' not in warning.getvalue() - assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' not in warning.getvalue() + warnings = warning.getvalue() + assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' in warnings + assert 'index.rst:55: WARNING: no number is assigned for section: index' not in warnings + assert 'index.rst:56: WARNING: invalid numfig_format: invalid' not in warnings + assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' not in warnings expects = { 'index.html': [ @@ -532,6 +522,10 @@ def test_numfig_disabled(app, status, warning): (".//li/code/span", '^Table:%s$', True), (".//li/code/span", '^CODE_1$', True), (".//li/code/span", '^Code-%s$', True), + (".//li/code/span", '^foo$', True), + (".//li/code/span", '^bar_a$', True), + (".//li/code/span", '^Fig.{number}$', True), + (".//li/code/span", '^Sect.{number}$', True), ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" @@ -557,10 +551,8 @@ def test_numfig_disabled(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -575,10 +567,11 @@ def test_numfig_without_numbered_toctree(app, status, warning): (app.srcdir / 'index.rst').write_text(index, encoding='utf-8') app.builder.build_all() - assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' - not in warning.getvalue()) - assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() - assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() + warnings = warning.getvalue() + assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings + assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings + assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings + assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings expects = { 'index.html': [ @@ -600,6 +593,10 @@ def test_numfig_without_numbered_toctree(app, status, warning): (".//li/a/span", '^Table:6$', True), (".//li/a/span", '^Listing 9$', True), (".//li/a/span", '^Code-6$', True), + (".//li/code/span", '^foo$', True), + (".//li/code/span", '^bar_a$', True), + (".//li/a/span", '^Fig.9 should be Fig.1$', True), + (".//li/code/span", '^Sect.{number}$', True), ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" @@ -658,10 +655,8 @@ def test_numfig_without_numbered_toctree(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -672,10 +667,11 @@ def test_numfig_without_numbered_toctree(app, status, warning): def test_numfig_with_numbered_toctree(app, status, warning): app.builder.build_all() - assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' - not in warning.getvalue()) - assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() - assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() + warnings = warning.getvalue() + assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings + assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings + assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings + assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings expects = { 'index.html': [ @@ -697,6 +693,10 @@ def test_numfig_with_numbered_toctree(app, status, warning): (".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Code-2.2$', True), + (".//li/a/span", '^Section.1$', True), + (".//li/a/span", '^Section.2.1$', True), + (".//li/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" @@ -755,10 +755,8 @@ def test_numfig_with_numbered_toctree(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -768,14 +766,16 @@ def test_numfig_with_numbered_toctree(app, status, warning): confoverrides={'numfig': True, 'numfig_format': {'figure': 'Figure:%s', 'table': 'Tab_%s', - 'code-block': 'Code-%s'}}) + 'code-block': 'Code-%s', + 'section': 'SECTION-%s'}}) def test_numfig_with_prefix(app, status, warning): app.builder.build_all() - assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' - not in warning.getvalue()) - assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() - assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() + warnings = warning.getvalue() + assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings + assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings + assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings + assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings expects = { 'index.html': [ @@ -797,6 +797,10 @@ def test_numfig_with_prefix(app, status, warning): (".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Code-1$', True), (".//li/a/span", '^Code-2.2$', True), + (".//li/a/span", '^SECTION-1$', True), + (".//li/a/span", '^SECTION-2.1$', True), + (".//li/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" @@ -855,10 +859,8 @@ def test_numfig_with_prefix(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -869,10 +871,11 @@ def test_numfig_with_prefix(app, status, warning): def test_numfig_with_secnum_depth(app, status, warning): app.builder.build_all() - assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' - not in warning.getvalue()) - assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() - assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() + warnings = warning.getvalue() + assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings + assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings + assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings + assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings expects = { 'index.html': [ @@ -894,6 +897,10 @@ def test_numfig_with_secnum_depth(app, status, warning): (".//li/a/span", '^Table:2.1.2$', True), (".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Code-2.1.2$', True), + (".//li/a/span", '^Section.1$', True), + (".//li/a/span", '^Section.2.1$', True), + (".//li/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/a/span", '^Sect.1 Foo$', True), ], 'foo.html': [ (".//div[@class='figure']/p[@class='caption']/" @@ -952,10 +959,8 @@ def test_numfig_with_secnum_depth(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -986,6 +991,10 @@ def test_numfig_with_singlehtml(app, status, warning): (".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Code-2.2$', True), + (".//li/a/span", '^Section.1$', True), + (".//li/a/span", '^Section.2.1$', True), + (".//li/a/span", '^Fig.1 should be Fig.1$', True), + (".//li/a/span", '^Sect.1 Foo$', True), (".//div[@class='figure']/p[@class='caption']/" "span[@class='caption-number']", '^Fig. 1.1 $', True), (".//div[@class='figure']/p[@class='caption']/" @@ -1038,10 +1047,8 @@ def test_numfig_with_singlehtml(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found @@ -1070,19 +1077,30 @@ def test_enumerable_node(app, status, warning): } for fname, paths in iteritems(expects): - parser = NslessParser() - parser.entity.update(html_entities.entitydefs) with (app.outdir / fname).open('rb') as fp: - etree = ET.parse(fp, parser) + etree = HTML_PARSER.parse(fp) for xpath, check, be_found in paths: yield check_xpath, etree, fname, xpath, check, be_found -@with_app(buildername='html', testroot='html_extra_path') -def test_html_extra_path(app, status, warning): +@with_app(buildername='html', testroot='html_assets') +def test_html_assets(app, status, warning): app.builder.build_all() + # html_static_path + assert not (app.outdir / '_static' / '.htaccess').exists() + assert not (app.outdir / '_static' / '.htpasswd').exists() + assert (app.outdir / '_static' / 'API.html').exists() + assert (app.outdir / '_static' / 'API.html').text() == 'Sphinx-1.4.4' + assert (app.outdir / '_static' / 'css/style.css').exists() + assert (app.outdir / '_static' / 'rimg.png').exists() + assert not (app.outdir / '_static' / '_build/index.html').exists() + assert (app.outdir / '_static' / 'background.png').exists() + assert not (app.outdir / '_static' / 'subdir' / '.htaccess').exists() + assert not (app.outdir / '_static' / 'subdir' / '.htpasswd').exists() + + # html_extra_path assert (app.outdir / '.htaccess').exists() assert not (app.outdir / '.htpasswd').exists() assert (app.outdir / 'API.html_t').exists() @@ -1090,3 +1108,17 @@ def test_html_extra_path(app, status, warning): assert (app.outdir / 'rimg.png').exists() assert not (app.outdir / '_build/index.html').exists() assert (app.outdir / 'background.png').exists() + assert (app.outdir / 'subdir' / '.htaccess').exists() + assert not (app.outdir / 'subdir' / '.htpasswd').exists() + + +@with_app(buildername='html', confoverrides={'html_sourcelink_suffix': ''}) +def test_html_sourcelink_suffix(app, status, warning): + app.builder.build_all() + content_otherext = (app.outdir / 'otherext.html').text() + content_images = (app.outdir / 'images.html').text() + + assert '<a href="_sources/otherext.foo"' in content_otherext + assert '<a href="_sources/images.txt"' in content_images + assert (app.outdir / '_sources' / 'otherext.foo').exists() + assert (app.outdir / '_sources' / 'images.txt').exists() diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 77ba4ca85..fcef77be6 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -12,17 +12,26 @@ from __future__ import print_function import os import re +from functools import wraps +from itertools import product from subprocess import Popen, PIPE from six import PY3 from sphinx.errors import SphinxError +from sphinx.util.osutil import cd, ensuredir from sphinx.writers.latex import LaTeXTranslator -from util import SkipTest, remove_unicode_literals, with_app +from util import SkipTest, remove_unicode_literals, with_app, strip_escseq, skip_if from test_build_html import ENV_WARNINGS +LATEX_ENGINES = ['pdflatex', 'lualatex', 'xelatex'] +DOCCLASSES = ['howto', 'manual'] +STYLEFILES = ['article.cls', 'fancyhdr.sty', 'titlesec.sty', 'amsmath.sty', + 'framed.sty', 'color.sty', 'fancyvrb.sty', 'threeparttable.sty', + 'fncychap.sty', 'geometry.sty', 'kvoptions.sty', 'hyperref.sty'] + LATEX_WARNINGS = ENV_WARNINGS + """\ %(root)s/index.rst:\\d+: WARNING: unknown option: &option %(root)s/index.rst:\\d+: WARNING: citation not found: missing @@ -34,70 +43,71 @@ if PY3: LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) -def run_latex(outdir): - """Run pdflatex, xelatex, and lualatex in the outdir""" - cwd = os.getcwd() - os.chdir(outdir) +# only run latex if all needed packages are there +def kpsetest(*filenames): try: - latexes = ('pdflatex', 'xelatex', 'lualatex') - available_latexes = len(latexes) - for latex in latexes: - try: - os.mkdir(latex) - p = Popen([latex, '--interaction=nonstopmode', - '-output-directory=%s' % latex, 'SphinxTests.tex'], - stdout=PIPE, stderr=PIPE) - except OSError: # most likely the latex executable was not found - available_latexes -= 1 - else: - stdout, stderr = p.communicate() - if p.returncode != 0: - print(stdout) - print(stderr) - assert False, '%s exited with return code %s' % ( - latex, p.returncode) - finally: - os.chdir(cwd) - - if available_latexes == 0: # no latex is available, skip the test - raise SkipTest + p = Popen(['kpsewhich'] + list(filenames), stdout=PIPE) + except OSError: + # no kpsewhich... either no tex distribution is installed or it is + # a "strange" one -- don't bother running latex + return None + else: + p.communicate() + if p.returncode != 0: + # not found + return False + # found + return True + + +# compile latex document with app.config.latex_engine +def compile_latex_document(app): + # now, try to run latex over it + with cd(app.outdir): + try: + ensuredir(app.config.latex_engine) + p = Popen([app.config.latex_engine, + '--interaction=nonstopmode', + '-output-directory=%s' % app.config.latex_engine, + 'SphinxTests.tex'], + stdout=PIPE, stderr=PIPE) + except OSError: # most likely the latex executable was not found + raise SkipTest + else: + stdout, stderr = p.communicate() + if p.returncode != 0: + print(stdout) + print(stderr) + assert False, '%s exited with return code %s' % ( + app.config.latex_engine, p.returncode) + +def skip_if_stylefiles_notfound(testfunc): + if kpsetest(*STYLEFILES) is False: + return skip_if(testfunc, + 'not running latex, the required styles do not seem to be installed') + else: + return testfunc + +def test_latex(): + for engine, docclass in product(LATEX_ENGINES, DOCCLASSES): + yield build_latex_doc, engine, docclass + + +@skip_if_stylefiles_notfound @with_app(buildername='latex') -def test_latex(app, status, warning): +def build_latex_doc(app, status, warning, engine, docclass): + app.config.latex_engine = engine + app.config.latex_documents[0] = app.config.latex_documents[0][:4] + (docclass,) + LaTeXTranslator.ignore_missing_images = True app.builder.build_all() # file from latex_additional_files assert (app.outdir / 'svgimg.svg').isfile() - # only run latex if all needed packages are there - def kpsetest(filename): - try: - p = Popen(['kpsewhich', filename], stdout=PIPE) - except OSError: - # no kpsewhich... either no tex distribution is installed or it is - # a "strange" one -- don't bother running latex - return None - else: - p.communicate() - if p.returncode != 0: - # not found - return False - # found - return True - - if kpsetest('article.sty') is None: - raise SkipTest('not running latex, it doesn\'t seem to be installed') - for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', - 'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty', - 'threeparttable.sty']: - if not kpsetest(filename): - raise SkipTest('not running latex, the %s package doesn\'t ' - 'seem to be installed' % filename) - - # now, try to run latex over it - run_latex(app.outdir) + compile_latex_document(app) @with_app(buildername='latex') @@ -126,53 +136,11 @@ def test_writer(app, status, warning): '\\end{wrapfigure}' in result) -@with_app(buildername='latex', - confoverrides={'latex_documents': [ - ('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation', - 'Georg Brandl \\and someone else', 'howto'), - ]}, - srcdir='latex_howto') -def test_latex_howto(app, status, warning): - LaTeXTranslator.ignore_missing_images = True - app.builder.build_all() - - # file from latex_additional_files - assert (app.outdir / 'svgimg.svg').isfile() - - # only run latex if all needed packages are there - def kpsetest(filename): - try: - p = Popen(['kpsewhich', filename], stdout=PIPE) - except OSError: - # no kpsewhich... either no tex distribution is installed or it is - # a "strange" one -- don't bother running latex - return None - else: - p.communicate() - if p.returncode != 0: - # not found - return False - # found - return True - - if kpsetest('article.sty') is None: - raise SkipTest('not running latex, it doesn\'t seem to be installed') - for filename in ['fancyhdr.sty', 'fancybox.sty', 'titlesec.sty', - 'amsmath.sty', 'framed.sty', 'color.sty', 'fancyvrb.sty', - 'threeparttable.sty']: - if not kpsetest(filename): - raise SkipTest('not running latex, the %s package doesn\'t ' - 'seem to be installed' % filename) - - # now, try to run latex over it - run_latex(app.outdir) - - @with_app(buildername='latex', testroot='warnings', freshenv=True) def test_latex_warnings(app, status, warning): app.builder.build_all() - warnings = warning.getvalue().replace(os.sep, '/') + warnings = strip_escseq(warning.getvalue().replace(os.sep, '/')) warnings_exp = LATEX_WARNINGS % { 'root': re.escape(app.srcdir.replace(os.sep, '/'))} assert re.match(warnings_exp + '$', warnings), \ @@ -191,20 +159,25 @@ def test_numref(app, status, warning): print(warning.getvalue()) assert '\\addto\\captionsenglish{\\renewcommand{\\figurename}{Fig.\\@ }}' in result assert '\\addto\\captionsenglish{\\renewcommand{\\tablename}{Table }}' in result - assert '\\SetupFloatingEnvironment{literal-block}{name=Listing }' in result + assert '\\addto\\captionsenglish{\\renewcommand{\\literalblockname}{Listing }}' in result assert '\\hyperref[index:fig1]{Fig.\\@ \\ref{index:fig1}}' in result assert '\\hyperref[baz:fig22]{Figure\\ref{baz:fig22}}' in result assert '\\hyperref[index:table-1]{Table \\ref{index:table-1}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[index:code-1]{Listing \\ref{index:code-1}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result + assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result + assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result + assert '\\hyperref[index:fig1]{Fig.\\ref{index:fig1} \\nameref{index:fig1}}' in result + assert '\\hyperref[foo:foo]{Sect.\\ref{foo:foo} \\nameref{foo:foo}}' in result @with_app(buildername='latex', testroot='numfig', confoverrides={'numfig': True, 'numfig_format': {'figure': 'Figure:%s', 'table': 'Tab_%s', - 'code-block': 'Code-%s'}}) + 'code-block': 'Code-%s', + 'section': 'SECTION-%s'}}) def test_numref_with_prefix1(app, status, warning): app.builder.build_all() result = (app.outdir / 'Python.tex').text(encoding='utf8') @@ -213,7 +186,7 @@ def test_numref_with_prefix1(app, status, warning): print(warning.getvalue()) assert '\\addto\\captionsenglish{\\renewcommand{\\figurename}{Figure:}}' in result assert '\\addto\\captionsenglish{\\renewcommand{\\tablename}{Tab\\_}}' in result - assert '\\SetupFloatingEnvironment{literal-block}{name=Code-}' in result + assert '\\addto\\captionsenglish{\\renewcommand{\\literalblockname}{Code-}}' in result assert '\\ref{index:fig1}' in result assert '\\ref{baz:fig22}' in result assert '\\ref{index:table-1}' in result @@ -226,13 +199,18 @@ def test_numref_with_prefix1(app, status, warning): assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result + assert '\\hyperref[foo:foo]{SECTION-\\ref{foo:foo}}' in result + assert '\\hyperref[bar:bar-a]{SECTION-\\ref{bar:bar-a}}' in result + assert '\\hyperref[index:fig1]{Fig.\\ref{index:fig1} \\nameref{index:fig1}}' in result + assert '\\hyperref[foo:foo]{Sect.\\ref{foo:foo} \\nameref{foo:foo}}' in result @with_app(buildername='latex', testroot='numfig', confoverrides={'numfig': True, 'numfig_format': {'figure': 'Figure:%s.', 'table': 'Tab_%s:', - 'code-block': 'Code-%s | '}}) + 'code-block': 'Code-%s | ', + 'section': 'SECTION_%s_'}}) def test_numref_with_prefix2(app, status, warning): app.builder.build_all() result = (app.outdir / 'Python.tex').text(encoding='utf8') @@ -243,13 +221,17 @@ def test_numref_with_prefix2(app, status, warning): assert '\\def\\fnum@figure{\\figurename\\thefigure.\\@}' in result assert '\\addto\\captionsenglish{\\renewcommand{\\tablename}{Tab\\_}}' in result assert '\\def\\fnum@table{\\tablename\\thetable:}' in result - assert '\\SetupFloatingEnvironment{literal-block}{name=Code-}' in result + assert '\\addto\\captionsenglish{\\renewcommand{\\literalblockname}{Code-}}' in result assert '\\hyperref[index:fig1]{Figure:\\ref{index:fig1}.\\@}' in result assert '\\hyperref[baz:fig22]{Figure\\ref{baz:fig22}}' in result assert '\\hyperref[index:table-1]{Tab\\_\\ref{index:table-1}:}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1} \\textbar{} }' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result + assert '\\hyperref[foo:foo]{SECTION\\_\\ref{foo:foo}\\_}' in result + assert '\\hyperref[bar:bar-a]{SECTION\\_\\ref{bar:bar-a}\\_}' in result + assert '\\hyperref[index:fig1]{Fig.\\ref{index:fig1} \\nameref{index:fig1}}' in result + assert '\\hyperref[foo:foo]{Sect.\\ref{foo:foo} \\nameref{foo:foo}}' in result @with_app(buildername='latex', testroot='numfig', @@ -262,13 +244,17 @@ def test_numref_with_language_ja(app, status, warning): print(warning.getvalue()) assert u'\\renewcommand{\\figurename}{\u56f3 }' in result assert '\\renewcommand{\\tablename}{TABLE }' in result - assert '\\SetupFloatingEnvironment{literal-block}{name=LIST }' in result + assert '\\renewcommand{\\literalblockname}{LIST }' in result assert u'\\hyperref[index:fig1]{\u56f3 \\ref{index:fig1}}' in result assert '\\hyperref[baz:fig22]{Figure\\ref{baz:fig22}}' in result assert '\\hyperref[index:table-1]{TABLE \\ref{index:table-1}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[index:code-1]{LIST \\ref{index:code-1}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result + assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result + assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result + assert '\\hyperref[index:fig1]{Fig.\\ref{index:fig1} \\nameref{index:fig1}}' in result + assert '\\hyperref[foo:foo]{Sect.\\ref{foo:foo} \\nameref{foo:foo}}' in result @with_app(buildername='latex') @@ -408,22 +394,26 @@ def test_footnote(app, status, warning): print(result) print(status.getvalue()) print(warning.getvalue()) - assert '\\footnote[1]{\sphinxAtStartFootnote%\nnumbered\n}' in result - assert '\\footnote[2]{\sphinxAtStartFootnote%\nauto numbered\n}' in result - assert '\\footnote[3]{\sphinxAtStartFootnote%\nnamed\n}' in result + assert ('\\begin{footnote}[1]\\sphinxAtStartFootnote\nnumbered\n%\n' + '\\end{footnote}') in result + assert ('\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n' + '\\end{footnote}') in result + assert '\\begin{footnote}[3]\\sphinxAtStartFootnote\nnamed\n%\n\\end{footnote}' in result assert '{\\hyperref[footnote:bar]{\\sphinxcrossref{{[}bar{]}}}}' in result assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} ' in result assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} \ncite' in result assert '\\bibitem[bar]{bar}{\\phantomsection\\label{footnote:bar} \ncite\n}' in result - assert '\\capstart\\caption{Table caption \\protect\\footnotemark[4]}' in result - assert 'name \\protect\\footnotemark[5]' in result - assert ('\\end{threeparttable}\n\n' - '\\footnotetext[4]{\sphinxAtStartFootnote%\nfootnotes in table caption\n}' - '\\footnotetext[5]{\sphinxAtStartFootnote%\nfootnotes in table\n}' in result) + assert '\\caption{Table caption \\sphinxfootnotemark[4]' in result + assert 'name \\sphinxfootnotemark[5]' in result + assert ('\\end{threeparttable}\n\n%\n' + '\\begin{footnotetext}[4]\sphinxAtStartFootnote\n' + 'footnotes in table caption\n%\n\\end{footnotetext}%\n' + '\\begin{footnotetext}[5]\sphinxAtStartFootnote\n' + 'footnotes in table\n%\n\\end{footnotetext}') in result @with_app(buildername='latex', testroot='footnotes') -def test_reference_in_caption(app, status, warning): +def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning): app.builder.build_all() result = (app.outdir / 'Python.tex').text(encoding='utf8') print(result) @@ -434,20 +424,27 @@ def test_reference_in_caption(app, status, warning): assert '\\chapter{The section with a reference to {[}AuthorYear{]}}' in result assert '\\caption{The table title with a reference to {[}AuthorYear{]}}' in result assert '\\paragraph{The rubric title with a reference to {[}AuthorYear{]}}' in result - assert ('\\chapter{The section with a reference to \\protect\\footnotemark[4]}\n' + assert ('\\chapter{The section with a reference to \\sphinxfootnotemark[4]}\n' '\\label{index:the-section-with-a-reference-to}' - '\\footnotetext[4]{\sphinxAtStartFootnote%\nFootnote in section\n}' in result) + '%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' + 'Footnote in section\n%\n\\end{footnotetext}') in result assert ('\\caption{This is the figure caption with a footnote to ' - '\\protect\\footnotemark[6].}\label{index:id23}\end{figure}\n' - '\\footnotetext[6]{\sphinxAtStartFootnote%\nFootnote in caption\n}')in result - assert ('\\caption{footnote \\protect\\footnotemark[7] ' - 'in caption of normal table}') in result - assert ('\\end{threeparttable}\n\n\\footnotetext[7]{\sphinxAtStartFootnote%\n' - 'Foot note in table\n}' in result) - assert ('\\caption{footnote \\protect\\footnotemark[8] in caption of longtable}' - in result) - assert ('\end{longtable}\n\n\\footnotetext[8]{\sphinxAtStartFootnote%\n' - 'Foot note in longtable\n}' in result) + '\\sphinxfootnotemark[6].}\label{index:id27}\end{figure}\n' + '%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n' + 'Footnote in caption\n%\n\\end{footnotetext}')in result + assert ('\\caption{footnote \\sphinxfootnotemark[7] ' + 'in caption of normal table}\\label{index:id28}') in result + assert ('\\caption{footnote \\sphinxfootnotemark[8] ' + 'in caption \sphinxfootnotemark[9] of longtable}') in result + assert ('\end{longtable}\n\n%\n\\begin{footnotetext}[8]' + '\sphinxAtStartFootnote\n' + 'Foot note in longtable\n%\n\\end{footnotetext}' in result) + assert ('This is a reference to the code-block in the footnote:\n' + '{\hyperref[index:codeblockinfootnote]{\\sphinxcrossref{\\DUrole' + '{std,std-ref}{I am in a footnote}}}}') in result + assert ('&\nThis is one more footnote with some code in it ' + '\\sphinxfootnotemark[10].\n\\\\') in result + assert '\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]' in result @with_app(buildername='latex', testroot='footnotes', @@ -458,29 +455,32 @@ def test_latex_show_urls_is_inline(app, status, warning): print(result) print(status.getvalue()) print(warning.getvalue()) - assert ('Same footnote number \\footnote[1]{\sphinxAtStartFootnote%\n' - 'footnote in bar\n} in bar.rst' in result) - assert ('Auto footnote number \\footnote[1]{\sphinxAtStartFootnote%\n' - 'footnote in baz\n} in baz.rst' in result) - assert ('\\phantomsection\\label{index:id26}{\\hyperref[index:the\\string-section' + assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'footnote in bar\n%\n\\end{footnote} in bar.rst' in result) + assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'footnote in baz\n%\n\\end{footnote} in baz.rst' in result) + assert ('\\phantomsection\\label{index:id30}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to\\string-authoryear]' '{\\sphinxcrossref{The section with a reference to ' '\\phantomsection\\label{index:id1}' '{\\hyperref[index:authoryear]{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}' in result) - assert ('\\phantomsection\\label{index:id27}{\\hyperref[index:the\\string-section' + assert ('\\phantomsection\\label{index:id31}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to]' '{\\sphinxcrossref{The section with a reference to }}}' in result) - assert 'First footnote: \\footnote[2]{\sphinxAtStartFootnote%\nFirst\n}' in result - assert 'Second footnote: \\footnote[1]{\sphinxAtStartFootnote%\nSecond\n}' in result + assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' + 'First\n%\n\\end{footnote}') in result + assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'Second\n%\n\\end{footnote}') in result assert '\\href{http://sphinx-doc.org/}{Sphinx} (http://sphinx-doc.org/)' in result - assert 'Third footnote: \\footnote[3]{\sphinxAtStartFootnote%\nThird\n}' in result + assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' + 'Third\n%\n\\end{footnote}') in result assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde} ' '(http://sphinx-doc.org/\\textasciitilde{}test/)' in result) assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term} (http://sphinx-doc.org/)}] ' '\\leavevmode\nDescription' in result) - assert ('\\item[{Footnote in term \\protect\\footnotemark[5]}] ' - '\\leavevmode\\footnotetext[5]{\sphinxAtStartFootnote%\n' - 'Footnote in term\n}\nDescription' in result) + assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] ' + '\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' + 'Footnote in term\n%\n\\end{footnotetext}\nDescription' in result) assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist} ' '(http://sphinx-doc.org/)}] \\leavevmode\nDescription' in result) assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result) @@ -496,40 +496,45 @@ def test_latex_show_urls_is_footnote(app, status, warning): print(result) print(status.getvalue()) print(warning.getvalue()) - assert ('Same footnote number \\footnote[1]{\sphinxAtStartFootnote%\n' - 'footnote in bar\n} in bar.rst' in result) - assert ('Auto footnote number \\footnote[2]{\sphinxAtStartFootnote%\n' - 'footnote in baz\n} in baz.rst' in result) - assert ('\\phantomsection\\label{index:id26}{\\hyperref[index:the\\string-section' + assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'footnote in bar\n%\n\\end{footnote} in bar.rst' in result) + assert ('Auto footnote number %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' + 'footnote in baz\n%\n\\end{footnote} in baz.rst' in result) + assert ('\\phantomsection\\label{index:id30}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to\\string-authoryear]' '{\\sphinxcrossref{The section with a reference ' 'to \\phantomsection\\label{index:id1}' '{\\hyperref[index:authoryear]{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}' in result) - assert ('\\phantomsection\\label{index:id27}{\\hyperref[index:the\\string-section' + assert ('\\phantomsection\\label{index:id31}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to]' '{\\sphinxcrossref{The section with a reference to }}}' in result) - assert 'First footnote: \\footnote[3]{\sphinxAtStartFootnote%\nFirst\n}' in result - assert 'Second footnote: \\footnote[1]{\sphinxAtStartFootnote%\nSecond\n}' in result + assert ('First footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' + 'First\n%\n\\end{footnote}') in result + assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'Second\n%\n\\end{footnote}') in result assert ('\\href{http://sphinx-doc.org/}{Sphinx}' - '\\footnote[4]{\sphinxAtStartFootnote%\n' - '\\nolinkurl{http://sphinx-doc.org/}\n}' in result) - assert 'Third footnote: \\footnote[6]{\sphinxAtStartFootnote%\nThird\n}' in result + '%\n\\begin{footnote}[4]\\sphinxAtStartFootnote\n' + '\\nolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result + assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n' + 'Third\n%\n\\end{footnote}') in result assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde}' - '\\footnote[5]{\sphinxAtStartFootnote%\n' - '\\nolinkurl{http://sphinx-doc.org/~test/}\n}' in result) - assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}\\protect\\footnotemark[8]}] ' - '\\leavevmode\\footnotetext[8]{\sphinxAtStartFootnote%\n' - '\\nolinkurl{http://sphinx-doc.org/}\n}\nDescription' in result) - assert ('\\item[{Footnote in term \\protect\\footnotemark[10]}] ' - '\\leavevmode\\footnotetext[10]{\sphinxAtStartFootnote%\n' - 'Footnote in term\n}\nDescription' in result) - assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}\\protect' - '\\footnotemark[9]}] ' - '\\leavevmode\\footnotetext[9]{\sphinxAtStartFootnote%\n' - '\\nolinkurl{http://sphinx-doc.org/}\n}\nDescription' in result) + '%\n\\begin{footnote}[5]\\sphinxAtStartFootnote\n' + '\\nolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result + assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}\\sphinxfootnotemark[8]}] ' + '\\leavevmode%\n\\begin{footnotetext}[8]\\sphinxAtStartFootnote\n' + '\\nolinkurl{http://sphinx-doc.org/}\n%\n' + '\\end{footnotetext}\nDescription') in result + assert ('\\item[{Footnote in term \\sphinxfootnotemark[10]}] ' + '\\leavevmode%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n' + 'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result + assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}' + '\\sphinxfootnotemark[9]}] ' + '\\leavevmode%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n' + '\\nolinkurl{http://sphinx-doc.org/}\n%\n' + '\\end{footnotetext}\nDescription') in result assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result) assert ('\\href{mailto:sphinx-dev@googlegroups.com}' - '{sphinx-dev@googlegroups.com}\n' in result) + '{sphinx-dev@googlegroups.com}\n') in result @with_app(buildername='latex', testroot='footnotes', @@ -540,33 +545,36 @@ def test_latex_show_urls_is_no(app, status, warning): print(result) print(status.getvalue()) print(warning.getvalue()) - assert ('Same footnote number \\footnote[1]{\sphinxAtStartFootnote%\n' - 'footnote in bar\n} in bar.rst' in result) - assert ('Auto footnote number \\footnote[1]{\sphinxAtStartFootnote%\n' - 'footnote in baz\n} in baz.rst' in result) - assert ('\\phantomsection\\label{index:id26}{\\hyperref[index:the\\string-section' + assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'footnote in bar\n%\n\\end{footnote} in bar.rst') in result + assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'footnote in baz\n%\n\\end{footnote} in baz.rst') in result + assert ('\\phantomsection\\label{index:id30}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to\\string-authoryear]' '{\\sphinxcrossref{The section with a reference ' 'to \\phantomsection\\label{index:id1}' - '{\\hyperref[index:authoryear]{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}' in result) - assert ('\\phantomsection\\label{index:id27}{\\hyperref[index:the\\string-section' + '{\\hyperref[index:authoryear]{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result + assert ('\\phantomsection\\label{index:id31}{\\hyperref[index:the\\string-section' '\\string-with\\string-a\\string-reference\\string-to]' '{\\sphinxcrossref{The section with a reference to }}}' in result) - assert 'First footnote: \\footnote[2]{\sphinxAtStartFootnote%\nFirst\n}' in result - assert 'Second footnote: \\footnote[1]{\sphinxAtStartFootnote%\nSecond\n}' in result + assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' + 'First\n%\n\\end{footnote}') in result + assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' + 'Second\n%\n\\end{footnote}') in result assert '\\href{http://sphinx-doc.org/}{Sphinx}' in result - assert 'Third footnote: \\footnote[3]{\sphinxAtStartFootnote%\nThird\n}' in result + assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' + 'Third\n%\n\\end{footnote}') in result assert '\\href{http://sphinx-doc.org/~test/}{URL including tilde}' in result assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}}] ' - '\\leavevmode\nDescription' in result) - assert ('\\item[{Footnote in term \\protect\\footnotemark[5]}] ' - '\\leavevmode\\footnotetext[5]{\sphinxAtStartFootnote%\n' - 'Footnote in term\n}\nDescription' in result) + '\\leavevmode\nDescription') in result + assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] ' + '\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' + 'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}}] ' - '\\leavevmode\nDescription' in result) + '\\leavevmode\nDescription') in result assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result) assert ('\\href{mailto:sphinx-dev@googlegroups.com}' - '{sphinx-dev@googlegroups.com}\n' in result) + '{sphinx-dev@googlegroups.com}\n') in result @with_app(buildername='latex', testroot='image-in-section') @@ -577,10 +585,10 @@ def test_image_in_section(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert ('\\chapter[Test section]{\\lowercase{\\sphinxincludegraphics' - '[width=15pt,height=15pt]}{{pic}.png} Test section}' + '[width=15bp,height=15bp]}{{pic}.png} Test section}' in result) assert ('\\chapter[Other {[}blah{]} section]{Other {[}blah{]} ' - '\\lowercase{\\sphinxincludegraphics[width=15pt,height=15pt]}' + '\\lowercase{\\sphinxincludegraphics[width=15bp,height=15bp]}' '{{pic}.png} section}' in result) assert ('\\chapter{Another section}' in result) @@ -606,6 +614,7 @@ def test_toctree_maxdepth_manual(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert '\\setcounter{tocdepth}{1}' in result + assert '\\setcounter{secnumdepth}' not in result @with_app(buildername='latex', testroot='toctree-maxdepth', @@ -620,6 +629,7 @@ def test_toctree_maxdepth_howto(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert '\\setcounter{tocdepth}{2}' in result + assert '\\setcounter{secnumdepth}' not in result @with_app(buildername='latex', testroot='toctree-maxdepth', @@ -631,6 +641,7 @@ def test_toctree_not_found(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert '\\setcounter{tocdepth}' not in result + assert '\\setcounter{secnumdepth}' not in result @with_app(buildername='latex', testroot='toctree-maxdepth', @@ -642,6 +653,19 @@ def test_toctree_without_maxdepth(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert '\\setcounter{tocdepth}' not in result + assert '\\setcounter{secnumdepth}' not in result + + +@with_app(buildername='latex', testroot='toctree-maxdepth', + confoverrides={'master_doc': 'qux'}) +def test_toctree_with_deeper_maxdepth(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'Python.tex').text(encoding='utf8') + print(result) + print(status.getvalue()) + print(warning.getvalue()) + assert '\\setcounter{tocdepth}{3}' in result + assert '\\setcounter{secnumdepth}{3}' in result @with_app(buildername='latex', testroot='toctree-maxdepth', @@ -686,3 +710,13 @@ def test_latex_toplevel_sectioning_is_section(app, status, warning): print(status.getvalue()) print(warning.getvalue()) assert '\\section{Foo}' in result + +@skip_if_stylefiles_notfound +@with_app(buildername='latex', testroot='maxlistdepth') +def test_maxlistdepth_at_ten(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8') + print(result) + print(status.getvalue()) + print(warning.getvalue()) + compile_latex_document(app) diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py new file mode 100644 index 000000000..1d75135af --- /dev/null +++ b/tests/test_build_linkcheck.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" + test_build_linkcheck + ~~~~~~~~~~~~~~~~~~~~ + + Test the build process with manpage builder with the test root. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import print_function + +from util import with_app + + +@with_app('linkcheck', testroot='linkcheck', freshenv=True) +def test_defaults(app, status, warning): + app.builder.build_all() + + assert (app.outdir / 'output.txt').exists() + content = (app.outdir / 'output.txt').text() + + print(content) + # looking for #top should fail + assert "Anchor 'top' not found" in content + assert len(content.splitlines()) == 1 + + +@with_app('linkcheck', testroot='linkcheck', freshenv=True, + confoverrides={'linkcheck_anchors_ignore': ["^!", "^top$"]}) +def test_anchors_ignored(app, status, warning): + app.builder.build_all() + + assert (app.outdir / 'output.txt').exists() + content = (app.outdir / 'output.txt').text() + + # expect all ok when excluding #top + assert not content diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 47bec2e23..06da311df 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -18,7 +18,7 @@ from six import PY3 from sphinx.writers.texinfo import TexinfoTranslator -from util import SkipTest, remove_unicode_literals, with_app +from util import SkipTest, remove_unicode_literals, with_app, strip_escseq from test_build_html import ENV_WARNINGS @@ -36,8 +36,7 @@ if PY3: @with_app(buildername='texinfo', testroot='warnings', freshenv=True) def test_texinfo_warnings(app, status, warning): app.builder.build_all() - - warnings = warning.getvalue().replace(os.sep, '/') + warnings = strip_escseq(warning.getvalue().replace(os.sep, '/')) warnings_exp = TEXINFO_WARNINGS % { 'root': re.escape(app.srcdir.replace(os.sep, '/'))} assert re.match(warnings_exp + '$', warnings), \ diff --git a/tests/test_build_text.py b/tests/test_build_text.py index 613d95d1f..f95e4d2ab 100644 --- a/tests/test_build_text.py +++ b/tests/test_build_text.py @@ -65,7 +65,7 @@ def test_nonascii_title_line(app, status, warning): app.builder.build_update() result = (app.outdir / 'nonascii_title.txt').text(encoding='utf-8') expect_underline = '******' - result_underline = result.splitlines()[2].strip() + result_underline = result.splitlines()[1].strip() assert expect_underline == result_underline diff --git a/tests/test_config.py b/tests/test_config.py index 5b18e3101..1b3c94957 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for details. """ from six import PY3, iteritems -from util import mock +import mock from util import TestApp, with_app, gen_with_app, with_tempdir, \ raises, raises_msg, assert_in, assert_not_in @@ -38,7 +38,7 @@ def test_core_config(app, status, warning): # simple default values assert 'locale_dirs' not in cfg.__dict__ - assert cfg.locale_dirs == [] + assert cfg.locale_dirs == ['locales'] assert cfg.trim_footnote_reference_space is False # complex default values @@ -206,3 +206,15 @@ def test_gen_check_types(app, status, warning): 'override on "%s" should%s raise a type warning' % (key, '' if should else ' NOT') ) + + +@with_app(testroot='config') +def test_check_enum(app, status, warning): + assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \ + not in warning.getvalue() + + +@with_app(testroot='config', confoverrides={'value17': 'invalid'}) +def test_check_enum_failed(app, status, warning): + assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \ + "but `invalid` is given." in warning.getvalue() diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py index 8209e8f61..a29db6b90 100644 --- a/tests/test_directive_code.py +++ b/tests/test_directive_code.py @@ -65,7 +65,7 @@ def test_code_block_caption_html(app, status, warning): def test_code_block_caption_latex(app, status, warning): app.builder.build_all() latex = (app.outdir / 'Python.tex').text(encoding='utf-8') - caption = '\\sphinxSetupCaptionForVerbatim{literal-block}{caption \\emph{test} rb}' + caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstyleemphasis{test} rb}' label = '\\def\\sphinxLiteralBlockLabel{\\label{caption:id1}}' link = '\hyperref[caption:name-test-rb]' \ '{Listing \\ref{caption:name-test-rb}}' @@ -222,17 +222,25 @@ def test_literal_include_lineno_match(app, status, warning): '14</pre></div></td>') assert start_after in html + start_at_end_at = ( + '<td class="linenos"><div class="linenodiv"><pre>' + ' 9\n' + '10\n' + '11</pre></div></td>') + assert start_at_end_at in html + @with_app('latex', testroot='directive-code') def test_literalinclude_file_whole_of_emptyline(app, status, warning): app.builder.build_all() latex = (app.outdir / 'Python.tex').text(encoding='utf-8').replace('\r\n', '\n') includes = ( - '\\begin{Verbatim}[commandchars=\\\\\\{\\},numbers=left,firstnumber=1,stepnumber=1]\n' + '\\begin{sphinxVerbatim}' + '[commandchars=\\\\\\{\\},numbers=left,firstnumber=1,stepnumber=1]\n' '\n' '\n' '\n' - '\\end{Verbatim}\n') + '\\end{sphinxVerbatim}\n') assert includes in latex @@ -252,7 +260,7 @@ def test_literalinclude_caption_html(app, status, warning): def test_literalinclude_caption_latex(app, status, warning): app.builder.build('index') latex = (app.outdir / 'Python.tex').text(encoding='utf-8') - caption = '\\sphinxSetupCaptionForVerbatim{literal-block}{caption \\textbf{test} py}' + caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstylestrong{test} py}' label = '\\def\\sphinxLiteralBlockLabel{\\label{caption:id2}}' link = '\hyperref[caption:name-test-py]' \ '{Listing \\ref{caption:name-test-py}}' diff --git a/tests/test_directive_only.py b/tests/test_directive_only.py index 7e499a3a1..def064c5a 100644 --- a/tests/test_directive_only.py +++ b/tests/test_directive_only.py @@ -12,6 +12,7 @@ import re from docutils import nodes +from sphinx.util.nodes import process_only_nodes from util import with_app @@ -46,7 +47,7 @@ def test_sectioning(app, status, warning): app.builder.build(['only']) doctree = app.env.get_doctree('only') - app.env.process_only_nodes(doctree, app.builder) + process_only_nodes(doctree, app.builder.tags) parts = [getsects(n) for n in [_n for _n in doctree.children if isinstance(_n, nodes.section)]] diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 778ac1c55..4a505119d 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -24,7 +24,10 @@ ids = [] def parse(name, string): - parser = DefinitionParser(string, None) + class Config(object): + cpp_id_attributes = ["id_attr"] + cpp_paren_attributes = ["paren_attr"] + parser = DefinitionParser(string, None, Config()) ast = parser.parse_declaration(name) if not parser.eof: print("Parsing stopped at", parser.pos) @@ -50,7 +53,7 @@ def check(name, input, idv1output=None, idv2output=None, output=None): print("Expected: ", output) raise DefinitionError("") rootSymbol = Symbol(None, None, None, None, None, None) - symbol = rootSymbol.add_declaration(ast, docname="Test") + symbol = rootSymbol.add_declaration(ast, docname="TestDoc") parentNode = addnodes.desc() signode = addnodes.desc_signature(input, '') parentNode += signode @@ -138,6 +141,20 @@ def test_type_definitions(): check('type', 'void (*f)(std::function<void(int i)> g)', 'f', '1f') +def test_concept_definitions(): + check('concept', 'template<typename Param> A::B::Concept', + None, 'I0EN1A1B7ConceptE') + check('concept', 'template<typename A, typename B, typename ...C> Foo', + None, 'I00DpE3Foo') + check('concept', 'template<typename Param> A::B::Concept()', + None, 'I0EN1A1B7ConceptE') + check('concept', 'template<typename A, typename B, typename ...C> Foo()', + None, 'I00DpE3Foo') + raises(DefinitionError, parse, 'concept', 'Foo') + raises(DefinitionError, parse, 'concept', + 'template<typename T> template<typename U> Foo') + + def test_member_definitions(): check('member', ' const std::string & name = 42', "name__ssCR", "4name", output='const std::string &name = 42') @@ -401,6 +418,70 @@ def test_templates(): None, "I00ElsRNSt13basic_ostreamI4Char6TraitsEE" "RK18c_string_view_baseIK4Char6TraitsE") + # template introductions + raises(DefinitionError, parse, 'enum', 'abc::ns::foo{id_0, id_1, id_2} A') + raises(DefinitionError, parse, 'enumerator', 'abc::ns::foo{id_0, id_1, id_2} A') + check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar', + None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar', + None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar<id_0, id_1, id_2>', + None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE') + check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar<id_0, id_1, id_2...>', + None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE') + + check('class', 'template<> Concept{U} A<int>::B', + None, 'IEI0EX7ConceptI1UEEN1AIiE1BE') + + check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux', + None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux', + None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()', + None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv') + check('function', 'abc::ns::foo{id_0, id_1, ...id_2} void xyz::bar()', + None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv') + check('member', 'abc::ns::foo{id_0, id_1, id_2} ghi::qux xyz::bar', + None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar', + None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + check('concept', 'Iterator{T, U} Another', + None, 'I00EX8IteratorI1T1UEE7Another') + check('concept', 'template<typename ...Pack> Numerics = (... && Numeric<Pack>)', + None, 'IDpE8Numerics') + + +def test_attributes(): + # style: C++ + check('member', '[[]] int f', 'f__i', '1f') + check('member', '[ [ ] ] int f', 'f__i', '1f', + # this will fail when the proper grammar is implemented + output='[[ ]] int f') + check('member', '[[a]] int f', 'f__i', '1f') + # style: GNU + check('member', '__attribute__(()) int f', 'f__i', '1f') + check('member', '__attribute__((a)) int f', 'f__i', '1f') + check('member', '__attribute__((a, b)) int f', 'f__i', '1f') + # style: user-defined id + check('member', 'id_attr int f', 'f__i', '1f') + # style: user-defined paren + check('member', 'paren_attr() int f', 'f__i', '1f') + check('member', 'paren_attr(a) int f', 'f__i', '1f') + check('member', 'paren_attr("") int f', 'f__i', '1f') + check('member', 'paren_attr(()[{}][]{}) int f', 'f__i', '1f') + raises(DefinitionError, parse, 'member', 'paren_attr(() int f') + raises(DefinitionError, parse, 'member', 'paren_attr([) int f') + raises(DefinitionError, parse, 'member', 'paren_attr({) int f') + raises(DefinitionError, parse, 'member', 'paren_attr([)]) int f') + raises(DefinitionError, parse, 'member', 'paren_attr((])) int f') + raises(DefinitionError, parse, 'member', 'paren_attr({]}) int f') + + # position: decl specs + check('function', 'static inline __attribute__(()) void f()', + 'f', '1fv', + output='__attribute__(()) static inline void f()') + + # def test_print(): # # used for getting all the ids out for checking diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py index 2d31ada2e..4f892cb01 100644 --- a/tests/test_domain_std.py +++ b/tests/test_domain_std.py @@ -10,9 +10,9 @@ """ from docutils import nodes +import mock from sphinx.domains.std import StandardDomain -from util import mock def test_process_doc_handle_figure_caption(): @@ -26,6 +26,7 @@ def test_process_doc_handle_figure_caption(): nameids={'testname': 'testid'}, ids={'testid': figure_node}, ) + document.traverse.return_value = [] domain = StandardDomain(env) if 'testname' in domain.data['labels']: @@ -47,6 +48,7 @@ def test_process_doc_handle_table_title(): nameids={'testname': 'testid'}, ids={'testid': table_node}, ) + document.traverse.return_value = [] domain = StandardDomain(env) if 'testname' in domain.data['labels']: diff --git a/tests/test_environment_indexentries.py b/tests/test_environment_indexentries.py new file mode 100644 index 000000000..57a3cf52f --- /dev/null +++ b/tests/test_environment_indexentries.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +""" + test_environment_indexentries + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Test the sphinx.environment.managers.indexentries. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from collections import namedtuple +from sphinx import locale +from sphinx.environment.managers.indexentries import IndexEntries + +import mock + +Environment = namedtuple('Environment', 'indexentries') + +dummy_builder = mock.Mock() +dummy_builder.get_relative_uri.return_value = '' + + +def test_create_single_index(): + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('single', 'docutils', 'id1', '', None), + ('single', 'Python', 'id2', '', None), + ('single', 'pip; install', 'id3', '', None), + ('single', 'pip; upgrade', 'id4', '', None), + ('single', 'Sphinx', 'id5', '', None), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 3 + assert index[0] == (u'D', [(u'docutils', [[('', '#id1')], [], None])]) + assert index[1] == (u'P', [(u'pip', [[], [(u'install', [('', '#id3')]), + (u'upgrade', [('', '#id4')])], None]), + (u'Python', [[('', '#id2')], [], None])]) + assert index[2] == (u'S', [(u'Sphinx', [[('', '#id5')], [], None])]) + + +def test_create_pair_index(): + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('pair', 'docutils; reStructuredText', 'id1', '', None), + ('pair', 'Python; interpreter', 'id2', '', None), + ('pair', 'Sphinx; documentation tool', 'id3', '', None), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 5 + assert index[0] == (u'D', + [(u'documentation tool', [[], [(u'Sphinx', [('', '#id3')])], None]), + (u'docutils', [[], [(u'reStructuredText', [('', '#id1')])], None])]) + assert index[1] == (u'I', [(u'interpreter', [[], [(u'Python', [('', '#id2')])], None])]) + assert index[2] == (u'P', [(u'Python', [[], [(u'interpreter', [('', '#id2')])], None])]) + assert index[3] == (u'R', + [(u'reStructuredText', [[], [(u'docutils', [('', '#id1')])], None])]) + assert index[4] == (u'S', + [(u'Sphinx', [[], [(u'documentation tool', [('', '#id3')])], None])]) + + +def test_create_triple_index(): + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('triple', 'foo; bar; baz', 'id1', '', None), + ('triple', 'Python; Sphinx; reST', 'id2', '', None), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 5 + assert index[0] == (u'B', [(u'bar', [[], [(u'baz, foo', [('', '#id1')])], None]), + (u'baz', [[], [(u'foo bar', [('', '#id1')])], None])]) + assert index[1] == (u'F', [(u'foo', [[], [(u'bar baz', [('', '#id1')])], None])]) + assert index[2] == (u'P', [(u'Python', [[], [(u'Sphinx reST', [('', '#id2')])], None])]) + assert index[3] == (u'R', [(u'reST', [[], [(u'Python Sphinx', [('', '#id2')])], None])]) + assert index[4] == (u'S', [(u'Sphinx', [[], [(u'reST, Python', [('', '#id2')])], None])]) + + +def test_create_see_index(): + locale.init([], None) + + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('see', 'docutils; reStructuredText', 'id1', '', None), + ('see', 'Python; interpreter', 'id2', '', None), + ('see', 'Sphinx; documentation tool', 'id3', '', None), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 3 + assert index[0] == (u'D', [(u'docutils', [[], [(u'see reStructuredText', [])], None])]) + assert index[1] == (u'P', [(u'Python', [[], [(u'see interpreter', [])], None])]) + assert index[2] == (u'S', [(u'Sphinx', [[], [(u'see documentation tool', [])], None])]) + + +def test_create_seealso_index(): + locale.init([], None) + + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('seealso', 'docutils; reStructuredText', 'id1', '', None), + ('seealso', 'Python; interpreter', 'id2', '', None), + ('seealso', 'Sphinx; documentation tool', 'id3', '', None), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 3 + assert index[0] == (u'D', + [(u'docutils', [[], [(u'see also reStructuredText', [])], None])]) + assert index[1] == (u'P', + [(u'Python', [[], [(u'see also interpreter', [])], None])]) + assert index[2] == (u'S', + [(u'Sphinx', [[], [(u'see also documentation tool', [])], None])]) + + +def test_create_index_by_key(): + # type, value, tid, main, index_key + env = Environment({ + 'index': [ + ('single', 'docutils', 'id1', '', None), + ('single', 'Python', 'id2', '', None), + ('single', u'スフィンクス', 'id3', '', u'ス'), + ], + }) + index = IndexEntries(env).create_index(dummy_builder) + assert len(index) == 3 + assert index[0] == (u'D', [(u'docutils', [[('', '#id1')], [], None])]) + assert index[1] == (u'P', [(u'Python', [[('', '#id2')], [], None])]) + assert index[2] == (u'ス', [(u'スフィンクス', [[('', '#id3')], [], u'ス'])]) diff --git a/tests/test_environment_toctree.py b/tests/test_environment_toctree.py new file mode 100644 index 000000000..20188c16a --- /dev/null +++ b/tests/test_environment_toctree.py @@ -0,0 +1,332 @@ +# -*- coding: utf-8 -*- +""" + test_environment_toctree + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Test the sphinx.environment.managers.toctree. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from docutils import nodes +from docutils.nodes import bullet_list, list_item, caption, comment, reference +from sphinx import addnodes +from sphinx.addnodes import compact_paragraph, only +from sphinx.builders.html import StandaloneHTMLBuilder + +from util import with_app, gen_with_app, assert_node + + +@gen_with_app('xml', testroot='toctree') +def test_basic(app, status, warning): + app.build() + yield _test_process_doc, app + yield _test_get_toc_for, app + yield _test_get_toc_for_only, app + yield _test_get_toc_for_tocdepth, app + yield _test_get_toctree_for, app + yield _test_get_toctree_for_collapse, app + yield _test_get_toctree_for_maxdepth, app + yield _test_get_toctree_for_includehidden, app + + +def _test_process_doc(app): + # tocs + toctree = app.env.tocs['index'] + assert_node(toctree, + [bullet_list, ([list_item, (compact_paragraph, # [0][0] + [bullet_list, (addnodes.toctree, # [0][1][0] + only, # [0][1][1] + list_item)])], # [0][1][2] + [list_item, (compact_paragraph, # [1][0] + [bullet_list, (addnodes.toctree, # [1][1][0] + addnodes.toctree)])], # [1][1][1] + list_item)]) + + assert_node(toctree[0][0], + [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + assert_node(toctree[0][0][0], reference, anchorname='') + assert_node(toctree[0][1][0], addnodes.toctree, + caption="Table of Contents", glob=False, hidden=False, + titlesonly=False, maxdepth=2, numbered=999, + entries=[(None, 'foo'), (None, 'bar'), (None, 'http://sphinx-doc.org/')], + includefiles=['foo', 'bar']) + + # only branch + assert_node(toctree[0][1][1], addnodes.only, expr="html") + assert_node(toctree[0][1][1], + [only, list_item, ([compact_paragraph, reference, "Section for HTML"], + [bullet_list, addnodes.toctree])]) + assert_node(toctree[0][1][1][0][0][0], reference, anchorname='#section-for-html') + assert_node(toctree[0][1][1][0][1][0], addnodes.toctree, + caption=None, glob=False, hidden=False, entries=[(None, 'baz')], + includefiles=['baz'], titlesonly=False, maxdepth=-1, numbered=0) + assert_node(toctree[0][1][2], + ([compact_paragraph, reference, "subsection"], + [bullet_list, list_item, compact_paragraph, reference, "subsubsection"])) + + assert_node(toctree[1][0], + [compact_paragraph, reference, "Test for issue #1157"]) + assert_node(toctree[1][0][0], reference, anchorname='#test-for-issue-1157') + assert_node(toctree[1][1][0], addnodes.toctree, + caption=None, entries=[], glob=False, hidden=False, + titlesonly=False, maxdepth=-1, numbered=0) + assert_node(toctree[1][1][1], addnodes.toctree, + caption=None, glob=False, hidden=True, + titlesonly=False, maxdepth=-1, numbered=0, + entries=[('Latest reference', 'http://sphinx-doc.org/latest/'), + ('Python', 'http://python.org/')]) + + assert_node(toctree[2][0], + [compact_paragraph, reference, "Indices and tables"]) + + # other collections + assert app.env.toc_num_entries['index'] == 6 + assert app.env.toctree_includes['index'] == ['foo', 'bar', 'baz'] + assert app.env.files_to_rebuild['foo'] == set(['index']) + assert app.env.files_to_rebuild['bar'] == set(['index']) + assert app.env.files_to_rebuild['baz'] == set(['index']) + assert app.env.glob_toctrees == set() + assert app.env.numbered_toctrees == set(['index']) + + # qux has no section title + assert len(app.env.tocs['qux']) == 0 + assert_node(app.env.tocs['qux'], nodes.bullet_list) + assert app.env.toc_num_entries['qux'] == 0 + assert 'qux' not in app.env.toctree_includes + + +@with_app('dummy', testroot='toctree-glob') +def test_glob(app, status, warning): + includefiles = ['foo', 'bar/index', 'bar/bar_1', 'bar/bar_2', + 'bar/bar_3', 'baz', 'qux/index'] + + app.build() + + # tocs + toctree = app.env.tocs['index'] + assert_node(toctree, + [bullet_list, list_item, (compact_paragraph, # [0][0] + [bullet_list, (list_item, # [0][1][0] + list_item)])]) # [0][1][1] + + assert_node(toctree[0][0], + [compact_paragraph, reference, "test-toctree-glob"]) + assert_node(toctree[0][1][0], + [list_item, ([compact_paragraph, reference, "normal order"], + [bullet_list, addnodes.toctree])]) # [0][1][0][1][0] + assert_node(toctree[0][1][0][1][0], addnodes.toctree, caption=None, + glob=True, hidden=False, titlesonly=False, + maxdepth=-1, numbered=0, includefiles=includefiles, + entries=[(None, 'foo'), (None, 'bar/index'), (None, 'bar/bar_1'), + (None, 'bar/bar_2'), (None, 'bar/bar_3'), (None, 'baz'), + (None, 'qux/index')]) + assert_node(toctree[0][1][1], + [list_item, ([compact_paragraph, reference, "reversed order"], + [bullet_list, addnodes.toctree])]) # [0][1][1][1][0] + assert_node(toctree[0][1][1][1][0], addnodes.toctree, caption=None, + glob=True, hidden=False, titlesonly=False, + maxdepth=-1, numbered=0, includefiles=includefiles, + entries=[(None, 'qux/index'), (None, 'baz'), (None, 'bar/bar_3'), + (None, 'bar/bar_2'), (None, 'bar/bar_1'), (None, 'bar/index'), + (None, 'foo')]) + includefiles = ['foo', 'bar/index', 'bar/bar_1', 'bar/bar_2', + 'bar/bar_3', 'baz', 'qux/index'] + + # other collections + assert app.env.toc_num_entries['index'] == 3 + assert app.env.toctree_includes['index'] == includefiles + includefiles + for file in includefiles: + assert 'index' in app.env.files_to_rebuild[file] + assert 'index' in app.env.glob_toctrees + assert app.env.numbered_toctrees == set() + + +def _test_get_toc_for(app): + toctree = app.env.get_toc_for('index', app.builder) + + assert_node(toctree, + [bullet_list, ([list_item, (compact_paragraph, # [0][0] + [bullet_list, (addnodes.toctree, # [0][1][0] + comment, # [0][1][1] + list_item)])], # [0][1][2] + [list_item, (compact_paragraph, # [1][0] + [bullet_list, (addnodes.toctree, + addnodes.toctree)])], + [list_item, compact_paragraph])]) # [2][0] + assert_node(toctree[0][0], + [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + assert_node(toctree[0][1][2], + ([compact_paragraph, reference, "subsection"], + [bullet_list, list_item, compact_paragraph, reference, "subsubsection"])) + assert_node(toctree[1][0], + [compact_paragraph, reference, "Test for issue #1157"]) + assert_node(toctree[2][0], + [compact_paragraph, reference, "Indices and tables"]) + + +def _test_get_toc_for_only(app): + builder = StandaloneHTMLBuilder(app) + toctree = app.env.get_toc_for('index', builder) + + assert_node(toctree, + [bullet_list, ([list_item, (compact_paragraph, # [0][0] + [bullet_list, (addnodes.toctree, # [0][1][0] + list_item, # [0][1][1] + list_item)])], # [0][1][2] + [list_item, (compact_paragraph, # [1][0] + [bullet_list, (addnodes.toctree, + addnodes.toctree)])], + [list_item, compact_paragraph])]) # [2][0] + assert_node(toctree[0][0], + [compact_paragraph, reference, "Welcome to Sphinx Tests's documentation!"]) + assert_node(toctree[0][1][1], + ([compact_paragraph, reference, "Section for HTML"], + [bullet_list, addnodes.toctree])) + assert_node(toctree[0][1][2], + ([compact_paragraph, reference, "subsection"], + [bullet_list, list_item, compact_paragraph, reference, "subsubsection"])) + assert_node(toctree[1][0], + [compact_paragraph, reference, "Test for issue #1157"]) + assert_node(toctree[2][0], + [compact_paragraph, reference, "Indices and tables"]) + + +def _test_get_toc_for_tocdepth(app): + toctree = app.env.get_toc_for('tocdepth', app.builder) + + assert_node(toctree, + [bullet_list, list_item, (compact_paragraph, # [0][0] + bullet_list)]) # [0][1] + assert_node(toctree[0][0], + [compact_paragraph, reference, "level 1"]) + assert_node(toctree[0][1], + [bullet_list, list_item, compact_paragraph, reference, "level 2"]) + + +def _test_get_toctree_for(app): + toctree = app.env.get_toctree_for('index', app.builder, collapse=False) + assert_node(toctree, + [compact_paragraph, ([caption, "Table of Contents"], + bullet_list, + bullet_list, + bullet_list)]) + + assert_node(toctree[1], + ([list_item, ([compact_paragraph, reference, "foo"], + bullet_list)], + [list_item, compact_paragraph, reference, "bar"], + [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) + assert_node(toctree[1][0][1], + ([list_item, compact_paragraph, reference, "quux"], + [list_item, compact_paragraph, reference, "foo.1"], + [list_item, compact_paragraph, reference, "foo.2"])) + + assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) + assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) + assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) + assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) + assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) + assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") + + assert_node(toctree[2], + [bullet_list, list_item, compact_paragraph, reference, "baz"]) + assert_node(toctree[3], + ([list_item, compact_paragraph, reference, "Latest reference"], + [list_item, compact_paragraph, reference, "Python"])) + assert_node(toctree[3][0][0][0], reference, refuri="http://sphinx-doc.org/latest/") + assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") + + +def _test_get_toctree_for_collapse(app): + toctree = app.env.get_toctree_for('index', app.builder, collapse=True) + assert_node(toctree, + [compact_paragraph, ([caption, "Table of Contents"], + bullet_list, + bullet_list, + bullet_list)]) + + assert_node(toctree[1], + ([list_item, compact_paragraph, reference, "foo"], + [list_item, compact_paragraph, reference, "bar"], + [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) + assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) + assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) + assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") + + assert_node(toctree[2], + [bullet_list, list_item, compact_paragraph, reference, "baz"]) + assert_node(toctree[3], + ([list_item, compact_paragraph, reference, "Latest reference"], + [list_item, compact_paragraph, reference, "Python"])) + assert_node(toctree[3][0][0][0], reference, refuri="http://sphinx-doc.org/latest/") + assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") + + +def _test_get_toctree_for_maxdepth(app): + toctree = app.env.get_toctree_for('index', app.builder, collapse=False, maxdepth=3) + assert_node(toctree, + [compact_paragraph, ([caption, "Table of Contents"], + bullet_list, + bullet_list, + bullet_list)]) + + assert_node(toctree[1], + ([list_item, ([compact_paragraph, reference, "foo"], + bullet_list)], + [list_item, compact_paragraph, reference, "bar"], + [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) + assert_node(toctree[1][0][1], + ([list_item, compact_paragraph, reference, "quux"], + [list_item, ([compact_paragraph, reference, "foo.1"], + bullet_list)], + [list_item, compact_paragraph, reference, "foo.2"])) + assert_node(toctree[1][0][1][1][1], + [bullet_list, list_item, compact_paragraph, reference, "foo.1-1"]) + + assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) + assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) + assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) + assert_node(toctree[1][0][1][1][1][0][0][0], + reference, refuri="foo#foo-1-1", secnumber=(1, 2, 1)) + assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) + assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) + assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") + + assert_node(toctree[2], + [bullet_list, list_item, compact_paragraph, reference, "baz"]) + assert_node(toctree[3], + ([list_item, compact_paragraph, reference, "Latest reference"], + [list_item, compact_paragraph, reference, "Python"])) + assert_node(toctree[3][0][0][0], reference, refuri="http://sphinx-doc.org/latest/") + assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/") + + +def _test_get_toctree_for_includehidden(app): + toctree = app.env.get_toctree_for('index', app.builder, collapse=False, + includehidden=False) + assert_node(toctree, + [compact_paragraph, ([caption, "Table of Contents"], + bullet_list, + bullet_list)]) + + assert_node(toctree[1], + ([list_item, ([compact_paragraph, reference, "foo"], + bullet_list)], + [list_item, compact_paragraph, reference, "bar"], + [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) + assert_node(toctree[1][0][1], + ([list_item, compact_paragraph, reference, "quux"], + [list_item, compact_paragraph, reference, "foo.1"], + [list_item, compact_paragraph, reference, "foo.2"])) + + assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) + assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) + assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) + assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) + assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) + assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") + + assert_node(toctree[2], + [bullet_list, list_item, compact_paragraph, reference, "baz"]) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py new file mode 100644 index 000000000..dad7521af --- /dev/null +++ b/tests/test_ext_autodoc.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" + test_autodoc + ~~~~~~~~~~~~ + + Test the autodoc extension. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import pickle +from docutils import nodes +from sphinx import addnodes +from util import with_app + + +@with_app(buildername='dummy', testroot='ext-autodoc') +def test_autodoc(app, status, warning): + app.builder.build_all() + + content = pickle.loads((app.doctreedir / 'contents.doctree').bytes()) + assert isinstance(content[3], addnodes.desc) + assert content[3][0].astext() == 'autodoc_dummy_module.test' + assert content[3][1].astext() == 'Dummy function using dummy.*' diff --git a/tests/test_ext_graphviz.py b/tests/test_ext_graphviz.py index dbab33761..aa97f4135 100644 --- a/tests/test_ext_graphviz.py +++ b/tests/test_ext_graphviz.py @@ -55,6 +55,10 @@ def test_graphviz_html(app, status, warning): html = '<img src=".*?" alt="digraph {\n bar -> baz\n}" />' assert re.search(html, content, re.M) + html = ('<div class="figure align-right" .*?>\s*<img .*?/>\s*<p class="caption">' + '<span class="caption-text">on right</span>.*</p>\s*</div>') + assert re.search(html, content, re.S) + @with_app('latex', testroot='ext-graphviz') @skip_if_graphviz_not_found @@ -70,6 +74,11 @@ def test_graphviz_latex(app, status, warning): macro = 'Hello \\\\includegraphics{graphviz-\w+.pdf} graphviz world' assert re.search(macro, content, re.S) + macro = ('\\\\begin{wrapfigure}{r}{0pt}\n\\\\centering\n' + '\\\\includegraphics{graphviz-\w+.pdf}\n' + '\\\\caption{on right}\\\\label{.*}\\\\end{wrapfigure}') + assert re.search(macro, content, re.S) + @with_app('html', testroot='ext-graphviz', confoverrides={'language': 'xx'}) @skip_if_graphviz_not_found diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_ext_inheritance_diagram.py index 64446eed8..bf1bbbac0 100644 --- a/tests/test_ext_inheritance_diagram.py +++ b/tests/test_ext_inheritance_diagram.py @@ -9,9 +9,34 @@ :license: BSD, see LICENSE for details. """ +import re from util import with_app +from test_ext_graphviz import skip_if_graphviz_not_found @with_app('html', testroot='ext-inheritance_diagram') +@skip_if_graphviz_not_found def test_inheritance_diagram_html(app, status, warning): app.builder.build_all() + + content = (app.outdir / 'index.html').text() + + pattern = ('<div class="figure" id="id1">\n' + '<img src="_images/inheritance-\w+.png" alt="Inheritance diagram of test.Foo" ' + 'class="inheritance"/>\n<p class="caption"><span class="caption-text">' + 'Test Foo!</span><a class="headerlink" href="#id1" ' + 'title="Permalink to this image">\xb6</a></p>') + assert re.search(pattern, content, re.M) + + +@with_app('latex', testroot='ext-inheritance_diagram') +@skip_if_graphviz_not_found +def test_inheritance_diagram_latex(app, status, warning): + app.builder.build_all() + + content = (app.outdir / 'Python.tex').text() + + pattern = ('\\\\begin{figure}\\[htbp]\n\\\\centering\n\\\\capstart\n\n' + '\\\\includegraphics{inheritance-\\w+.pdf}\n' + '\\\\caption{Test Foo!}\\\\label{index:id1}\\\\end{figure}') + assert re.search(pattern, content, re.M) diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index deec5a379..e1995e3d3 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -15,13 +15,15 @@ import zlib from six import BytesIO from docutils import nodes +import mock from sphinx import addnodes -from sphinx.ext.intersphinx import read_inventory_v1, read_inventory_v2, \ +from sphinx.ext.intersphinx import setup as intersphinx_setup +from sphinx.ext.intersphinx import read_inventory, \ load_mappings, missing_reference, _strip_basic_auth, _read_from_url, \ _get_safe_url, fetch_inventory, INVENTORY_FILENAME -from util import with_app, with_tempdir, mock +from util import with_app, with_tempdir inventory_v1 = '''\ @@ -49,8 +51,7 @@ a term including:colon std:term -1 glossary.html#term-a-term-including-colon - def test_read_inventory_v1(): f = BytesIO(inventory_v1) - f.readline() - invdata = read_inventory_v1(f, '/util', posixpath.join) + invdata = read_inventory(f, '/util', posixpath.join) assert invdata['py:module']['module'] == \ ('foo', '1.0', '/util/foo.html#module-module', '-') assert invdata['py:class']['module.cls'] == \ @@ -59,13 +60,11 @@ def test_read_inventory_v1(): def test_read_inventory_v2(): f = BytesIO(inventory_v2) - f.readline() - invdata1 = read_inventory_v2(f, '/util', posixpath.join) + invdata1 = read_inventory(f, '/util', posixpath.join) # try again with a small buffer size to test the chunking algorithm f = BytesIO(inventory_v2) - f.readline() - invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5) + invdata2 = read_inventory(f, '/util', posixpath.join, bufsize=5) assert invdata1 == invdata2 @@ -84,47 +83,48 @@ def test_read_inventory_v2(): @with_app() -@mock.patch('sphinx.ext.intersphinx.read_inventory_v2') +@mock.patch('sphinx.ext.intersphinx.read_inventory') @mock.patch('sphinx.ext.intersphinx._read_from_url') -def test_fetch_inventory_redirection(app, status, warning, _read_from_url, read_inventory_v2): +def test_fetch_inventory_redirection(app, status, warning, _read_from_url, read_inventory): + intersphinx_setup(app) _read_from_url().readline.return_value = '# Sphinx inventory version 2'.encode('utf-8') # same uri and inv, not redirected - _read_from_url().geturl.return_value = 'http://hostname/' + INVENTORY_FILENAME + _read_from_url().url = 'http://hostname/' + INVENTORY_FILENAME fetch_inventory(app, 'http://hostname/', 'http://hostname/' + INVENTORY_FILENAME) assert 'intersphinx inventory has moved' not in status.getvalue() - assert read_inventory_v2.call_args[0][1] == 'http://hostname/' + assert read_inventory.call_args[0][1] == 'http://hostname/' # same uri and inv, redirected status.seek(0) status.truncate(0) - _read_from_url().geturl.return_value = 'http://hostname/new/' + INVENTORY_FILENAME + _read_from_url().url = 'http://hostname/new/' + INVENTORY_FILENAME fetch_inventory(app, 'http://hostname/', 'http://hostname/' + INVENTORY_FILENAME) assert status.getvalue() == ('intersphinx inventory has moved: ' 'http://hostname/%s -> http://hostname/new/%s\n' % (INVENTORY_FILENAME, INVENTORY_FILENAME)) - assert read_inventory_v2.call_args[0][1] == 'http://hostname/new' + assert read_inventory.call_args[0][1] == 'http://hostname/new' # different uri and inv, not redirected status.seek(0) status.truncate(0) - _read_from_url().geturl.return_value = 'http://hostname/new/' + INVENTORY_FILENAME + _read_from_url().url = 'http://hostname/new/' + INVENTORY_FILENAME fetch_inventory(app, 'http://hostname/', 'http://hostname/new/' + INVENTORY_FILENAME) assert 'intersphinx inventory has moved' not in status.getvalue() - assert read_inventory_v2.call_args[0][1] == 'http://hostname/' + assert read_inventory.call_args[0][1] == 'http://hostname/' # different uri and inv, redirected status.seek(0) status.truncate(0) - _read_from_url().geturl.return_value = 'http://hostname/other/' + INVENTORY_FILENAME + _read_from_url().url = 'http://hostname/other/' + INVENTORY_FILENAME fetch_inventory(app, 'http://hostname/', 'http://hostname/new/' + INVENTORY_FILENAME) assert status.getvalue() == ('intersphinx inventory has moved: ' 'http://hostname/new/%s -> http://hostname/other/%s\n' % (INVENTORY_FILENAME, INVENTORY_FILENAME)) - assert read_inventory_v2.call_args[0][1] == 'http://hostname/' + assert read_inventory.call_args[0][1] == 'http://hostname/' @with_app() @@ -248,64 +248,23 @@ class TestStripBasicAuth(unittest.TestCase): """basic auth creds stripped from URL containing creds""" url = 'https://user:12345@domain.com/project/objects.inv' expected = 'https://domain.com/project/objects.inv' - actual_url, actual_username, actual_password = _strip_basic_auth(url) - self.assertEqual(expected, actual_url) - self.assertEqual('user', actual_username) - self.assertEqual('12345', actual_password) + actual = _strip_basic_auth(url) + self.assertEqual(expected, actual) def test_no_auth(self): """url unchanged if param doesn't contain basic auth creds""" url = 'https://domain.com/project/objects.inv' expected = 'https://domain.com/project/objects.inv' - actual_url, actual_username, actual_password = _strip_basic_auth(url) - self.assertEqual(expected, actual_url) - self.assertEqual(None, actual_username) - self.assertEqual(None, actual_password) + actual = _strip_basic_auth(url) + self.assertEqual(expected, actual) def test_having_port(self): """basic auth creds correctly stripped from URL containing creds even if URL contains port""" url = 'https://user:12345@domain.com:8080/project/objects.inv' expected = 'https://domain.com:8080/project/objects.inv' - actual_url, actual_username, actual_password = _strip_basic_auth(url) - self.assertEqual(expected, actual_url) - self.assertEqual('user', actual_username) - self.assertEqual('12345', actual_password) - - -@mock.patch('six.moves.urllib.request.HTTPBasicAuthHandler') -@mock.patch('six.moves.urllib.request.HTTPPasswordMgrWithDefaultRealm') -@mock.patch('six.moves.urllib.request.build_opener') -def test_readfromurl_authed(m_build_opener, m_HTTPPasswordMgrWithDefaultRealm, - m_HTTPBasicAuthHandler): - # read from URL containing basic auth creds - password_mgr = mock.Mock() - m_HTTPPasswordMgrWithDefaultRealm.return_value = password_mgr - - url = 'https://user:12345@domain.com/project/objects.inv' - _read_from_url(url) - - m_HTTPPasswordMgrWithDefaultRealm.assert_called_once_with() - password_mgr.add_password.assert_called_with( - None, 'https://domain.com/project/objects.inv', 'user', '12345') - - -@mock.patch('six.moves.urllib.request.HTTPBasicAuthHandler') -@mock.patch('six.moves.urllib.request.HTTPPasswordMgrWithDefaultRealm') -@mock.patch('sphinx.ext.intersphinx.default_opener') -def test_readfromurl_unauthed(m_default_opener, m_HTTPPasswordMgrWithDefaultRealm, - m_HTTPBasicAuthHandler): - # read from URL without auth creds - password_mgr = mock.Mock() - m_HTTPPasswordMgrWithDefaultRealm.return_value = password_mgr - - url = 'https://domain.com/project/objects.inv' - _read_from_url(url) - - # assert password manager not created - assert m_HTTPPasswordMgrWithDefaultRealm.call_args is None - # assert no password added to the password manager - assert password_mgr.add_password.call_args is None + actual = _strip_basic_auth(url) + self.assertEqual(expected, actual) def test_getsafeurl_authed(): @@ -316,6 +275,14 @@ def test_getsafeurl_authed(): assert expected == actual +def test_getsafeurl_authed_having_port(): + """_get_safe_url() with a url with basic auth having port""" + url = 'https://user:12345@domain.com:8080/project/objects.inv' + expected = 'https://user@domain.com:8080/project/objects.inv' + actual = _get_safe_url(url) + assert expected == actual + + def test_getsafeurl_unauthed(): """_get_safe_url() with a url without basic auth""" url = 'https://domain.com/project/objects.inv' diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index c858afcd2..0c7d44e8e 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -22,10 +22,13 @@ def test_jsmath(app, status, warning): assert '<div class="math">\na^2 + b^2 = c^2</div>' in content assert '<div class="math">\n\\begin{split}a + 1 < b\\end{split}</div>' in content - assert ('<span class="eqno">(1)</span><div class="math" id="equation-foo">\n' - 'e^{i\\pi} = 1</div>' in content) - assert ('<span class="eqno">(2)</span><div class="math">\n' - 'e^{ix} = \\cos x + i\\sin x</div>' in content) + assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" ' + u'title="Permalink to this equation">\xb6</a></span>' + u'<div class="math" id="equation-foo">\ne^{i\\pi} = 1</div>' in content) + assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math:0" ' + u'title="Permalink to this equation">\xb6</a></span>' + u'<div class="math" id="equation-math:0">\n' + u'e^{ix} = \\cos x + i\\sin x</div>' in content) assert '<div class="math">\nn \\in \\mathbb N</div>' in content assert '<div class="math">\na + 1 < b</div>' in content @@ -80,8 +83,8 @@ def test_math_number_all_mathjax(app, status, warning): app.builder.build_all() content = (app.outdir / 'index.html').text() - html = (r'<div class="math">\s*' - r'<span class="eqno">\(1\)</span>\\\[a\^2\+b\^2=c\^2\\\]</div>') + html = (r'<div class="math" id="equation-index:0">\s*' + r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>') assert re.search(html, content, re.S) @@ -109,3 +112,6 @@ def test_math_number_all_latex(app, status, warning): r'V &= \\frac\{4}\{3} \\pi r\^3\\\\\s*' r'\\end{aligned}\\end{align\*}') assert re.search(macro, content, re.S) + + macro = r'Referencing equation \\eqref{equation:math:foo}.' + assert re.search(macro, content, re.S) diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py index d4ce96001..7ecd08292 100644 --- a/tests/test_ext_napoleon.py +++ b/tests/test_ext_napoleon.py @@ -16,7 +16,7 @@ from unittest import TestCase from sphinx.application import Sphinx from sphinx.ext.napoleon import (_process_docstring, _skip_member, Config, setup) -from util import mock +import mock def _private_doc(): diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 17c1a7a56..37dcca90c 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -19,7 +19,7 @@ from unittest import TestCase from sphinx.ext.napoleon import Config from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring -from util import mock +import mock class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))): @@ -222,6 +222,24 @@ class GoogleDocstringTest(BaseDocstringTest): """ Single line summary + Args: + arg1 (list(int)): Description + arg2 (list[int]): Description + arg3 (dict(str, int)): Description + arg4 (dict[str, int]): Description + """, + """ + Single line summary + + :Parameters: * **arg1** (*list(int)*) -- Description + * **arg2** (*list[int]*) -- Description + * **arg3** (*dict(str, int)*) -- Description + * **arg4** (*dict[str, int]*) -- Description + """ + ), ( + """ + Single line summary + Yield: str:Extended description of yielded value @@ -249,7 +267,11 @@ class GoogleDocstringTest(BaseDocstringTest): )] def test_docstrings(self): - config = Config(napoleon_use_param=False, napoleon_use_rtype=False) + config = Config( + napoleon_use_param=False, + napoleon_use_rtype=False, + napoleon_use_keyword=False + ) for docstring, expected in self.docstrings: actual = str(GoogleDocstring(dedent(docstring), config)) expected = dedent(expected) @@ -325,7 +347,9 @@ Returns: codecode """ expected = """ -:returns: foo:: +:returns: + + foo:: codecode codecode @@ -1046,7 +1070,10 @@ class NumpyDocstringTest(BaseDocstringTest): )] def test_docstrings(self): - config = Config(napoleon_use_param=False, napoleon_use_rtype=False) + config = Config( + napoleon_use_param=False, + napoleon_use_rtype=False, + napoleon_use_keyword=False) for docstring, expected in self.docstrings: actual = str(NumpyDocstring(dedent(docstring), config)) expected = dedent(expected) @@ -1736,3 +1763,19 @@ definition_after_normal_text : int config = Config(napoleon_use_param=False) actual = str(NumpyDocstring(docstring, config)) self.assertEqual(expected, actual) + + def test_keywords_with_types(self): + docstring = """\ +Do as you please + +Keyword Args: + gotham_is_yours (None): shall interfere. +""" + actual = str(GoogleDocstring(docstring)) + expected = """\ +Do as you please + +:keyword gotham_is_yours: shall interfere. +:kwtype gotham_is_yours: None +""" + self.assertEqual(expected, actual) diff --git a/tests/test_ext_todo.py b/tests/test_ext_todo.py new file mode 100644 index 000000000..269a8a2be --- /dev/null +++ b/tests/test_ext_todo.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +""" + test_ext_todo + ~~~~~~~~~~~~~ + + Test sphinx.ext.todo extension. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from util import with_app + + +@with_app('html', testroot='ext-todo', freshenv=True, + confoverrides={'todo_include_todos': True, 'todo_emit_warnings': True}) +def test_todo(app, status, warning): + todos = [] + + def on_todo_defined(app, node): + todos.append(node) + + app.connect('todo-defined', on_todo_defined) + app.builder.build_all() + + # check todolist + content = (app.outdir / 'index.html').text() + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in foo</p>') + assert re.search(html, content, re.S) + + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in bar</p>') + assert re.search(html, content, re.S) + + # check todo + content = (app.outdir / 'foo.html').text() + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in foo</p>') + assert re.search(html, content, re.S) + + # check emitted warnings + assert 'WARNING: TODO entry found: todo in foo' in warning.getvalue() + assert 'WARNING: TODO entry found: todo in bar' in warning.getvalue() + + # check handled event + assert len(todos) == 2 + assert set(todo[1].astext() for todo in todos) == set(['todo in foo', 'todo in bar']) + + +@with_app('html', testroot='ext-todo', freshenv=True, + confoverrides={'todo_include_todos': False, 'todo_emit_warnings': True}) +def test_todo_not_included(app, status, warning): + todos = [] + + def on_todo_defined(app, node): + todos.append(node) + + app.connect('todo-defined', on_todo_defined) + app.builder.build_all() + + # check todolist + content = (app.outdir / 'index.html').text() + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in foo</p>') + assert not re.search(html, content, re.S) + + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in bar</p>') + assert not re.search(html, content, re.S) + + # check todo + content = (app.outdir / 'foo.html').text() + html = ('<p class="first admonition-title">Todo</p>\n' + '<p class="last">todo in foo</p>') + assert not re.search(html, content, re.S) + + # check emitted warnings + assert 'WARNING: TODO entry found: todo in foo' in warning.getvalue() + assert 'WARNING: TODO entry found: todo in bar' in warning.getvalue() + + # check handled event + assert len(todos) == 2 + assert set(todo[1].astext() for todo in todos) == set(['todo in foo', 'todo in bar']) diff --git a/tests/test_intl.py b/tests/test_intl.py index b31f1678b..43cd17c8e 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -118,6 +118,11 @@ def assert_count(expected_expr, result, count): def test_text_builder(app, status, warning): app.builder.build_all() + # --- toctree + + result = (app.outdir / 'contents.txt').text(encoding='utf-8') + yield assert_startswith, result, u"CONTENTS\n********\n\nTABLE OF CONTENTS\n" + # --- warnings in translation warnings = getwarning(warning) @@ -126,7 +131,7 @@ def test_text_builder(app, status, warning): yield assert_re_search, warning_expr, warnings result = (app.outdir / 'warnings.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH REST WARNINGS" + expect = (u"I18N WITH REST WARNINGS" u"\n***********************\n" u"\nLINE OF >>``<<BROKEN LITERAL MARKUP.\n") yield assert_equal, result, expect @@ -134,7 +139,7 @@ def test_text_builder(app, status, warning): # --- simple translation; check title underlines result = (app.outdir / 'bom.txt').text(encoding='utf-8') - expect = (u"\nDatei mit UTF-8" + expect = (u"Datei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") yield assert_equal, result, expect @@ -142,12 +147,12 @@ def test_text_builder(app, status, warning): # --- check translation in subdirs result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8') - yield assert_startswith, result, u"\nsubdir contents\n***************\n" + yield assert_startswith, result, u"subdir contents\n***************\n" # --- check warnings for inconsistency in number of references result = (app.outdir / 'refs_inconsistency.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH REFS INCONSISTENCY" + expect = (u"I18N WITH REFS INCONSISTENCY" u"\n****************************\n" u"\n* FOR FOOTNOTE [ref2].\n" u"\n* reference FOR reference.\n" @@ -169,7 +174,7 @@ def test_text_builder(app, status, warning): # --- check warning for literal block result = (app.outdir / 'literalblock.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH LITERAL BLOCK" + expect = (u"I18N WITH LITERAL BLOCK" u"\n***********************\n" u"\nCORRECT LITERAL BLOCK:\n" u"\n this is" @@ -186,7 +191,7 @@ def test_text_builder(app, status, warning): # --- definition terms: regression test for #975, #2198, #2205 result = (app.outdir / 'definition_terms.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH DEFINITION TERMS" + expect = (u"I18N WITH DEFINITION TERMS" u"\n**************************\n" u"\nSOME TERM" u"\n THE CORRESPONDING DEFINITION\n" @@ -202,7 +207,7 @@ def test_text_builder(app, status, warning): # --- glossary terms: regression test for #1090 result = (app.outdir / 'glossary_terms.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH GLOSSARY TERMS" + expect = (u"I18N WITH GLOSSARY TERMS" u"\n************************\n" u"\nSOME NEW TERM" u"\n THE CORRESPONDING GLOSSARY\n" @@ -216,7 +221,7 @@ def test_text_builder(app, status, warning): # --- glossary term inconsistencies: regression test for #1090 result = (app.outdir / 'glossary_terms_inconsistency.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH GLOSSARY TERMS INCONSISTENCY" + expect = (u"I18N WITH GLOSSARY TERMS INCONSISTENCY" u"\n**************************************\n" u"\n1. LINK TO *SOME NEW TERM*.\n") yield assert_equal, result, expect @@ -230,7 +235,7 @@ def test_text_builder(app, status, warning): # --- seealso result = (app.outdir / 'seealso.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH SEEALSO" + expect = (u"I18N WITH SEEALSO" u"\n*****************\n" u"\nSee also: SHORT TEXT 1\n" u"\nSee also: LONG TEXT 1\n" @@ -241,7 +246,7 @@ def test_text_builder(app, status, warning): # --- figure captions: regression test for #940 result = (app.outdir / 'figure.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH FIGURE CAPTION" + expect = (u"I18N WITH FIGURE CAPTION" u"\n************************\n" u"\n [image]MY CAPTION OF THE FIGURE\n" u"\n MY DESCRIPTION PARAGRAPH1 OF THE FIGURE.\n" @@ -267,7 +272,7 @@ def test_text_builder(app, status, warning): # --- rubric: regression test for pull request #190 result = (app.outdir / 'rubric.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH RUBRIC" + expect = (u"I18N WITH RUBRIC" u"\n****************\n" u"\n-[ RUBRIC TITLE ]-\n" u"\n" @@ -280,7 +285,7 @@ def test_text_builder(app, status, warning): # --- docfields result = (app.outdir / 'docfields.txt').text(encoding='utf-8') - expect = (u"\nI18N WITH DOCFIELDS" + expect = (u"I18N WITH DOCFIELDS" u"\n*******************\n" u"\nclass Cls1\n" u"\n Parameters:" @@ -318,6 +323,12 @@ def test_text_builder(app, status, warning): def test_gettext_builder(app, status, warning): app.builder.build_all() + # --- toctree + expect = read_po(app.srcdir / 'contents.po') + actual = read_po(app.outdir / 'contents.pot') + for expect_msg in [m for m in expect if m.id]: + yield assert_in, expect_msg.id, [m.id for m in actual if m.id] + # --- definition terms: regression test for #2198, #2205 expect = read_po(app.srcdir / 'definition_terms.po') actual = read_po(app.outdir / 'definition_terms.pot') @@ -338,11 +349,25 @@ def test_gettext_builder(app, status, warning): for expect_msg in [m for m in expect if m.id]: yield assert_in, expect_msg.id, [m.id for m in actual if m.id] + # --- gettext builder always ignores ``only`` directive + expect = read_po(app.srcdir / 'only.po') + actual = read_po(app.outdir / 'only.pot') + for expect_msg in [m for m in expect if m.id]: + yield assert_in, expect_msg.id, [m.id for m in actual if m.id] + @gen_with_intl_app('html', freshenv=True) def test_html_builder(app, status, warning): app.builder.build_all() + # --- test for meta + + result = (app.outdir / 'contents.html').text(encoding='utf-8') + expected_expr = '<meta content="TESTDATA FOR I18N" name="description" />' + yield assert_in, expected_expr, result + expected_expr = '<meta content="I18N, SPHINX, MARKUP" name="keywords" />' + yield assert_in, expected_expr, result + # --- test for #955 cant-build-html-with-footnotes-when-using # expect no error by build @@ -373,7 +398,10 @@ def test_html_builder(app, status, warning): start_tag = "<%s[^>]*>" % tag end_tag = "</%s>" % tag return r"%s\s*%s\s*%s" % (start_tag, keyword, end_tag) - + def wrap_nest(parenttag, childtag, keyword): + start_tag1 = "<%s[^>]*>" % parenttag + start_tag2 = "<%s[^>]*>" % childtag + return r"%s\s*%s\s*%s" % (start_tag1, keyword, start_tag2) expected_exprs = [ wrap('a', 'NEWSLETTER'), wrap('a', 'MAILING LIST'), @@ -381,8 +409,8 @@ def test_html_builder(app, status, warning): wrap('a', 'FIRST SECOND'), wrap('a', 'SECOND THIRD'), wrap('a', 'THIRD, FIRST'), - wrap('dt', 'ENTRY'), - wrap('dt', 'SEE'), + wrap_nest('li', 'ul', 'ENTRY'), + wrap_nest('li', 'ul', 'SEE'), wrap('a', 'MODULE'), wrap('a', 'KEYWORD'), wrap('a', 'OPERATOR'), @@ -603,7 +631,7 @@ def test_xml_builder(app, status, warning): yield (assert_elem, para2[3], ['LINK TO', '--module', 'AND', '-m', '.'], - ['cmdoption--module', 'cmdoption-m']) + ['cmdoption-module', 'cmdoption-m']) yield (assert_elem, para2[4], ['LINK TO', 'env2', 'AND', 'env1', '.'], diff --git a/tests/test_markup.py b/tests/test_markup.py index 2fde2306c..ec203447f 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -15,31 +15,35 @@ import pickle from docutils import frontend, utils, nodes from docutils.parsers import rst +from sphinx import addnodes from sphinx.util import texescape +from sphinx.util.docutils import sphinx_domains from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator from util import TestApp, with_app, assert_node -app = settings = parser = None +app = settings = parser = domain_context = None def setup_module(): - global app, settings, parser + global app, settings, parser, domain_context texescape.init() # otherwise done by the latex builder app = TestApp() optparser = frontend.OptionParser( components=(rst.Parser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() settings.env = app.builder.env - settings.env.patch_lookup_functions() settings.env.temp_data['docname'] = 'dummy' parser = rst.Parser() + domain_context = sphinx_domains(settings.env) + domain_context.enable() def teardown_module(): app.cleanup() + domain_context.disable() # since we're not resolving the markup afterwards, these nodes may remain @@ -101,7 +105,7 @@ def test_inline(): '<p><code class="samp docutils literal"><span class="pre">a</span>' '<em><span class="pre">b</span></em>' '<span class="pre">c</span></code></p>', - '\\sphinxcode{a\\emph{b}c}') + '\\sphinxcode{a\\sphinxstyleemphasis{b}c}') # interpolation of arrows in menuselection yield (verify, ':menuselection:`a --> b`', @@ -131,7 +135,7 @@ def test_inline(): # verify classes for inline roles yield (verify, ':manpage:`mp(1)`', '<p><em class="manpage">mp(1)</em></p>', - '\\emph{\\texttt{mp(1)}}') + '\\sphinxstyleliteralemphasis{mp(1)}') def test_latex_escaping(): @@ -140,9 +144,9 @@ def test_latex_escaping(): r'\(\Gamma\)\textbackslash{}\(\infty\)\$') # in verbatim code fragments yield (verify, u'::\n\n @Γ\\∞${}', None, - u'\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n' u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n' - u'\\end{Verbatim}') + u'\\end{sphinxVerbatim}') # in URIs yield (verify_re, u'`test <http://example.com/~me/>`_', None, r'\\href{http://example.com/~me/}{test}.*') @@ -170,3 +174,39 @@ def test_rst_prolog(app, status, warning): # rst_prolog & rst_epilog on exlucding reST parser assert not md.rawsource.startswith('*Hello world*.') assert not md.rawsource.endswith('*Good-bye world*.\n') + + +@with_app(buildername='dummy', testroot='keep_warnings') +def test_keep_warnings_is_True(app, status, warning): + app.builder.build_all() + doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + assert_node(doctree[0], nodes.section) + assert len(doctree[0]) == 2 + assert_node(doctree[0][1], nodes.system_message) + + +@with_app(buildername='dummy', testroot='keep_warnings', + confoverrides={'keep_warnings': False}) +def test_keep_warnings_is_False(app, status, warning): + app.builder.build_all() + doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + assert_node(doctree[0], nodes.section) + assert len(doctree[0]) == 1 + + +@with_app(buildername='dummy', testroot='refonly_bullet_list') +def test_compact_refonly_bullet_list(app, status, warning): + app.builder.build_all() + doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes()) + assert_node(doctree[0], nodes.section) + assert len(doctree[0]) == 5 + + assert doctree[0][1].astext() == 'List A:' + assert_node(doctree[0][2], nodes.bullet_list) + assert_node(doctree[0][2][0][0], addnodes.compact_paragraph) + assert doctree[0][2][0][0].astext() == 'genindex' + + assert doctree[0][3].astext() == 'List B:' + assert_node(doctree[0][4], nodes.bullet_list) + assert_node(doctree[0][4][0][0], nodes.paragraph) + assert doctree[0][4][0][0].astext() == 'Hello' diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index ac6507ce5..7a77ce225 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -298,3 +298,16 @@ def test_default_filename(tempdir): assert ns['latex_documents'][0][1] == 'sphinx.tex' assert ns['man_pages'][0][1] == 'sphinx' assert ns['texinfo_documents'][0][1] == 'sphinx' + + +@with_tempdir +def test_extensions(tempdir): + qs.main(['sphinx-quickstart', '-q', + '-p', 'project_name', '-a', 'author', + '--extensions', 'foo,bar,baz', tempdir]) + + conffile = tempdir / 'conf.py' + assert conffile.isfile() + ns = {} + execfile_(conffile, ns) + assert ns['extensions'] == ['foo', 'bar', 'baz'] diff --git a/tests/test_search.py b/tests/test_search.py index b7c38674f..fb7d47d6f 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import os from docutils import frontend, utils from docutils.parsers import rst @@ -28,6 +29,17 @@ def setup_module(): parser = rst.Parser() +def jsload(path): + searchindex = path.text() + assert searchindex.startswith('Search.setIndex(') + + return jsdump.loads(searchindex[16:-2]) + + +def is_registered_term(index, keyword): + return index['terms'].get(keyword, []) != [] + + FILE_CONTENTS = '''\ .. test that comments are not indexed: boson @@ -41,7 +53,7 @@ def test_wordcollector(): parser.parse(FILE_CONTENTS, doc) ix = IndexBuilder(None, 'en', {}, None) - ix.feed('filename', 'title', doc) + ix.feed('docname', 'filename', 'title', doc) assert 'boson' not in ix._mapping assert 'fermion' in ix._mapping @@ -54,3 +66,62 @@ def test_objects_are_escaped(app, status, warning): index = jsdump.loads(searchindex[16:-2]) assert 'n::Array<T, d>' in index.get('objects').get('') # n::Array<T,d> is escaped + + +@with_app(testroot='search') +def test_meta_keys_are_handled_for_language_en(app, status, warning): + app.builder.build_all() + searchindex = jsload(app.outdir / 'searchindex.js') + assert not is_registered_term(searchindex, 'thisnoteith') + assert is_registered_term(searchindex, 'thisonetoo') + assert is_registered_term(searchindex, 'findthiskei') + assert is_registered_term(searchindex, 'thistoo') + assert not is_registered_term(searchindex, 'onlygerman') + assert is_registered_term(searchindex, 'notgerman') + assert not is_registered_term(searchindex, 'onlytoogerman') + + +@with_app(testroot='search', confoverrides={'html_search_language': 'de'}) +def test_meta_keys_are_handled_for_language_de(app, status, warning): + app.builder.build_all() + searchindex = jsload(app.outdir / 'searchindex.js') + assert not is_registered_term(searchindex, 'thisnoteith') + assert is_registered_term(searchindex, 'thisonetoo') + assert not is_registered_term(searchindex, 'findthiskei') + assert not is_registered_term(searchindex, 'thistoo') + assert is_registered_term(searchindex, 'onlygerman') + assert not is_registered_term(searchindex, 'notgerman') + assert is_registered_term(searchindex, 'onlytoogerman') + + +@with_app(testroot='search') +def test_stemmer_does_not_remove_short_words(app, status, warning): + app.builder.build_all() + searchindex = (app.outdir / 'searchindex.js').text() + assert 'zfs' in searchindex + + +@with_app(testroot='search') +def test_stemmer(app, status, warning): + searchindex = jsload(app.outdir / 'searchindex.js') + print(searchindex) + assert is_registered_term(searchindex, 'findthisstemmedkei') + assert is_registered_term(searchindex, 'intern') + + +@with_app(testroot='search') +def test_term_in_heading_and_section(app, status, warning): + searchindex = (app.outdir / 'searchindex.js').text() + # if search term is in the title of one doc and in the text of another + # both documents should be a hit in the search index as a title, + # respectively text hit + assert 'textinhead:1' in searchindex + assert 'textinhead:0' in searchindex + + +@with_app(testroot='search') +def test_term_in_raw_directive(app, status, warning): + searchindex = jsload(app.outdir / 'searchindex.js') + assert not is_registered_term(searchindex, 'raw') + assert is_registered_term(searchindex, 'rawword') + assert not is_registered_term(searchindex, 'latex_keyword') diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index 78f9b64ab..ee350182f 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -41,7 +41,7 @@ def search_adapter_helper(adapter): # Make sure documents are properly updated by the search adapter. s.init_indexing(changed=['markup']) - s.add_document(u'markup', u'title', u'SomeLongRandomWord') + s.add_document(u'markup', u'filename', u'title', u'SomeLongRandomWord') s.finish_indexing() # Now a search for "Epigraph" should return zero results. results = s.query(u'Epigraph') diff --git a/tests/test_setup_command.py b/tests/test_setup_command.py index 77c9ade46..c92f6220f 100644 --- a/tests/test_setup_command.py +++ b/tests/test_setup_command.py @@ -108,3 +108,25 @@ def test_build_sphinx_return_nonzero_status(pkgroot, proc): print(out) print(err) assert proc.returncode != 0, 'expect non-zero status for setup.py' + + +@with_setup_command(root) +def test_build_sphinx_warning_return_zero_status(pkgroot, proc): + srcdir = (pkgroot / 'doc') + (srcdir / 'contents.txt').write_text( + 'See :ref:`unexisting-reference-label`') + out, err = proc.communicate() + print(out) + print(err) + assert proc.returncode == 0 + + +@with_setup_command(root, '--warning-is-error') +def test_build_sphinx_warning_is_error_return_nonzero_status(pkgroot, proc): + srcdir = (pkgroot / 'doc') + (srcdir / 'contents.txt').write_text( + 'See :ref:`unexisting-reference-label`') + out, err = proc.communicate() + print(out) + print(err) + assert proc.returncode != 0, 'expect non-zero status for setup.py' diff --git a/tests/test_theming.py b/tests/test_theming.py index 07787ecca..b62cbcd72 100644 --- a/tests/test_theming.py +++ b/tests/test_theming.py @@ -12,9 +12,11 @@ import os import zipfile +import mock + from sphinx.theming import Theme, ThemeError -from util import with_app, raises, mock, path +from util import with_app, raises, path @with_app(confoverrides={'html_theme': 'ziptheme', @@ -26,7 +28,7 @@ def test_theme_api(app, status, warning): assert set(Theme.themes.keys()) == \ set(['basic', 'default', 'scrolls', 'agogo', 'sphinxdoc', 'haiku', 'traditional', 'testtheme', 'ziptheme', 'epub', 'nature', - 'pyramid', 'bizstyle', 'classic']) + 'pyramid', 'bizstyle', 'classic', 'nonav']) assert Theme.themes['testtheme'][1] is None assert isinstance(Theme.themes['ziptheme'][1], zipfile.ZipFile) @@ -66,7 +68,7 @@ def test_js_source(app, status, warning): app.builder.build(['contents']) - v = '1.11.1' + v = '3.1.0' msg = 'jquery.js version does not match to {v}'.format(v=v) jquery_min = (app.outdir / '_static' / 'jquery.js').text() assert 'jQuery v{v}'.format(v=v) in jquery_min, msg diff --git a/tests/test_util_fileutil.py b/tests/test_util_fileutil.py new file mode 100644 index 000000000..5810dd2a8 --- /dev/null +++ b/tests/test_util_fileutil.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +""" + test_util_fileutil + ~~~~~~~~~~~~~~~~~~ + + Tests sphinx.util.fileutil functions. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from sphinx.util.fileutil import copy_asset, copy_asset_file +from sphinx.jinja2glue import BuiltinTemplateLoader + +import mock +from util import with_tempdir + + +class DummyTemplateLoader(BuiltinTemplateLoader): + def __init__(self): + BuiltinTemplateLoader.__init__(self) + builder = mock.Mock() + builder.config.templates_path = [] + builder.app.translater = None + self.init(builder) + + +@with_tempdir +def test_copy_asset_file(tmpdir): + renderer = DummyTemplateLoader() + + # copy normal file + src = (tmpdir / 'asset.txt') + src.write_text('# test data') + dest = (tmpdir / 'output.txt') + + copy_asset_file(src, dest) + assert dest.exists() + assert src.text() == dest.text() + + # copy template file + src = (tmpdir / 'asset.txt_t') + src.write_text('# {{var1}} data') + dest = (tmpdir / 'output.txt_t') + + copy_asset_file(src, dest, {'var1': 'template'}, renderer) + assert not dest.exists() + assert (tmpdir / 'output.txt').exists() + assert (tmpdir / 'output.txt').text() == '# template data' + + # copy template file to subdir + src = (tmpdir / 'asset.txt_t') + src.write_text('# {{var1}} data') + subdir1 = (tmpdir / 'subdir') + subdir1.makedirs() + + copy_asset_file(src, subdir1, {'var1': 'template'}, renderer) + assert (subdir1 / 'asset.txt').exists() + assert (subdir1 / 'asset.txt').text() == '# template data' + + # copy template file without context + src = (tmpdir / 'asset.txt_t') + subdir2 = (tmpdir / 'subdir2') + subdir2.makedirs() + + copy_asset_file(src, subdir2) + assert not (subdir2 / 'asset.txt').exists() + assert (subdir2 / 'asset.txt_t').exists() + assert (subdir2 / 'asset.txt_t').text() == '# {{var1}} data' + + +@with_tempdir +def test_copy_asset(tmpdir): + renderer = DummyTemplateLoader() + + # prepare source files + source = (tmpdir / 'source') + source.makedirs() + (source / 'index.rst').write_text('index.rst') + (source / 'foo.rst_t').write_text('{{var1}}.rst') + (source / '_static').makedirs() + (source / '_static' / 'basic.css').write_text('basic.css') + (source / '_templates').makedirs() + (source / '_templates' / 'layout.html').write_text('layout.html') + (source / '_templates' / 'sidebar.html_t').write_text('sidebar: {{var2}}') + + # copy a single file + assert not (tmpdir / 'test1').exists() + copy_asset(source / 'index.rst', tmpdir / 'test1') + assert (tmpdir / 'test1').exists() + assert (tmpdir / 'test1/index.rst').exists() + + # copy directories + destdir = tmpdir / 'test2' + copy_asset(source, destdir, context=dict(var1='bar', var2='baz'), renderer=renderer) + assert (destdir / 'index.rst').exists() + assert (destdir / 'foo.rst').exists() + assert (destdir / 'foo.rst').text() == 'bar.rst' + assert (destdir / '_static' / 'basic.css').exists() + assert (destdir / '_templates' / 'layout.html').exists() + assert (destdir / '_templates' / 'sidebar.html').exists() + assert (destdir / '_templates' / 'sidebar.html').text() == 'sidebar: baz' + + # copy with exclusion + def excluded(path): + return ('sidebar.html' in path or 'basic.css' in path) + + destdir = tmpdir / 'test3' + copy_asset(source, destdir, excluded, + context=dict(var1='bar', var2='baz'), renderer=renderer) + assert (destdir / 'index.rst').exists() + assert (destdir / 'foo.rst').exists() + assert not (destdir / '_static' / 'basic.css').exists() + assert (destdir / '_templates' / 'layout.html').exists() + assert not (destdir / '_templates' / 'sidebar.html').exists() diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py index 3e0cfd5f3..849796a8f 100644 --- a/tests/test_util_i18n.py +++ b/tests/test_util_i18n.py @@ -255,6 +255,18 @@ def test_get_filename_for_language(): '../foo.png', app.env) == 'images/en/../foo.png' assert i18n.get_image_filename_for_language('foo', app.env) == 'images/en/foo' + # new path and basename tokens + app.env.config.language = 'en' + app.env.config.figure_language_filename = '{path}{language}/{basename}{ext}' + assert i18n.get_image_filename_for_language('foo.png', app.env) == 'en/foo.png' + assert i18n.get_image_filename_for_language( + 'foo.bar.png', app.env) == 'en/foo.bar.png' + assert i18n.get_image_filename_for_language( + 'subdir/foo.png', app.env) == 'subdir/en/foo.png' + assert i18n.get_image_filename_for_language( + '../foo.png', app.env) == '../en/foo.png' + assert i18n.get_image_filename_for_language('foo', app.env) == 'en/foo' + # invalid figure_language_filename app.env.config.figure_language_filename = '{root}.{invalid}{ext}' raises(SphinxError, i18n.get_image_filename_for_language, 'foo.png', app.env) diff --git a/tests/test_util_matching.py b/tests/test_util_matching.py new file mode 100644 index 000000000..9e99a5322 --- /dev/null +++ b/tests/test_util_matching.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +""" + test_util_matching + ~~~~~~~~~~~~~~~~~~ + + Tests sphinx.util.matching functions. + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from sphinx.util.matching import compile_matchers, Matcher + + +def test_compile_matchers(): + # exact matching + pat = compile_matchers(['hello.py']).pop() + assert pat('hello.py') + assert not pat('hello-py') + assert not pat('subdir/hello.py') + + # wild card (*) + pat = compile_matchers(['hello.*']).pop() + assert pat('hello.py') + assert pat('hello.rst') + + pat = compile_matchers(['*.py']).pop() + assert pat('hello.py') + assert pat('world.py') + assert not pat('subdir/hello.py') + + # wild card (**) + pat = compile_matchers(['hello.**']).pop() + assert pat('hello.py') + assert pat('hello.rst') + assert pat('hello.py/world.py') + + pat = compile_matchers(['**.py']).pop() + assert pat('hello.py') + assert pat('world.py') + assert pat('subdir/hello.py') + + pat = compile_matchers(['**/hello.py']).pop() + assert not pat('hello.py') + assert pat('subdir/hello.py') + assert pat('subdir/subdir/hello.py') + + # wild card (?) + pat = compile_matchers(['hello.?']).pop() + assert pat('hello.c') + assert not pat('hello.py') + + # pattern ([...]) + pat = compile_matchers(['hello[12\\].py']).pop() + assert pat('hello1.py') + assert pat('hello2.py') + assert pat('hello\\.py') + assert not pat('hello3.py') + + pat = compile_matchers(['hello[^12].py']).pop() # "^" is not negative identifier + assert pat('hello1.py') + assert pat('hello2.py') + assert pat('hello^.py') + assert not pat('hello3.py') + + # negative pattern ([!...]) + pat = compile_matchers(['hello[!12].py']).pop() + assert not pat('hello1.py') + assert not pat('hello2.py') + assert not pat('hello/.py') # negative pattern does not match to "/" + assert pat('hello3.py') + + # non patterns + pat = compile_matchers(['hello[.py']).pop() + assert pat('hello[.py') + assert not pat('hello.py') + + pat = compile_matchers(['hello[].py']).pop() + assert pat('hello[].py') + assert not pat('hello.py') + + pat = compile_matchers(['hello[!].py']).pop() + assert pat('hello[!].py') + assert not pat('hello.py') + + +def test_Matcher(): + matcher = Matcher(['hello.py', '**/world.py']) + assert matcher('hello.py') + assert not matcher('subdir/hello.py') + assert matcher('world.py') + assert matcher('subdir/world.py') diff --git a/tests/test_writer_latex.py b/tests/test_writer_latex.py new file mode 100644 index 000000000..72eb7ed2a --- /dev/null +++ b/tests/test_writer_latex.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" + test_writer_latex + ~~~~~~~~~~~~~~~~ + + Test the LaTeX writer + + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import print_function +from sphinx.writers.latex import rstdim_to_latexdim + +from util import raises + + +def test_rstdim_to_latexdim(): + # Length units docutils supported + # http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units + assert rstdim_to_latexdim('160em') == '160em' + assert rstdim_to_latexdim('160px') == '160\\sphinxpxdimen' + assert rstdim_to_latexdim('160in') == '160in' + assert rstdim_to_latexdim('160cm') == '160cm' + assert rstdim_to_latexdim('160mm') == '160mm' + assert rstdim_to_latexdim('160pt') == '160bp' + assert rstdim_to_latexdim('160pc') == '160pc' + assert rstdim_to_latexdim('30%') == '0.300\\linewidth' + assert rstdim_to_latexdim('160') == '160\\sphinxpxdimen' + + # flaot values + assert rstdim_to_latexdim('160.0em') == '160.0em' + assert rstdim_to_latexdim('.5em') == '.5em' + + # unknown values (it might be generated by 3rd party extension) + raises(ValueError, rstdim_to_latexdim, 'unknown') + assert rstdim_to_latexdim('160.0unknown') == '160.0unknown' diff --git a/tests/typing_test_data.py b/tests/typing_test_data.py index 3c3126b07..84bb3377b 100644 --- a/tests/typing_test_data.py +++ b/tests/typing_test_data.py @@ -35,20 +35,24 @@ def f5(x: int, *, y: str, z: str) -> None: pass -def f6(x: int = None, y: dict = {}) -> None: +def f6(x: int, *args, y: str, z: str) -> None: pass -def f7(x: Callable[[int, str], int]) -> None: +def f7(x: int = None, y: dict = {}) -> None: + pass + + +def f8(x: Callable[[int, str], int]) -> None: # See https://github.com/ambv/typehinting/issues/149 for Callable[..., int] pass -def f8(x: Callable) -> None: +def f9(x: Callable) -> None: pass -def f9(x: Tuple[int, str], y: Tuple[int, ...]) -> None: +def f10(x: Tuple[int, str], y: Tuple[int, ...]) -> None: pass @@ -57,5 +61,5 @@ class CustomAnnotation: return 'CustomAnnotation' -def f10(x: CustomAnnotation(), y: 123) -> None: +def f11(x: CustomAnnotation(), y: 123) -> None: pass diff --git a/tests/util.py b/tests/util.py index cb0d3f7a0..120492d47 100644 --- a/tests/util.py +++ b/tests/util.py @@ -13,7 +13,7 @@ import sys import tempfile from functools import wraps -from six import StringIO +from six import StringIO, string_types from nose import tools, SkipTest @@ -28,12 +28,6 @@ from sphinx.pycode import ModuleAnalyzer from path import path, repr_as # NOQA -try: - # Python >=3.3 - from unittest import mock -except ImportError: - import mock - __all__ = [ 'rootdir', 'tempdir', 'raises', 'raises_msg', @@ -41,7 +35,6 @@ __all__ = [ 'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'path', 'with_tempdir', 'sprint', 'remove_unicode_literals', - 'mock', ] @@ -94,14 +87,33 @@ def assert_startswith(thing, prefix): assert False, '%r does not start with %r' % (thing, prefix) -def assert_node(node, cls=None, **kwargs): +def assert_node(node, cls=None, xpath="", **kwargs): if cls: - assert isinstance(node, cls), '%r is not subclass of %r' % (node, cls) + if isinstance(cls, list): + assert_node(node, cls[0], xpath=xpath, **kwargs) + if cls[1:]: + if isinstance(cls[1], tuple): + assert_node(node, cls[1], xpath=xpath, **kwargs) + else: + assert len(node) == 1, \ + 'The node%s has %d child nodes, not one' % (xpath, len(node)) + assert_node(node[0], cls[1:], xpath=xpath + "[0]", **kwargs) + elif isinstance(cls, tuple): + assert len(node) == len(cls), \ + 'The node%s has %d child nodes, not %r' % (xpath, len(node), len(cls)) + for i, nodecls in enumerate(cls): + path = xpath + "[%d]" % i + assert_node(node[i], nodecls, xpath=path, **kwargs) + elif isinstance(cls, string_types): + assert node == cls, 'The node %r is not %r: %r' % (xpath, cls, node) + else: + assert isinstance(node, cls), \ + 'The node%s is not subclass of %r: %r' % (xpath, cls, node) for key, value in kwargs.items(): - assert key in node, '%r does not have %r attribute' % (node, key) + assert key in node, 'The node%s does not have %r attribute: %r' % (xpath, key, node) assert node[key] == value, \ - '%r[%s]: %r does not equals %r' % (node, key, node[key], value) + 'The node%s[%s] is not %r: %r' % (xpath, key, value, node[key]) try: @@ -309,3 +321,7 @@ def find_files(root, suffix=None): for f in [f for f in files if not suffix or f.endswith(suffix)]: fpath = dirpath / f yield os.path.relpath(fpath, root) + + +def strip_escseq(text): + return re.sub('\x1b.*?m', '', text) |