Skip to content

Commit d20dd8e

Browse files
author
Devin Torres
committed
Merge pull request vmg#122 from uranusjr/unittest
New test framework based on Python’s unittest module
2 parents b92c454 + 00a5b7a commit d20dd8e

8 files changed

+272
-51
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ src/html_blocks.c: html_block_names.gperf
4646
# Testing
4747

4848
test: hoedown
49-
test/runner.sh ./hoedown test/MarkdownTest_1.0.3/Tests
49+
python test/runner.py
5050

5151
test-pl: hoedown
5252
perl test/MarkdownTest_1.0.3/MarkdownTest.pl \

test/Tests/Math.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<p>\[
2+
1*2*3 multi-line math
3+
\]</p>
4+
5+
<p>\( 1*2*3 inline-math \)</p>
6+
7+
<p>\[ 1*2*3 math with dollar \]</p>
8+
9+
<p>\[ 1*2*3 \$ \\ \text{dollar with escapes} \]</p>
10+
11+
<p>\( \\ \text{backslash with escapes} \$ 1*2*3 \)</p>
12+
13+
<p>( not <em>really</em> math )</p>
14+
15+
<p>$$ also <em>not</em> math $$</p>
16+
17+
<p>this$$ should <em>not</em> be$$ math</p>
18+
19+
<p>nor $$ <em>should</em> $$this</p>
20+
21+
<p>this \(*should* be\) math</p>
22+
23+
<p>Something \{ like <em>math</em> but \} is not</p>
24+
25+
<p>Also \(like <em>math</em> but \) is not</p>
26+
27+
<p>\\( should not be <em>math</em> either \\)</p>
28+
29+
<p>This is \( math, and the \\\( inner one \\\) should be \) preserved</p>
30+
31+
<p>\[ did you &lt;em&gt; know &lt;/em&gt; this is math? \]</p>

test/Tests/Math.text

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
\\[
2+
1*2*3 multi-line math
3+
\\]
4+
5+
\\( 1*2*3 inline-math \\)
6+
7+
$$ 1*2*3 math with dollar $$
8+
9+
$$ 1*2*3 \$ \\ \text{dollar with escapes} $$
10+
11+
\\( \\ \text{backslash with escapes} \$ 1*2*3 \\)
12+
13+
\( not *really* math \)
14+
15+
\$$ also *not* math \$$
16+
17+
this$$ should *not* be$$ math
18+
19+
nor $$ *should* $$this
20+
21+
this $$*should* be$$ math
22+
23+
Something \\{ like *math* but \\} is not
24+
25+
Also \\\(like *math* but \\\) is not
26+
27+
\\\\( should not be *math* either \\\\)
28+
29+
This is \\( math, and the \\\( inner one \\\) should be \\) preserved
30+
31+
$$ did you <em> know </em> this is math? $$

