OILS / doctools / cmark_test.py View on Github | oils.pub

296 lines, 133 significant
1#!/usr/bin/env python2
2"""cmark_test.py: Tests for cmark.py."""
3from __future__ import print_function
4
5import cStringIO
6import os
7import sys
8import unittest
9from pprint import pprint
10
11import cmark # module under test
12
13# No TOC!
14SIMPLE_DOC = cStringIO.StringIO("""
15hi
16""")
17
18TOC_DOC = cStringIO.StringIO("""
19Title
20-----
21
22<div id="toc">
23</div>
24
25### Intro
26
27This is an h3
28in the intro.
29
30### Part One: <code>bash</code>
31
32Another h3.
33
34#### Detail 1 with <a href="foo.html?a=1&b=2">link</a>
35
36An h4.
37
38<h4 id="detail2">Detail 2</h4>
39
40Another h4.
41
42### Conclusion
43
44Concluding h3.
45
46<!-- The blank lines here show a problem that is papered over by fill-blank-lines
47 in Snip -->
48<div class="highlight"><pre><span></span>
49def f():
50 if 0:
51 return False
52
53 if 0:
54 return True
55</pre></div>
56""")
57
58NEW_DOC = """
59Title
60=====
61
62<div id="toc">
63</div>
64
65## One
66
67hello h2.
68
69### subheading `backticks`
70
71hello H3.
72
73#### subsubheading
74
75This kind of heading gets an h4. It's not in the TOC, but it can be linked to.
76
77## Two &amp; Three
78
79"""
80
81DOC_WITH_METADATA = cStringIO.StringIO("""
82- repo-url: doc/README.md
83
84Title
85=====
86
87## One
88""")
89
90_HTML_1 = '''
91<p>dummy
92</p>
93
94<div id="toc">
95</div>
96
97<h2>One <a href="/">link</a></h2>
98
99hello one.
100
101<h3>subheading <code>backticks</code></h3>
102
103<h3>one &amp; two</h3>
104
105<h2 id="explicit">Two</h2>
106
107'''
108
109
110class RenderTest(unittest.TestCase):
111
112 def testRender(self):
113 # type: () -> None
114 opts, _ = cmark.Options().parse_args([])
115
116 out_file = cStringIO.StringIO()
117 cmark.Render(opts, {}, SIMPLE_DOC, out_file)
118 self.assertEqual('<p>hi</p>\n', out_file.getvalue())
119
120 out_file = cStringIO.StringIO()
121 cmark.Render(opts, {}, TOC_DOC, out_file)
122 print(out_file.getvalue())
123
124 def testNewRender(self):
125 # type: () -> None
126 # New style of doc
127
128 new_flags = ['--toc-tag', 'h2', '--toc-tag', 'h3']
129 opts, _ = cmark.Options().parse_args(new_flags)
130
131 in_file = cStringIO.StringIO(NEW_DOC)
132 out_file = cStringIO.StringIO()
133 cmark.Render(opts, {}, in_file, out_file)
134
135 h = out_file.getvalue()
136 self.assert_('<div class="toclevel1"><a href="#one">' in h, h)
137
138 def testNewPrettyHref(self):
139 # type: () -> None
140 # New style of doc
141
142 new_flags = ['--toc-tag', 'h2', '--toc-tag', 'h3', '--toc-pretty-href']
143 opts, _ = cmark.Options().parse_args(new_flags)
144
145 in_file = cStringIO.StringIO(NEW_DOC)
146 out_file = cStringIO.StringIO()
147 cmark.Render(opts, {}, in_file, out_file)
148 h = out_file.getvalue()
149 self.assert_('<a name="subsubheading">' in h, h)
150
151 self.assert_('<div class="toclevel1"><a href="#one">' in h, h)
152 print(h)
153
154 def testExtractor(self):
155 # type: () -> None
156 parser = cmark.TocExtractor()
157 parser.feed(_HTML_1)
158 self.assertEqual(5, parser.toc_begin_line)
159
160 for heading in parser.headings:
161 print(heading)
162
163 headings = parser.headings
164 self.assertEqual(4, len(headings))
165
166 line_num, tag, css_id, html, text = headings[0]
167 self.assertEqual(8, line_num)
168 self.assertEqual('h2', tag)
169 self.assertEqual(None, css_id)
170 # nested <a> tags are omitted!
171 self.assertEqual('One link', ''.join(html))
172 self.assertEqual('One link', ''.join(text))
173
174 line_num, tag, css_id, html, text = headings[1]
175 self.assertEqual(12, line_num)
176 self.assertEqual('h3', tag)
177 self.assertEqual(None, css_id)
178 self.assertEqual('subheading <code>backticks</code>', ''.join(html))
179 self.assertEqual('subheading backticks', ''.join(text))
180
181 line_num, tag, css_id, html, text = headings[2]
182 self.assertEqual(14, line_num)
183 self.assertEqual('h3', tag)
184 self.assertEqual(None, css_id)
185 self.assertEqual('one &amp; two', ''.join(html))
186 self.assertEqual('one two', ''.join(text))
187
188 line_num, tag, css_id, html, text = headings[3]
189 self.assertEqual(16, line_num)
190 self.assertEqual('h2', tag)
191 self.assertEqual('explicit', css_id)
192 self.assertEqual('Two', ''.join(html))
193 self.assertEqual('Two', ''.join(text))
194
195 def testExtractorDense(self):
196 # type: () -> None
197 parser = cmark.TocExtractor()
198 parser.feed(_HTML_1.replace('"toc"', '"dense-toc"'))
199
200 self.assertEqual(-1, parser.toc_begin_line)
201 self.assertEqual(5, parser.dense_toc_begin_line)
202
203 insertions = cmark._MakeTocInsertionsDense(parser.headings,
204 parser.dense_toc_begin_line,
205 True)
206 pprint(insertions)
207
208
209def InitCMark():
210 import ctypes
211
212 # Geez find_library returns the filename and not the path? Just hardcode it as
213 # a workaround.
214 # https://bugs.python.org/issue21042
215
216 #from ctypes.util import find_library
217 #libname = find_library("cmark")
218 #assert libname, "cmark not found"
219
220 # There's some ongoing discussion about how to deal with the same in Nix.
221 # I think normally you'd just patch/substitute this path during the Nix build.
222 # See note in shell.nix
223 this_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
224
225 cmark1 = os.environ.get('_NIX_SHELL_LIBCMARK')
226 cmark2 = os.path.join(this_dir, '../../oil_DEPS/libcmark.so')
227 cmark3 = os.path.join(cmark.CMARK_WEDGE_DIR,
228 'lib/libcmark.so') # a symlink
229
230 if cmark1 is not None and os.path.exists(cmark1):
231 libname = cmark1
232 elif os.path.exists(cmark2):
233 libname = cmark2
234 elif os.path.exists(cmark3):
235 libname = cmark3
236 else:
237 raise AssertionError("Couldn't find libcmark.so")
238
239 cmark_dll = ctypes.CDLL(libname)
240
241 markdown = cmark_dll.cmark_markdown_to_html
242 markdown.restype = ctypes.c_char_p
243 markdown.argtypes = [ctypes.c_char_p, ctypes.c_long, ctypes.c_long]
244 return markdown
245
246
247# Version 0.29.0 disallowed raw HTML by default!
248CMARK_OPT_UNSAFE = (1 << 17)
249
250
251def md2html(md):
252 # type: (str) -> str
253 markdown = InitCMark()
254
255 if sys.version_info.major == 2:
256 md_bytes = md
257 else:
258 md_bytes = md.encode('utf-8')
259
260 md_len = len(md)
261 html = markdown(md_bytes, md_len, CMARK_OPT_UNSAFE)
262
263 if sys.version_info.major == 2:
264 return html
265 else:
266 return html.decode('utf-8')
267
268
269class CompareTest(unittest.TestCase):
270 """Test that the child process behaves like the shared library"""
271
272 def OLD_testChildProcess(self):
273 # OK it adds a newline
274 md = '*hi*'
275 h = md2html(md)
276 print(repr(h))
277
278 h2 = cmark.cmark_bin(md)
279 print(repr(h2))
280
281 self.assertEqual(h, h2)
282
283 def OLD_testHtml(self):
284 md2 = '*hi* <script>alert("hi");</script>'
285 h = md2html(md2)
286 print(repr(h))
287
288 # OK this omits the HTML, which we need
289 h2 = cmark.cmark_bin(md2)
290 print(repr(h2))
291
292 self.assertEqual(h, h2)
293
294
295if __name__ == '__main__':
296 unittest.main()