Organise tests into directories

This commit is contained in:
Adam Turner
2024-01-17 02:38:46 +00:00
parent 841f2bd9b7
commit 462404cb25
121 changed files with 467 additions and 442 deletions

View File

View File

@@ -0,0 +1,595 @@
"""Test the code-block directive."""
import os.path
import pytest
from docutils import nodes
from sphinx.config import Config
from sphinx.directives.code import LiteralIncludeReader
from sphinx.testing.util import etree_parse
DUMMY_CONFIG = Config({}, {})
@pytest.fixture(scope='module')
def testroot(rootdir):
testroot_path = rootdir / 'test-directive-code'
return testroot_path
@pytest.fixture(scope='module')
def literal_inc_path(testroot):
return testroot / 'literal.inc'
def test_LiteralIncludeReader(literal_inc_path):
options = {'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == literal_inc_path.read_text(encoding='utf8')
assert lines == 13
assert reader.lineno_start == 1
def test_LiteralIncludeReader_lineno_start(literal_inc_path):
options = {'lineno-start': 4}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == literal_inc_path.read_text(encoding='utf8')
assert lines == 13
assert reader.lineno_start == 4
def test_LiteralIncludeReader_pyobject1(literal_inc_path):
options = {'lineno-match': True, 'pyobject': 'Foo'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Foo:\n"
" pass\n")
assert reader.lineno_start == 5
def test_LiteralIncludeReader_pyobject2(literal_inc_path):
options = {'pyobject': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Bar:\n"
" def baz():\n"
" pass\n")
assert reader.lineno_start == 1 # no lineno-match
def test_LiteralIncludeReader_pyobject3(literal_inc_path):
options = {'pyobject': 'Bar.baz'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == (" def baz():\n"
" pass\n")
def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
options = {'pyobject': 'Bar', 'lines': '2-'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == (" def baz():\n"
" pass\n")
def test_LiteralIncludeReader_lines1(literal_inc_path):
options = {'lines': '1-3'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("# Literally included file using Python highlighting\n"
"\n"
"foo = \"Including Unicode characters: üöä\"\n")
def test_LiteralIncludeReader_lines2(literal_inc_path):
options = {'lines': '1,3,5'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("# Literally included file using Python highlighting\n"
"foo = \"Including Unicode characters: üöä\"\n"
"class Foo:\n")
def test_LiteralIncludeReader_lines_and_lineno_match1(literal_inc_path):
options = {'lines': '3-5', 'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("foo = \"Including Unicode characters: üöä\"\n"
"\n"
"class Foo:\n")
assert reader.lineno_start == 3
@pytest.mark.sphinx() # init locale for errors
def test_LiteralIncludeReader_lines_and_lineno_match2(literal_inc_path, app, status, warning):
options = {'lines': '0,3,5', 'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match='Cannot use "lineno-match" with a disjoint set of "lines"'):
reader.read()
@pytest.mark.sphinx() # init locale for errors
def test_LiteralIncludeReader_lines_and_lineno_match3(literal_inc_path, app, status, warning):
options = {'lines': '100-', 'lineno-match': True}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match="Line spec '100-': no lines pulled from include file"):
reader.read()
def test_LiteralIncludeReader_start_at(literal_inc_path):
options = {'lineno-match': True, 'start-at': 'Foo', 'end-at': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Foo:\n"
" pass\n"
"\n"
"class Bar:\n")
assert reader.lineno_start == 5
def test_LiteralIncludeReader_start_after(literal_inc_path):
options = {'lineno-match': True, 'start-after': 'Foo', 'end-before': 'Bar'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == (" pass\n"
"\n")
assert reader.lineno_start == 6
def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
options = {'lineno-match': True, 'lines': '6-',
'start-after': 'Literally', 'end-before': 'comment'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("\n"
"class Bar:\n"
" def baz():\n"
" pass\n"
"\n")
assert reader.lineno_start == 7
def test_LiteralIncludeReader_start_at_and_lines(literal_inc_path):
options = {'lines': '2, 3, 5', 'start-at': 'foo', 'end-before': '#'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("\n"
"class Foo:\n"
"\n")
assert reader.lineno_start == 1
def test_LiteralIncludeReader_missing_start_and_end(literal_inc_path):
options = {'start-at': 'NOTHING'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match='start-at pattern not found: NOTHING'):
reader.read()
options = {'end-at': 'NOTHING'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match='end-at pattern not found: NOTHING'):
reader.read()
options = {'start-after': 'NOTHING'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match='start-after pattern not found: NOTHING'):
reader.read()
options = {'end-before': 'NOTHING'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
with pytest.raises(ValueError, match='end-before pattern not found: NOTHING'):
reader.read()
def test_LiteralIncludeReader_end_before(literal_inc_path):
options = {'end-before': 'nclud'} # *nclud* matches first and third lines.
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("# Literally included file using Python highlighting\n"
"\n")
def test_LiteralIncludeReader_prepend(literal_inc_path):
options = {'lines': '1', 'prepend': 'Hello', 'append': 'Sphinx'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("Hello\n"
"# Literally included file using Python highlighting\n"
"Sphinx\n")
def test_LiteralIncludeReader_dedent(literal_inc_path):
# dedent: 2
options = {'lines': '9-11', 'dedent': 2}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == (" def baz():\n"
" pass\n"
"\n")
# dedent: 4
options = {'lines': '9-11', 'dedent': 4}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("def baz():\n"
" pass\n"
"\n")
# dedent: 6
options = {'lines': '9-11', 'dedent': 6}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("f baz():\n"
" pass\n"
"\n")
# dedent: None
options = {'lines': '9-11', 'dedent': None}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("def baz():\n"
" pass\n"
"\n")
def test_LiteralIncludeReader_dedent_and_append_and_prepend(literal_inc_path):
# dedent: 2
options = {'lines': '9-11', 'dedent': 2, 'prepend': 'class Foo:', 'append': '# comment'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Foo:\n"
" def baz():\n"
" pass\n"
"\n"
"# comment\n")
def test_LiteralIncludeReader_tabwidth(testroot):
# tab-width: 4
options = {'tab-width': 4, 'pyobject': 'Qux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Qux:\n"
" def quux(self):\n"
" pass\n")
# tab-width: 8
options = {'tab-width': 8, 'pyobject': 'Qux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("class Qux:\n"
" def quux(self):\n"
" pass\n")
def test_LiteralIncludeReader_tabwidth_dedent(testroot):
options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("def quux(self):\n"
" pass\n")
def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
options = {'diff': testroot / 'literal-diff.inc'}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("--- " + os.path.join(testroot, 'literal-diff.inc') + "\n"
"+++ " + os.path.join(testroot, 'literal.inc') + "\n"
"@@ -6,8 +6,8 @@\n"
" pass\n"
" \n"
" class Bar:\n"
"- def baz(self):\n"
"+ def baz():\n"
" pass\n"
" \n"
"-# comment after Bar class\n"
"+# comment after Bar class definition\n"
" def bar(): pass\n")
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_code_block(app, status, warning):
app.build(filenames=[app.srcdir / 'index.rst'])
et = etree_parse(app.outdir / 'index.xml')
secs = et.findall('./section/section')
code_block = secs[0].findall('literal_block')
assert len(code_block) > 0
actual = code_block[0].text
expect = (
" def ruby?\n" +
" false\n" +
" end"
)
assert actual == expect
@pytest.mark.sphinx('html', testroot='directive-code')
def test_force_option(app, status, warning):
app.build(filenames=[app.srcdir / 'force.rst'])
assert 'force.rst' not in warning.getvalue()
@pytest.mark.sphinx('html', testroot='directive-code')
def test_code_block_caption_html(app, status, warning):
app.build(filenames=[app.srcdir / 'caption.rst'])
html = (app.outdir / 'caption.html').read_text(encoding='utf8')
caption = ('<div class="code-block-caption">'
'<span class="caption-number">Listing 1 </span>'
'<span class="caption-text">caption <em>test</em> rb'
'</span><a class="headerlink" href="#id1" '
'title="Link to this code">\xb6</a></div>')
assert caption in html
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_code_block_caption_latex(app, status, warning):
app.build(force_all=True)
latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstyleemphasis{test} rb}'
label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id1}}}'
link = '\\hyperref[\\detokenize{caption:name-test-rb}]' \
'{Listing \\ref{\\detokenize{caption:name-test-rb}}}'
assert caption in latex
assert label in latex
assert link in latex
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_code_block_namedlink_latex(app, status, warning):
app.build(force_all=True)
latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
label1 = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:name-test-rb}}}'
link1 = '\\hyperref[\\detokenize{caption:name-test-rb}]'\
'{\\sphinxcrossref{\\DUrole{std,std-ref}{Ruby}}'
label2 = ('\\def\\sphinxLiteralBlockLabel'
'{\\label{\\detokenize{namedblocks:some-ruby-code}}}')
link2 = '\\hyperref[\\detokenize{namedblocks:some-ruby-code}]'\
'{\\sphinxcrossref{\\DUrole{std,std-ref}{the ruby code}}}'
assert label1 in latex
assert link1 in latex
assert label2 in latex
assert link2 in latex
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_code_block_emphasize_latex(app, status, warning):
app.build(filenames=[app.srcdir / 'emphasize.rst'])
latex = (app.outdir / 'python.tex').read_text(encoding='utf8').replace('\r\n', '\n')
includes = '\\fvset{hllines={, 5, 6, 13, 14, 15, 24, 25, 26,}}%\n'
assert includes in latex
includes = '\\end{sphinxVerbatim}\n\\sphinxresetverbatimhllines\n'
assert includes in latex
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_literal_include(app, status, warning):
app.build(filenames=[app.srcdir / 'index.rst'])
et = etree_parse(app.outdir / 'index.xml')
secs = et.findall('./section/section')
literal_include = secs[1].findall('literal_block')
literal_src = (app.srcdir / 'literal.inc').read_text(encoding='utf8')
assert len(literal_include) > 0
actual = literal_include[0].text
assert actual == literal_src
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_literal_include_block_start_with_comment_or_brank(app, status, warning):
app.build(filenames=[app.srcdir / 'python.rst'])
et = etree_parse(app.outdir / 'python.xml')
secs = et.findall('./section/section')
literal_include = secs[0].findall('literal_block')
assert len(literal_include) > 0
actual = literal_include[0].text
expect = (
'def block_start_with_comment():\n'
' # Comment\n'
' return 1\n'
)
assert actual == expect
actual = literal_include[1].text
expect = (
'def block_start_with_blank():\n'
'\n'
' return 1\n'
)
assert actual == expect
@pytest.mark.sphinx('html', testroot='directive-code')
def test_literal_include_linenos(app, status, warning):
app.build(filenames=[app.srcdir / 'linenos.rst'])
html = (app.outdir / 'linenos.html').read_text(encoding='utf8')
# :linenos:
assert ('<span class="linenos"> 1</span><span class="c1">'
'# Literally included file using Python highlighting</span>' in html)
# :lineno-start:
assert ('<span class="linenos">200</span><span class="c1">'
'# Literally included file using Python highlighting</span>' in html)
# :lines: 5-9
assert ('<span class="linenos">5</span><span class="k">class</span> '
'<span class="nc">Foo</span><span class="p">:</span>' in html)
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_literalinclude_file_whole_of_emptyline(app, status, warning):
app.build(force_all=True)
latex = (app.outdir / 'python.tex').read_text(encoding='utf8').replace('\r\n', '\n')
includes = (
'\\begin{sphinxVerbatim}'
'[commandchars=\\\\\\{\\},numbers=left,firstnumber=1,stepnumber=1]\n'
'\n'
'\n'
'\n'
'\\end{sphinxVerbatim}\n')
assert includes in latex
@pytest.mark.sphinx('html', testroot='directive-code')
def test_literalinclude_caption_html(app, status, warning):
app.build(force_all=True)
html = (app.outdir / 'caption.html').read_text(encoding='utf8')
caption = ('<div class="code-block-caption">'
'<span class="caption-number">Listing 2 </span>'
'<span class="caption-text">caption <strong>test</strong> py'
'</span><a class="headerlink" href="#id2" '
'title="Link to this code">\xb6</a></div>')
assert caption in html
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_literalinclude_caption_latex(app, status, warning):
app.build(filenames='index')
latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstylestrong{test} py}'
label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id2}}}'
link = '\\hyperref[\\detokenize{caption:name-test-py}]' \
'{Listing \\ref{\\detokenize{caption:name-test-py}}}'
assert caption in latex
assert label in latex
assert link in latex
@pytest.mark.sphinx('latex', testroot='directive-code')
def test_literalinclude_namedlink_latex(app, status, warning):
app.build(filenames='index')
latex = (app.outdir / 'python.tex').read_text(encoding='utf8')
label1 = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:name-test-py}}}'
link1 = '\\hyperref[\\detokenize{caption:name-test-py}]'\
'{\\sphinxcrossref{\\DUrole{std,std-ref}{Python}}'
label2 = ('\\def\\sphinxLiteralBlockLabel'
'{\\label{\\detokenize{namedblocks:some-python-code}}}')
link2 = '\\hyperref[\\detokenize{namedblocks:some-python-code}]'\
'{\\sphinxcrossref{\\DUrole{std,std-ref}{the python code}}}'
assert label1 in latex
assert link1 in latex
assert label2 in latex
assert link2 in latex
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_literalinclude_classes(app, status, warning):
app.build(filenames=[app.srcdir / 'classes.rst'])
et = etree_parse(app.outdir / 'classes.xml')
secs = et.findall('./section/section')
code_block = secs[0].findall('literal_block')
assert len(code_block) > 0
assert code_block[0].get('classes') == 'foo bar'
assert code_block[0].get('names') == 'code_block'
literalinclude = secs[1].findall('literal_block')
assert len(literalinclude) > 0
assert literalinclude[0].get('classes') == 'bar baz'
assert literalinclude[0].get('names') == 'literal_include'
@pytest.mark.sphinx('xml', testroot='directive-code')
def test_literalinclude_pydecorators(app, status, warning):
app.build(filenames=[app.srcdir / 'py-decorators.rst'])
et = etree_parse(app.outdir / 'py-decorators.xml')
secs = et.findall('./section/section')
literal_include = secs[0].findall('literal_block')
assert len(literal_include) == 3
actual = literal_include[0].text
expect = (
'@class_decorator\n'
'@other_decorator()\n'
'class TheClass(object):\n'
'\n'
' @method_decorator\n'
' @other_decorator()\n'
' def the_method():\n'
' pass\n'
)
assert actual == expect
actual = literal_include[1].text
expect = (
' @method_decorator\n'
' @other_decorator()\n'
' def the_method():\n'
' pass\n'
)
assert actual == expect
actual = literal_include[2].text
expect = (
'@function_decorator\n'
'@other_decorator()\n'
'def the_function():\n'
' pass\n'
)
assert actual == expect
@pytest.mark.sphinx('dummy', testroot='directive-code')
def test_code_block_highlighted(app, status, warning):
app.build(filenames=[app.srcdir / 'highlight.rst'])
doctree = app.env.get_doctree('highlight')
codeblocks = list(doctree.findall(nodes.literal_block))
assert codeblocks[0]['language'] == 'default'
assert codeblocks[1]['language'] == 'python2'
assert codeblocks[2]['language'] == 'python3'
assert codeblocks[3]['language'] == 'python2'
@pytest.mark.sphinx('html', testroot='directive-code')
def test_linenothreshold(app, status, warning):
app.build(filenames=[app.srcdir / 'linenothreshold.rst'])
html = (app.outdir / 'linenothreshold.html').read_text(encoding='utf8')
# code-block using linenothreshold
assert ('<span class="linenos">1</span><span class="k">class</span> '
'<span class="nc">Foo</span><span class="p">:</span>' in html)
# code-block not using linenothreshold (no line numbers)
assert '<span></span><span class="c1"># comment</span>' in html
# literal include using linenothreshold
assert ('<span class="linenos"> 1</span><span class="c1">'
'# Literally included file using Python highlighting</span>' in html)
# literal include not using linenothreshold (no line numbers)
assert ('<span></span><span class="c1"># Very small literal include '
'(linenothreshold check)</span>' in html)
@pytest.mark.sphinx('dummy', testroot='directive-code')
def test_code_block_dedent(app, status, warning):
app.build(filenames=[app.srcdir / 'dedent.rst'])
doctree = app.env.get_doctree('dedent')
codeblocks = list(doctree.findall(nodes.literal_block))
# Note: comparison string should not have newlines at the beginning or end
text_0_indent = '''First line
Second line
Third line
Fourth line'''
text_2_indent = ''' First line
Second line
Third line
Fourth line'''
text_4_indent = ''' First line
Second line
Third line
Fourth line'''
assert codeblocks[0].astext() == text_0_indent
assert codeblocks[1].astext() == text_0_indent
assert codeblocks[2].astext() == text_4_indent
assert codeblocks[3].astext() == text_2_indent
assert codeblocks[4].astext() == text_4_indent
assert codeblocks[5].astext() == text_0_indent