test/config.json

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"tests": [
3+
{
4+
"input": "MarkdownTest_1.0.3/Tests/Amps and angle encoding.text",
5+
"output": "MarkdownTest_1.0.3/Tests/Amps and angle encoding.html"
6+
},
7+
{
8+
"input": "MarkdownTest_1.0.3/Tests/Auto links.text",
9+
"output": "MarkdownTest_1.0.3/Tests/Auto links.html"
10+
},
11+
{
12+
"input": "MarkdownTest_1.0.3/Tests/Backslash escapes.text",
13+
"output": "MarkdownTest_1.0.3/Tests/Backslash escapes.html"
14+
},
15+
{
16+
"input": "MarkdownTest_1.0.3/Tests/Blockquotes with code blocks.text",
17+
"output": "MarkdownTest_1.0.3/Tests/Blockquotes with code blocks.html"
18+
},
19+
{
20+
"input": "MarkdownTest_1.0.3/Tests/Code Blocks.text",
21+
"output": "MarkdownTest_1.0.3/Tests/Code Blocks.html"
22+
},
23+
{
24+
"input": "MarkdownTest_1.0.3/Tests/Code Spans.text",
25+
"output": "MarkdownTest_1.0.3/Tests/Code Spans.html"
26+
},
27+
{
28+
"input": "MarkdownTest_1.0.3/Tests/Hard-wrapped paragraphs with list-like lines.text",
29+
"output": "MarkdownTest_1.0.3/Tests/Hard-wrapped paragraphs with list-like lines.html"
30+
},
31+
{
32+
"input": "MarkdownTest_1.0.3/Tests/Horizontal rules.text",
33+
"output": "MarkdownTest_1.0.3/Tests/Horizontal rules.html"
34+
},
35+
{
36+
"input": "MarkdownTest_1.0.3/Tests/Inline HTML (Advanced).text",
37+
"output": "MarkdownTest_1.0.3/Tests/Inline HTML (Advanced).html"
38+
},
39+
{
40+
"input": "MarkdownTest_1.0.3/Tests/Inline HTML (Simple).text",
41+
"output": "MarkdownTest_1.0.3/Tests/Inline HTML (Simple).html"
42+
},
43+
{
44+
"input": "MarkdownTest_1.0.3/Tests/Inline HTML comments.text",
45+
"output": "MarkdownTest_1.0.3/Tests/Inline HTML comments.html"
46+
},
47+
{
48+
"input": "MarkdownTest_1.0.3/Tests/Links, inline style.text",
49+
"output": "MarkdownTest_1.0.3/Tests/Links, inline style.html"
50+
},
51+
{
52+
"input": "MarkdownTest_1.0.3/Tests/Links, reference style.text",
53+
"output": "MarkdownTest_1.0.3/Tests/Links, reference style.html"
54+
},
55+
{
56+
"input": "MarkdownTest_1.0.3/Tests/Links, shortcut references.text",
57+
"output": "MarkdownTest_1.0.3/Tests/Links, shortcut references.html"
58+
},
59+
{
60+
"input": "MarkdownTest_1.0.3/Tests/Literal quotes in titles.text",
61+
"output": "MarkdownTest_1.0.3/Tests/Literal quotes in titles.html"
62+
},
63+
{
64+
"input": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Basics.text",
65+
"output": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Basics.html"
66+
},
67+
{
68+
"input": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Syntax.text",
69+
"output": "MarkdownTest_1.0.3/Tests/Markdown Documentation - Syntax.html"
70+
},
71+
{
72+
"input": "MarkdownTest_1.0.3/Tests/Nested blockquotes.text",
73+
"output": "MarkdownTest_1.0.3/Tests/Nested blockquotes.html"
74+
},
75+
{
76+
"input": "MarkdownTest_1.0.3/Tests/Ordered and unordered lists.text",
77+
"output": "MarkdownTest_1.0.3/Tests/Ordered and unordered lists.html"
78+
},
79+
{
80+
"input": "MarkdownTest_1.0.3/Tests/Strong and em together.text",
81+
"output": "MarkdownTest_1.0.3/Tests/Strong and em together.html"
82+
},
83+
{
84+
"input": "MarkdownTest_1.0.3/Tests/Tabs.text",
85+
"output": "MarkdownTest_1.0.3/Tests/Tabs.html"
86+
},
87+
{
88+
"input": "MarkdownTest_1.0.3/Tests/Tidyness.text",
89+
"output": "MarkdownTest_1.0.3/Tests/Tidyness.html"
90+
},
91+
{
92+
"input": "Tests/Escape character.text",
93+
"output": "Tests/Escape character.html"
94+
},
95+
{
96+
"input": "Tests/Math.text",
97+
"output": "Tests/Math.html",
98+
"flags": ["--math"]
99+
}
100+
]
101+
}

