sphinx/tests/test_ext_inheritance_diagram.py

264 lines
10 KiB
Python
Raw Normal View History

2016-02-14 00:06:28 -06:00
"""
test_ext_inheritance_diagram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test sphinx.ext.inheritance_diagram extension.
2019-12-31 23:27:43 -06:00
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
2016-02-14 00:06:28 -06:00
:license: BSD, see LICENSE for details.
"""
2016-11-22 22:38:20 -06:00
import re
import os
import sys
2017-01-03 07:24:00 -06:00
import pytest
2016-02-14 00:06:28 -06:00
from sphinx.ext.inheritance_diagram import (
InheritanceDiagram, InheritanceException, import_classes
)
@pytest.mark.sphinx(buildername="html", testroot="inheritance")
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram(app, status, warning):
# monkey-patch InheritaceDiagram.run() so we can get access to its
# results.
orig_run = InheritanceDiagram.run
graphs = {}
def new_run(self):
result = orig_run(self)
node = result[0]
source = os.path.basename(node.document.current_source).replace(".rst", "")
graphs[source] = node['graph']
return result
InheritanceDiagram.run = new_run
try:
app.builder.build_all()
finally:
InheritanceDiagram.run = orig_run
assert app.statuscode == 0
html_warnings = warning.getvalue()
assert html_warnings == ""
# note: it is better to split these asserts into separate test functions
# but I can't figure out how to build only a specific .rst file
# basic inheritance diagram showing all classes
for cls in graphs['basic_diagram'].class_info:
# use in b/c traversing order is different sometimes
assert cls in [
('dummy.test.A', 'dummy.test.A', [], None),
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
('dummy.test.B', 'dummy.test.B', ['dummy.test.A'], None)
]
# inheritance diagram using :parts: 1 option
for cls in graphs['diagram_w_parts'].class_info:
assert cls in [
('A', 'dummy.test.A', [], None),
('F', 'dummy.test.F', ['C'], None),
('C', 'dummy.test.C', ['A'], None),
('E', 'dummy.test.E', ['B'], None),
('D', 'dummy.test.D', ['B', 'C'], None),
('B', 'dummy.test.B', ['A'], None)
]
# inheritance diagram with 1 top class
# :top-classes: dummy.test.B
# rendering should be
# A
# \
# B C
# / \ / \
# E D F
#
for cls in graphs['diagram_w_1_top_class'].class_info:
assert cls in [
('dummy.test.A', 'dummy.test.A', [], None),
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
('dummy.test.C', 'dummy.test.C', ['dummy.test.A'], None),
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
('dummy.test.B', 'dummy.test.B', [], None)
]
# inheritance diagram with 2 top classes
# :top-classes: dummy.test.B, dummy.test.C
# Note: we're specifying separate classes, not the entire module here
# rendering should be
#
# B C
# / \ / \
# E D F
#
for cls in graphs['diagram_w_2_top_classes'].class_info:
assert cls in [
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
('dummy.test.C', 'dummy.test.C', [], None),
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
('dummy.test.B', 'dummy.test.B', [], None)
]
# inheritance diagram with 2 top classes and specifiying the entire module
# rendering should be
#
# A
# B C
# / \ / \
# E D F
#
# Note: dummy.test.A is included in the graph before its descendants are even processed
# b/c we've specified to load the entire module. The way InheritanceGraph works it is very
# hard to exclude parent classes once after they have been included in the graph.
# If you'd like to not show class A in the graph don't specify the entire module.
# this is a known issue.
for cls in graphs['diagram_module_w_2_top_classes'].class_info:
assert cls in [
('dummy.test.F', 'dummy.test.F', ['dummy.test.C'], None),
('dummy.test.C', 'dummy.test.C', [], None),
('dummy.test.E', 'dummy.test.E', ['dummy.test.B'], None),
('dummy.test.D', 'dummy.test.D', ['dummy.test.B', 'dummy.test.C'], None),
('dummy.test.B', 'dummy.test.B', [], None),
('dummy.test.A', 'dummy.test.A', [], None),
]
2016-02-14 00:06:28 -06:00
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram')
2017-01-03 07:24:00 -06:00
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_png_html(app, status, warning):
2016-02-14 00:06:28 -06:00
app.builder.build_all()
content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure align-default" id="id1">\n'
'<div class="graphviz">'
2017-02-12 11:02:51 -06:00
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
'class="inheritance graphviz" /></div>\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)
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram',
confoverrides={'graphviz_output_format': 'svg'})
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_svg_html(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure align-default" id="id1">\n'
'<div class="graphviz">'
'<object data="_images/inheritance-\\w+.svg" '
'type="image/svg\\+xml" class="inheritance graphviz">\n'
'<p class=\"warning\">Inheritance diagram of test.Foo</p>'
'</object></div>\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)
@pytest.mark.sphinx('latex', testroot='ext-inheritance_diagram')
2017-01-03 07:24:00 -06:00
@pytest.mark.usefixtures('if_graphviz_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'
'\\\\sphinxincludegraphics\\[\\]{inheritance-\\w+.pdf}\n'
'\\\\caption{Test Foo!}\\\\label{\\\\detokenize{index:id1}}\\\\end{figure}')
assert re.search(pattern, content, re.M)
2016-11-22 22:22:38 -06:00
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram',
srcdir='ext-inheritance_diagram-alias')
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_latex_alias(app, status, warning):
app.config.inheritance_alias = {'test.Foo': 'alias.Foo'}
app.builder.build_all()
doc = app.env.get_and_resolve_doctree('index', app)
aliased_graph = doc.children[0].children[3]['graph'].class_info
assert len(aliased_graph) == 3
assert ('test.Baz', 'test.Baz', ['test.Bar'], None) in aliased_graph
assert ('test.Bar', 'test.Bar', ['alias.Foo'], None) in aliased_graph
assert ('alias.Foo', 'alias.Foo', [], None) in aliased_graph
content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure align-default" id="id1">\n'
'<div class="graphviz">'
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
'class="inheritance graphviz" /></div>\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)
def test_import_classes(rootdir):
from sphinx.parsers import Parser, RSTParser
from sphinx.util.i18n import CatalogInfo
try:
sys.path.append(rootdir / 'test-ext-inheritance_diagram')
from example.sphinx import DummyClass
# got exception for unknown class or module
with pytest.raises(InheritanceException):
import_classes('unknown', None)
with pytest.raises(InheritanceException):
import_classes('unknown.Unknown', None)
2018-02-01 13:29:18 -06:00
# got exception InheritanceException for wrong class or module
# not AttributeError (refs: #4019)
with pytest.raises(InheritanceException):
import_classes('unknown', '.')
with pytest.raises(InheritanceException):
import_classes('unknown.Unknown', '.')
with pytest.raises(InheritanceException):
import_classes('.', None)
# a module having no classes
classes = import_classes('sphinx', None)
assert classes == []
classes = import_classes('sphinx', 'foo')
assert classes == []
# all of classes in the module
classes = import_classes('sphinx.parsers', None)
assert set(classes) == {Parser, RSTParser}
# specified class in the module
classes = import_classes('sphinx.parsers.Parser', None)
assert classes == [Parser]
# specified class in current module
classes = import_classes('Parser', 'sphinx.parsers')
assert classes == [Parser]
# relative module name to current module
classes = import_classes('i18n.CatalogInfo', 'sphinx.util')
assert classes == [CatalogInfo]
# got exception for functions
with pytest.raises(InheritanceException):
import_classes('encode_uri', 'sphinx.util')
# import submodule on current module (refs: #3164)
classes = import_classes('sphinx', 'example')
assert classes == [DummyClass]
finally:
sys.path.pop()