View File

@@ -0,0 +1,59 @@
"""Test object description directives."""
import docutils.utils
import pytest
from docutils import nodes
from sphinx import addnodes
from sphinx.io import create_publisher
from sphinx.testing import restructuredtext
from sphinx.util.docutils import sphinx_domains
def _doctree_for_test(builder, docname: str) -> nodes.document:
builder.env.prepare_settings(docname)
publisher = create_publisher(builder.app, 'restructuredtext')
with sphinx_domains(builder.env):
publisher.set_source(source_path=builder.env.doc2path(docname))
publisher.publish()
return publisher.document
@pytest.mark.sphinx('text', testroot='object-description-sections')
def test_object_description_sections(app):
doctree = _doctree_for_test(app.builder, 'index')
# <document>
# <index>
# <desc>
# <desc_signature>
# <desc_name>
# func
# <desc_parameterlist>
# <desc_content>
# <section>
# <title>
# Overview
# <paragraph>
# Lorem ipsum dolar sit amet
assert isinstance(doctree[0], addnodes.index)
assert isinstance(doctree[1], addnodes.desc)
assert isinstance(doctree[1][0], addnodes.desc_signature)
assert isinstance(doctree[1][1], addnodes.desc_content)
assert isinstance(doctree[1][1][0], nodes.section)
assert isinstance(doctree[1][1][0][0], nodes.title)
assert doctree[1][1][0][0][0] == 'Overview'
assert isinstance(doctree[1][1][0][1], nodes.paragraph)
assert doctree[1][1][0][1][0] == 'Lorem ipsum dolar sit amet'
def test_object_description_content_line_number(app):
text = (".. py:function:: foo(bar)\n" +
"\n" +
" Some link here: :ref:`abc`\n")
doc = restructuredtext.parse(app, text)
xrefs = list(doc.findall(condition=addnodes.pending_xref))
assert len(xrefs) == 1
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3

