1"""
2    test_application
3    ~~~~~~~~~~~~~~~~
4
5    Test the Sphinx class.
6
7    :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
8    :license: BSD, see LICENSE for details.
9"""
10
11from unittest.mock import Mock
12
13import pytest
14from docutils import nodes
15
16from sphinx.errors import ExtensionError
17from sphinx.testing.util import strip_escseq
18from sphinx.util import logging
19
20
21def test_events(app, status, warning):
22    def empty():
23        pass
24    with pytest.raises(ExtensionError) as excinfo:
25        app.connect("invalid", empty)
26    assert "Unknown event name: invalid" in str(excinfo.value)
27
28    app.add_event("my_event")
29    with pytest.raises(ExtensionError) as excinfo:
30        app.add_event("my_event")
31    assert "Event 'my_event' already present" in str(excinfo.value)
32
33    def mock_callback(a_app, *args):
34        assert a_app is app
35        assert emit_args == args
36        return "ret"
37    emit_args = (1, 3, "string")
38    listener_id = app.connect("my_event", mock_callback)
39    assert app.emit("my_event", *emit_args) == ["ret"], "Callback not called"
40
41    app.disconnect(listener_id)
42    assert app.emit("my_event", *emit_args) == [], \
43        "Callback called when disconnected"
44
45
46def test_emit_with_nonascii_name_node(app, status, warning):
47    node = nodes.section(names=['\u65e5\u672c\u8a9e'])
48    app.emit('my_event', node)
49
50
51def test_extensions(app, status, warning):
52    app.setup_extension('shutil')
53    warning = strip_escseq(warning.getvalue())
54    assert "extension 'shutil' has no setup() function" in warning
55
56
57def test_extension_in_blacklist(app, status, warning):
58    app.setup_extension('sphinxjp.themecore')
59    msg = strip_escseq(warning.getvalue())
60    assert msg.startswith("WARNING: the extension 'sphinxjp.themecore' was")
61
62
63@pytest.mark.sphinx(testroot='add_source_parser')
64def test_add_source_parser(app, status, warning):
65    assert set(app.config.source_suffix) == {'.rst', '.test'}
66
67    # .rst; only in :confval:`source_suffix`
68    assert '.rst' not in app.registry.get_source_parsers()
69    assert app.registry.source_suffix['.rst'] is None
70
71    # .test; configured by API
72    assert app.registry.source_suffix['.test'] == 'test'
73    assert 'test' in app.registry.get_source_parsers()
74    assert app.registry.get_source_parsers()['test'].__name__ == 'TestSourceParser'
75
76
77@pytest.mark.sphinx(testroot='extensions')
78def test_add_is_parallel_allowed(app, status, warning):
79    logging.setup(app, status, warning)
80
81    assert app.is_parallel_allowed('read') is True
82    assert app.is_parallel_allowed('write') is True
83    assert warning.getvalue() == ''
84
85    app.setup_extension('read_parallel')
86    assert app.is_parallel_allowed('read') is True
87    assert app.is_parallel_allowed('write') is True
88    assert warning.getvalue() == ''
89    app.extensions.pop('read_parallel')
90
91    app.setup_extension('write_parallel')
92    assert app.is_parallel_allowed('read') is False
93    assert app.is_parallel_allowed('write') is True
94    assert ("the write_parallel extension does not declare if it is safe "
95            "for parallel reading, assuming it isn't - please ") in warning.getvalue()
96    app.extensions.pop('write_parallel')
97    warning.truncate(0)  # reset warnings
98
99    app.setup_extension('read_serial')
100    assert app.is_parallel_allowed('read') is False
101    assert "the read_serial extension is not safe for parallel reading" in warning.getvalue()
102    warning.truncate(0)  # reset warnings
103    assert app.is_parallel_allowed('write') is True
104    assert warning.getvalue() == ''
105    app.extensions.pop('read_serial')
106
107    app.setup_extension('write_serial')
108    assert app.is_parallel_allowed('read') is False
109    assert app.is_parallel_allowed('write') is False
110    assert ("the write_serial extension does not declare if it is safe "
111            "for parallel reading, assuming it isn't - please ") in warning.getvalue()
112    app.extensions.pop('write_serial')
113    warning.truncate(0)  # reset warnings
114
115
116@pytest.mark.sphinx('dummy', testroot='root')
117def test_build_specific(app):
118    app.builder.build = Mock()
119    filenames = [app.srcdir / 'index.txt',                      # normal
120                 app.srcdir / 'images',                         # without suffix
121                 app.srcdir / 'notfound.txt',                   # not found
122                 app.srcdir / 'img.png',                        # unknown suffix
123                 '/index.txt',                                  # external file
124                 app.srcdir / 'subdir',                         # directory
125                 app.srcdir / 'subdir/includes.txt',            # file on subdir
126                 app.srcdir / 'subdir/../subdir/excluded.txt']  # not normalized
127    app.build(False, filenames)
128
129    expected = ['index', 'img.png', 'subdir/includes', 'subdir/excluded']
130    app.builder.build.assert_called_with(expected,
131                                         method='specific',
132                                         summary='4 source files given on command line')
133