#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from unittest import TestCase, skip
from docutils.core import Publisher
from docutils import io
from m2r import prolog, convert
class RendererTestBase(TestCase):
def conv(self, src, **kwargs):
out = convert(src, **kwargs)
self.check_rst(out)
return out
def conv_no_check(self, src, **kwargs):
out = convert(src, **kwargs)
return out
def check_rst(self, rst):
pub = Publisher(reader=None, parser=None, writer=None, settings=None,
source_class=io.StringInput,
destination_class=io.StringOutput)
pub.set_components(reader_name='standalone',
parser_name='restructuredtext',
writer_name='pseudoxml')
pub.process_programmatic_settings(
settings_spec=None,
settings_overrides={'output_encoding': 'unicode'},
config_section=None,
)
pub.set_source(rst, source_path=None)
pub.set_destination(destination=None, destination_path=None)
output = pub.publish(enable_exit_status=False)
self.assertLess(pub.document.reporter.max_level, 0)
return output, pub
class TestBasic(RendererTestBase):
def test_fail_rst(self):
with self.assertRaises(AssertionError):
# This check should be failed and report warning
self.check_rst('```')
def test_simple_paragraph(self):
src = 'this is a sentence.\n'
out = self.conv(src)
self.assertEqual(out, '\n' + src)
def test_multiline_paragraph(self):
src = '\n'.join([
'first sentence.',
'second sentence.',
])
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_multi_paragraph(self):
src = '\n'.join([
'first paragraph.',
'',
'second paragraph.',
])
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_hr(self):
src = 'a\n\n---\n\nb'
out = self.conv(src)
self.assertEqual(out, '\na\n\n----\n\nb\n')
def test_linebreak(self):
src = 'abc def \nghi'
out = self.conv(src)
self.assertEqual(
out,
prolog + '\nabc def\\ :raw-html-m2r:`
`\nghi' + '\n',
)
class TestInlineMarkdown(RendererTestBase):
def test_inline_code(self):
src = '`a`'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '``a``')
def test_inline_code_with_backticks(self):
src = '```a``a```'
out = self.conv(src)
self.assertEqual(out.strip(),
'.. role:: raw-html-m2r(raw)\n'
' :format: html\n\n\n'
':raw-html-m2r:`'
'a``a
`'
)
def test_strikethrough(self):
src = ('~~a~~')
self.conv(src)
def test_emphasis(self):
src = '*a*'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '*a*')
def test_emphasis_(self):
src = '_a_'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '*a*')
def test_emphasis_no_(self):
src = '_a_'
out = self.conv(src, no_underscore_emphasis=True)
self.assertEqual(out.replace('\n', ''), '_a_')
def test_double_emphasis(self):
src = '**a**'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '**a**')
def test_double_emphasis__(self):
src = '__a__'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '**a**')
def test_emphasis_no__(self):
src = '__a__'
out = self.conv(src, no_underscore_emphasis=True)
self.assertEqual(out.replace('\n', ''), '__a__')
def test_autolink(self):
src = 'link to http://example.com/ in sentence.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_link(self):
src = 'this is a [link](http://example.com/).'
out = self.conv(src)
self.assertEqual(
out, '\nthis is a `link `_.\n')
def test_anonymous_link(self):
src = 'this is a [link](http://example.com/).'
out = self.conv(src, anonymous_references=True)
self.assertEqual(
out, '\nthis is a `link `__.\n')
def test_link_with_rel_link_enabled(self):
src = 'this is a [link](http://example.com/).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a `link `_.\n')
def test_anonymous_link_with_rel_link_enabled(self):
src = 'this is a [link](http://example.com/).'
out = self.conv_no_check(
src,
parse_relative_links=True,
anonymous_references=True
)
self.assertEqual(
out, '\nthis is a `link `__.\n')
def test_anchor(self):
src = 'this is an [anchor](#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is an :ref:`anchor `.\n')
def test_relative_link(self):
src = 'this is a [relative link](a_file.md).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link `.\n')
def test_relative_link_with_anchor(self):
src = 'this is a [relative link](a_file.md#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link `.\n')
def test_link_title(self):
src = 'this is a [link](http://example.com/ "example").'
out = self.conv(src)
self.assertEqual(
out,
'.. role:: raw-html-m2r(raw)\n'
' :format: html\n\n\n'
'this is a :raw-html-m2r:'
'`link`.\n'
)
def test_image_link(self):
src = '[](link_target_url)'
out = self.conv(src)
self.assertEqual(
out,
'\n\n.. image:: image_taget_url\n'
' :target: link_target_url\n :alt: Alt Text\n\n',
)
def test_rest_role(self):
src = 'a :code:`some code` inline.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_role2(self):
src = 'a `some code`:code: inline.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link(self):
src = 'a `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link_and_role(self):
src = 'a :code:`a` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link_and_role2(self):
src = 'a `a`:code: and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_role_incomplete(self):
src = 'a co:`de` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(
out,
'\na co:\\ ``de`` and `RefLink `_ here.\n',
)
def test_rest_role_incomplete2(self):
src = 'a `RefLink `_ and co:`de` here.'
out = self.conv(src)
self.assertEqual(
out,
'\na `RefLink `_ and co:\\ ``de`` here.\n',
)
def test_rest_role_with_code(self):
src = 'a `code` and :code:`rest` here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and :code:`rest` here.\n')
def test_rest2_role_with_code(self):
src = 'a `code` and `rest`:code: here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and `rest`:code: here.\n')
def test_code_with_rest_role(self):
src = 'a :code:`rest` and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na :code:`rest` and ``code`` here.\n')
def test_code_with_rest_role2(self):
src = 'a `rest`:code: and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na `rest`:code: and ``code`` here.\n')
def test_rest_link_with_code(self):
src = 'a `RefLink `_ and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na `RefLink `_ and ``code`` here.\n')
def test_code_with_rest_link(self):
src = 'a `code` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and `RefLink `_ here.\n')
def test_inline_math(self):
src = 'this is `$E = mc^2$` inline math.'
out = self.conv(src)
self.assertEqual(out, '\nthis is :math:`E = mc^2` inline math.\n')
def test_disable_inline_math(self):
src = 'this is `$E = mc^2$` inline math.'
out = self.conv(src, disable_inline_math=True)
self.assertEqual(out, '\nthis is ``$E = mc^2$`` inline math.\n')
def test_inline_html(self):
src = 'this is html.'
out = self.conv(src)
self.assertEqual(
out, prolog + '\nthis is :raw-html-m2r:`html`.\n')
def test_block_html(self):
src = 'title
'
out = self.conv(src)
self.assertEqual(out, '\n\n.. raw:: html\n\n title
\n\n')
class TestBlockQuote(RendererTestBase):
def test_block_quote(self):
src = '> q1\n> q2'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n q1\n q2\n\n')
def test_block_quote_nested(self):
src = '> q1\n> > q2'
out = self.conv(src)
# one extra empty line is inserted, but still valid rst anyway
self.assertEqual(out, '\n..\n\n q1\n\n ..\n\n q2\n\n')
@skip('markdown does not support dedent in block quote')
def test_block_quote_nested_2(self):
src = '> q1\n> > q2\n> q3'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n q1\n\n ..\n q2\n\n q3\n\n')
class TestCodeBlock(RendererTestBase):
def test_plain_code_block(self):
src = '\n'.join([
'```',
'pip install sphinx',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block::\n\n pip install sphinx\n')
def test_plain_code_block_tilda(self):
src = '\n'.join([
'~~~',
'pip install sphinx',
'~~~',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block::\n\n pip install sphinx\n')
def test_code_block_math(self):
src = '\n'.join([
'```math',
'E = mc^2',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. math::\n\n E = mc^2\n')
def test_plain_code_block_indent(self):
src = '\n'.join([
'```',
'pip install sphinx',
' new line',
'```',
])
out = self.conv(src)
self.assertEqual(
out,
'\n.. code-block::\n\n pip install sphinx\n new line\n',
)
def test_python_code_block(self):
src = '\n'.join([
'```python',
'print(1)',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block:: python\n\n print(1)\n')
def test_python_code_block_indent(self):
src = '\n'.join([
'```python',
'def a(i):',
' print(i)',
'```',
])
out = self.conv(src)
self.assertEqual(
out,
'\n.. code-block:: python\n\n def a(i):\n print(i)\n',
)
class TestImage(RendererTestBase):
def test_image(self):
src = ''
out = self.conv(src)
# first and last newline is inserted by paragraph
self.assertEqual(
out,
'\n\n.. image:: a.png\n :target: a.png\n :alt: alt text\n\n',
)
def test_image_title(self):
src = ''
self.conv(src)
# title is not supported now
class TestHeading(RendererTestBase):
def test_heading(self):
src = '# head 1'
out = self.conv(src)
self.assertEqual(out, '\nhead 1\n' + '=' * 6 + '\n')
def test_heading_multibyte(self):
src = '# マルチバイト文字\n'
out = self.conv(src)
self.assertEqual(out, '\nマルチバイト文字\n' + '=' * 16 + '\n')
class TestList(RendererTestBase):
def test_ul(self):
src = '* list'
out = self.conv(src)
self.assertEqual(out, '\n\n* list\n')
def test_ol(self):
src = '1. list'
out = self.conv(src)
self.assertEqual(out, '\n\n#. list\n')
def test_nested_ul(self):
src = '\n'.join([
'* list 1',
'* list 2',
' * list 2.1',
' * list 2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n* list 1\n'
'* list 2\n\n'
' * list 2.1\n'
' * list 2.2\n\n'
'* list 3\n',
)
def test_nested_ul_2(self):
src = '\n'.join([
'* list 1',
'* list 2',
' * list 2.1',
' * list 2.2',
' * list 2.2.1',
' * list 2.2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n* list 1\n'
'* list 2\n\n'
' * list 2.1\n'
' * list 2.2\n\n'
' * list 2.2.1\n'
' * list 2.2.2\n\n'
'* list 3\n'
)
def test_nested_ol(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' 2. list 2.1',
' 3. list 2.2',
'3. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n#. list 1\n'
'#. list 2\n'
'\n'
' #. list 2.1\n'
' #. list 2.2\n'
'\n'
'#. list 3\n',
)
def test_nested_ol_2(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' 3. list 2.1',
' 4. list 2.2',
' 5. list 2.2.1',
' 6. list 2.2.2',
'7. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
'#. list 2',
'',
' #. list 2.1',
' #. list 2.2',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_mixed_1(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' * list 2.1',
' * list 2.2',
' 1. list 2.2.1',
' 2. list 2.2.2',
'7. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
'#. list 2',
'',
' * list 2.1',
' * list 2.2',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_multiline_1(self):
src = '\n'.join([
'* list 1',
' list 1 cont',
'* list 2',
' list 2 cont',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
' * list 2.2.1',
' * list 2.2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n* list 1',
' list 1 cont',
'* list 2',
' list 2 cont',
'',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
'',
' * list 2.2.1',
' * list 2.2.2',
'',
'* list 3\n',
])
)
def test_nested_multiline_2(self):
src = '\n'.join([
'1. list 1',
' list 1 cont',
'1. list 2',
' list 2 cont',
' 1. list 2.1',
' list 2.1 cont',
' 1. list 2.2',
' list 2.2 cont',
' 1. list 2.2.1',
' 1. list 2.2.2',
'1. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
' list 1 cont',
'#. list 2',
' list 2 cont',
'',
' #. list 2.1',
' list 2.1 cont',
' #. list 2.2',
' list 2.2 cont',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_multiline_3(self):
src = '\n'.join([
'1. list 1',
' list 1 cont',
'1. list 2',
' list 2 cont',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
' 1. list 2.2.1',
' 1. list 2.2.2',
'1. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
' list 1 cont',
'#. list 2',
' list 2 cont',
'',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
class TestConplexText(RendererTestBase):
def test_code(self):
src = '''
some sentence
```python
print(1)
```
some sentence
# title
```python
print(1)
```
---
end
'''
self.conv(src)
class TestTable(RendererTestBase):
def test_table(self):
src = '''h1 | h2 | h3\n--- | --- | ---\n1 | 2 | 3\n4 | 5 | 6'''
out = self.conv(src)
self.assertEqual(out, '\n'.join([
'',
'.. list-table::',
' :header-rows: 1',
'',
' * - h1',
' - h2',
' - h3',
' * - 1',
' - 2',
' - 3',
' * - 4',
' - 5',
' - 6',
'',
'',
]))
class TestFootNote(RendererTestBase):
def test_footnote(self):
src = '\n'.join([
'This is a[^1] footnote[^2] ref[^ref] with rst [#a]_.',
'',
'[^1]: note 1',
'[^2]: note 2',
'[^ref]: note ref',
'.. [#a] note rst',
])
out = self.conv(src)
self.assertEqual(out, '\n'.join([
'',
'This is a\\ [#fn-1]_ '
'footnote\\ [#fn-2]_ ref\\ [#fn-ref]_ with rst [#a]_.',
'',
'.. [#a] note rst', # one empty line inserted...
'',
'.. [#fn-1] note 1',
'.. [#fn-2] note 2',
'.. [#fn-ref] note ref',
'',
]))
def test_sphinx_ref(self):
src = 'This is a sphinx [ref]_ global ref.\n\n.. [ref] ref text'
out = self.conv(src)
self.assertEqual(out, '\n' + src)
class TestDirective(RendererTestBase):
def test_comment_oneline(self):
src = '.. a'
out = self.conv(src)
self.assertEqual(out, '\n.. a')
def test_comment_indented(self):
src = ' .. a'
out = self.conv(src)
self.assertEqual(out, '\n .. a')
def test_comment_newline(self):
src = '..\n\n comment\n\nnewline'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n comment\n\nnewline\n')
def test_comment_multiline(self):
comment = (
'.. this is comment.\n'
' this is also comment.\n'
'\n'
'\n'
' comment may include empty line.\n'
'\n\n')
src = comment + '`eoc`'
out = self.conv(src)
self.assertEqual(out, '\n' + comment + '``eoc``\n')
class TestRestCode(RendererTestBase):
def test_rest_code_block_empty(self):
src = '\n\n::\n\n'
out = self.conv(src)
self.assertEqual(out, '\n\n')
def test_eol_marker(self):
src = 'a::\n\n code\n'
out = self.conv(src)
self.assertEqual(out, '\na:\n\n.. code-block::\n\n code\n')
def test_eol_marker_remove(self):
src = 'a ::\n\n code\n'
out = self.conv(src)
self.assertEqual(out, '\na\n\n.. code-block::\n\n code\n')