View File

@@ -0,0 +1,46 @@
"""Test the only directive with the test root."""
import re
import pytest
from docutils import nodes
@pytest.mark.sphinx('text', testroot='directive-only')
def test_sectioning(app, status, warning):
def getsects(section):
if not isinstance(section, nodes.section):
return [getsects(n) for n in section.children]
title = section.next_node(nodes.title).astext().strip()
subsects = []
children = section.children[:]
while children:
node = children.pop(0)
if isinstance(node, nodes.section):
subsects.append(node)
continue
children = list(node.children) + children
return [title, [getsects(subsect) for subsect in subsects]]
def testsects(prefix, sects, indent=0):
title = sects[0]
parent_num = title.split()[0]
assert prefix == parent_num, \
'Section out of place: %r' % title
for i, subsect in enumerate(sects[1]):
num = subsect[0].split()[0]
assert re.match('[0-9]+[.0-9]*[.]', num), \
'Unnumbered section: %r' % subsect[0]
testsects(prefix + str(i + 1) + '.', subsect, indent + 4)
app.build(filenames=[app.srcdir / 'only.rst'])
doctree = app.env.get_doctree('only')
app.env.apply_post_transforms(doctree, 'only')
parts = [getsects(n)
for n in [_n for _n in doctree.children if isinstance(_n, nodes.section)]]
for i, s in enumerate(parts):
testsects(str(i + 1) + '.', s, 4)
assert len(parts) == 4, 'Expected 4 document level headings, got:\n%s' % \
'\n'.join(p[0] for p in parts)