test/runner.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
import difflib
5+
import json
6+
import os
7+
import re
8+
import subprocess
9+
import unittest
10+
11+
TEST_ROOT = os.path.dirname(__file__)
12+
PROJECT_ROOT = os.path.dirname(TEST_ROOT)
13+
HOEDOWN = [os.path.abspath(os.path.join(PROJECT_ROOT, 'hoedown'))]
14+
TIDY = ['tidy', '--show-body-only', '1', '--show-warnings', '0',
15+
'--quiet', '1']
16+
CONFIG_PATH = os.path.join(TEST_ROOT, 'config.json')
17+
SLUGIFY_PATTERN = re.compile(r'\W')
18+
19+
20+
def with_metaclass(meta, *bases):
21+
"""Metaclass injection utility from six.
22+
23+
See: https://pythonhosted.org/six/
24+
"""
25+
class metaclass(meta):
26+
def __new__(cls, name, this_bases, d):
27+
return meta(name, bases, d)
28+
return type.__new__(metaclass, 'temporary_class', (), {})
29+
30+
31+
class TestFailed(AssertionError):
32+
def __init__(self, name, expected, got):
33+
super(TestFailed, self).__init__(self)
34+
diff = difflib.unified_diff(
35+
expected.splitlines(), got.splitlines(),
36+
fromfile='Expected', tofile='Got',
37+
)
38+
self.description = '{name}\n{diff}'.format(
39+
name=name, diff='\n'.join(diff),
40+
)
41+
42+
def __str__(self):
43+
return self.description
44+
45+
46+
def _test_func(test_case):
47+
flags = test_case.get('flags') or []
48+
hoedown_proc = subprocess.Popen(
49+
HOEDOWN + flags + [os.path.join(TEST_ROOT, test_case['input'])],
50+
stdout=subprocess.PIPE,
51+
)
52+
hoedown_proc.wait()
53+
got_tidy_proc = subprocess.Popen(
54+
TIDY, stdin=hoedown_proc.stdout, stdout=subprocess.PIPE,
55+
)
56+
got_tidy_proc.wait()
57+
got = got_tidy_proc.stdout.read().strip()
58+
59+
expected_tidy_proc = subprocess.Popen(
60+
TIDY + [os.path.join(TEST_ROOT, test_case['output'])],
61+
stdout=subprocess.PIPE,
62+
)
63+
expected_tidy_proc.wait()
64+
expected = expected_tidy_proc.stdout.read().strip()
65+
66+
# Cleanup.
67+
hoedown_proc.stdout.close()
68+
got_tidy_proc.stdout.close()
69+
expected_tidy_proc.stdout.close()
70+
71+
try:
72+
assert expected == got
73+
except AssertionError:
74+
raise TestFailed(test_case['input'], expected, got)
75+
76+
77+
def _make_test(test_case):
78+
return lambda self: _test_func(test_case)
79+
80+
81+
class MarkdownTestsMeta(type):
82+
"""Meta class for ``MarkdownTestCase`` to inject test cases on the fly.
83+
"""
84+
def __new__(meta, name, bases, attrs):
85+
with open(CONFIG_PATH) as f:
86+
config = json.load(f)
87+
88+
for test in config['tests']:
89+
input_name = test['input']
90+
attr_name = 'test_' + SLUGIFY_PATTERN.sub(
91+
'_', os.path.splitext(input_name)[0].lower(),
92+
)
93+
func = _make_test(test)
94+
func.__doc__ = input_name
95+
if test.get('skip', False):
96+
func = unittest.skip(input_name)(func)
97+
if test.get('fail', False):
98+
func = unittest.expectsFailure(func)
99+
attrs[attr_name] = func
100+
return type.__new__(meta, name, bases, attrs)
101+
102+
103+
class MarkdownTests(with_metaclass(MarkdownTestsMeta, unittest.TestCase)):
104+
pass
105+
106+
107+
if __name__ == '__main__':
108+
unittest.main()

test/runner.sh

-50
This file was deleted.

0 commit comments

Comments
 (0)