View File

@@ -0,0 +1,195 @@
"""Test the other directives."""
from pathlib import Path
import pytest
from docutils import nodes
from sphinx import addnodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree(app):
text = (".. toctree::\n"
"\n"
" foo\n"
" bar/index\n"
" baz\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'baz')],
includefiles=['foo', 'bar/index', 'baz'])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_relative_toctree(app):
text = (".. toctree::\n"
"\n"
" bar_1\n"
" bar_2\n"
" bar_3\n"
" ../quux\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'bar/index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'bar/bar_1'), (None, 'bar/bar_2'), (None, 'bar/bar_3'),
(None, 'quux')],
includefiles=['bar/bar_1', 'bar/bar_2', 'bar/bar_3', 'quux'])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree_urls_and_titles(app):
text = (".. toctree::\n"
"\n"
" Sphinx <https://www.sphinx-doc.org/>\n"
" https://readthedocs.org/\n"
" The BAR <bar/index>\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[('Sphinx', 'https://www.sphinx-doc.org/'),
(None, 'https://readthedocs.org/'),
('The BAR', 'bar/index')],
includefiles=['bar/index'])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree_glob(app):
text = (".. toctree::\n"
" :glob:\n"
"\n"
" *\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux')],
includefiles=['baz', 'foo', 'quux'])
# give both docname and glob (case1)
text = (".. toctree::\n"
" :glob:\n"
"\n"
" foo\n"
" *\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'baz'), (None, 'quux')],
includefiles=['foo', 'baz', 'quux'])
# give both docname and glob (case2)
text = (".. toctree::\n"
" :glob:\n"
"\n"
" *\n"
" foo\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux'), (None, 'foo')],
includefiles=['baz', 'foo', 'quux', 'foo'])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree_glob_and_url(app):
text = (".. toctree::\n"
" :glob:\n"
"\n"
" https://example.com/?q=sphinx\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'https://example.com/?q=sphinx')],
includefiles=[])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_reversed_toctree(app):
text = (".. toctree::\n"
" :reversed:\n"
"\n"
" foo\n"
" bar/index\n"
" baz\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'baz'), (None, 'bar/index'), (None, 'foo')],
includefiles=['baz', 'bar/index', 'foo'])
@pytest.mark.sphinx(testroot='toctree-glob')
def test_toctree_twice(app):
text = (".. toctree::\n"
"\n"
" foo\n"
" foo\n")
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
assert_node(doctree[0][0],
entries=[(None, 'foo'), (None, 'foo')],
includefiles=['foo', 'foo'])
@pytest.mark.sphinx(testroot='directive-include')
def test_include_include_read_event(app):
sources_reported = []
def source_read_handler(_app, relative_path, parent_docname, source):
sources_reported.append((relative_path, parent_docname, source[0]))
app.connect("include-read", source_read_handler)
text = """\
.. include:: baz/baz.rst
:start-line: 4
.. include:: text.txt
:literal:
.. include:: bar.txt
"""
app.env.find_files(app.config, app.builder)
restructuredtext.parse(app, text, 'index')
included_files = {filename.as_posix()
for filename, p, s in sources_reported}
assert 'index.rst' not in included_files # sources don't emit 'include-read'
assert 'baz/baz.rst' in included_files
assert 'text.txt' not in included_files # text was included as literal, no rst parsing
assert 'bar.txt' in included_files # suffix not in source-suffixes
assert (Path('baz/baz.rst'), 'index', '\nBaz was here.') in sources_reported
@pytest.mark.sphinx(testroot='directive-include')
def test_include_include_read_event_nested_includes(app):
def source_read_handler(_app, _relative_path, _parent_docname, source):
text = source[0].replace("#magical", "amazing")
source[0] = text
app.connect("include-read", source_read_handler)
text = ".. include:: baz/baz.rst\n"
app.env.find_files(app.config, app.builder)
doctree = restructuredtext.parse(app, text, 'index')
assert_node(doctree, addnodes.document)
assert len(doctree.children) == 3
assert_node(doctree.children[1], nodes.paragraph)
assert doctree.children[1].rawsource == "The amazing foo."

View File

@@ -0,0 +1,110 @@
"""Test the patched directives."""
import pytest
from docutils import nodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
def test_code_directive(app):
# normal case
text = ('.. code::\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="default", highlight_args={})
# with language
text = ('.. code:: python\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", highlight_args={})
# :number-lines: option
text = ('.. code:: python\n'
' :number-lines:\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", linenos=True, highlight_args={})
# :number-lines: option
text = ('.. code:: python\n'
' :number-lines: 5\n'
'\n'
' print("hello world")\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.literal_block, 'print("hello world")'])
assert_node(doctree[0], language="python", linenos=True, highlight_args={'linenostart': 5})
@pytest.mark.sphinx(testroot='directive-csv-table')
def test_csv_table_directive(app):
# relative path from current document
text = ('.. csv-table::\n'
' :file: example.csv\n')
doctree = restructuredtext.parse(app, text, docname="subdir/index")
assert_node(doctree,
([nodes.table, nodes.tgroup, (nodes.colspec,
nodes.colspec,
nodes.colspec,
[nodes.tbody, nodes.row])],))
assert_node(doctree[0][0][3][0],
([nodes.entry, nodes.paragraph, "FOO"],
[nodes.entry, nodes.paragraph, "BAR"],
[nodes.entry, nodes.paragraph, "BAZ"]))
# absolute path from source directory
text = ('.. csv-table::\n'
' :file: /example.csv\n')
doctree = restructuredtext.parse(app, text, docname="subdir/index")
assert_node(doctree,
([nodes.table, nodes.tgroup, (nodes.colspec,
nodes.colspec,
nodes.colspec,
[nodes.tbody, nodes.row])],))
assert_node(doctree[0][0][3][0],
([nodes.entry, nodes.paragraph, "foo"],
[nodes.entry, nodes.paragraph, "bar"],
[nodes.entry, nodes.paragraph, "baz"]))
def test_math_directive(app):
# normal case
text = '.. math:: E = mc^2'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, nodes.math_block, 'E = mc^2\n\n'])
# :name: option
text = ('.. math:: E = mc^2\n'
' :name: eq1\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, (nodes.target,
[nodes.math_block, "E = mc^2\n\n"])])
assert_node(doctree[1], nodes.math_block, docname='index', label="eq1", number=1)
# :label: option
text = ('.. math:: E = mc^2\n'
' :label: eq2\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, (nodes.target,
[nodes.math_block, 'E = mc^2\n\n'])])
assert_node(doctree[1], nodes.math_block, docname='index', label="eq2", number=2)
# :label: option without value
text = ('.. math:: E = mc^2\n'
' :label:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, [nodes.document, (nodes.target,
[nodes.math_block, 'E = mc^2\n\n'])])
assert_node(doctree[1], nodes.math_block, ids=['equation-index-0'],
docname='index', label="index:0", number=3)

View File

@@ -0,0 +1,108 @@
"""Tests the directives"""
import pytest
from docutils import nodes
from sphinx import addnodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
DOMAINS = [
# directive, no-index, no-index-entry, signature of f, signature of g, index entry of g
('c:function', False, True, 'void f()', 'void g()', ('single', 'g (C function)', 'c.g', '', None)),
('cpp:function', False, True, 'void f()', 'void g()', ('single', 'g (C++ function)', '_CPPv41gv', '', None)),
('js:function', True, True, 'f()', 'g()', ('single', 'g() (built-in function)', 'g', '', None)),
('py:function', True, True, 'f()', 'g()', ('pair', 'built-in function; g()', 'g', '', None)),
('rst:directive', True, False, 'f', 'g', ('single', 'g (directive)', 'directive-g', '', None)),
('cmdoption', True, False, 'f', 'g', ('pair', 'command line option; g', 'cmdoption-arg-g', '', None)),
('envvar', True, False, 'f', 'g', ('single', 'environment variable; g', 'envvar-g', '', None)),
]
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, nodes.target))
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_twice(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the target nodes
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_noindex_orig(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
if not no_index:
pytest.skip(f'{directive} does not support :no-index: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :no-index:\n'
f'.. {directive}:: {sig_g}\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.desc, addnodes.index, addnodes.desc))
assert_node(doctree[2], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_noindex(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
if not no_index:
pytest.skip(f'{directive} does not support :no-index: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :no-index:\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target))
assert_node(doctree[0], addnodes.index, entries=[])
assert_node(doctree[1], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_no_index_entry(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
if not no_index_entry:
pytest.skip(f'{directive} does not support :no-index-entry: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :no-index-entry:\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
assert_node(doctree[0], addnodes.index, entries=[])
assert_node(doctree[1], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_code(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n'
f'.. code::\n'
f'\n'
f' code\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the targets
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target, nodes.literal_block))
@pytest.mark.parametrize(('directive', 'no_index', 'no_index_entry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_heading(app, directive, no_index, no_index_entry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n'
f'\n'
f'Heading\n'
f'=======\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the targets and the heading is floated before those.
assert_node(doctree, (nodes.title, addnodes.index, addnodes.index, nodes.target, nodes.target))