107 lines
3 KiB
Python
Executable file
107 lines
3 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import difflib
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import unittest
|
|
|
|
TEST_ROOT = os.path.dirname(__file__)
|
|
PROJECT_ROOT = os.path.dirname(TEST_ROOT)
|
|
HOEDOWN = [os.path.abspath(os.path.join(PROJECT_ROOT, 'hoedown'))]
|
|
TIDY = ['tidy', '--show-body-only', '1', '--show-warnings', '0',
|
|
'--quiet', '1']
|
|
CONFIG_PATH = os.path.join(TEST_ROOT, 'config.json')
|
|
SLUGIFY_PATTERN = re.compile(r'\W')
|
|
|
|
|
|
def with_metaclass(meta, *bases):
|
|
"""Metaclass injection utility from six.
|
|
|
|
See: https://pythonhosted.org/six/
|
|
"""
|
|
class metaclass(meta):
|
|
def __new__(cls, name, this_bases, d):
|
|
return meta(name, bases, d)
|
|
return type.__new__(metaclass, 'temporary_class', (), {})
|
|
|
|
|
|
class TestFailed(AssertionError):
|
|
def __init__(self, name, expected, got):
|
|
super(TestFailed, self).__init__(self)
|
|
diff = difflib.unified_diff(
|
|
expected.splitlines(), got.splitlines(),
|
|
fromfile='Expected', tofile='Got',
|
|
)
|
|
self.description = '{name}\n{diff}'.format(
|
|
name=name, diff='\n'.join(diff),
|
|
)
|
|
|
|
def __str__(self):
|
|
return self.description
|
|
|
|
|
|
def _test_func(test_case):
|
|
flags = test_case.get('flags') or []
|
|
hoedown_proc = subprocess.Popen(
|
|
HOEDOWN + flags + [os.path.join(TEST_ROOT, test_case['input'])],
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
stdoutdata = hoedown_proc.communicate()[0]
|
|
|
|
got_tidy_proc = subprocess.Popen(
|
|
TIDY, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
)
|
|
got = got_tidy_proc.communicate(input=stdoutdata)[0].strip()
|
|
|
|
expected_tidy_proc = subprocess.Popen(
|
|
TIDY + [os.path.join(TEST_ROOT, test_case['output'])],
|
|
stdout=subprocess.PIPE,
|
|
)
|
|
expected = expected_tidy_proc.communicate()[0].strip()
|
|
|
|
# Cleanup.
|
|
hoedown_proc.stdout.close()
|
|
got_tidy_proc.stdout.close()
|
|
expected_tidy_proc.stdout.close()
|
|
|
|
try:
|
|
assert expected == got
|
|
except AssertionError:
|
|
raise TestFailed(test_case['input'], expected, got)
|
|
|
|
|
|
def _make_test(test_case):
|
|
return lambda self: _test_func(test_case)
|
|
|
|
|
|
class MarkdownTestsMeta(type):
|
|
"""Meta class for ``MarkdownTestCase`` to inject test cases on the fly.
|
|
"""
|
|
def __new__(meta, name, bases, attrs):
|
|
with open(CONFIG_PATH) as f:
|
|
config = json.load(f)
|
|
|
|
for test in config['tests']:
|
|
input_name = test['input']
|
|
attr_name = 'test_' + SLUGIFY_PATTERN.sub(
|
|
'_', os.path.splitext(input_name)[0].lower(),
|
|
)
|
|
func = _make_test(test)
|
|
func.__doc__ = input_name
|
|
if test.get('skip', False):
|
|
func = unittest.skip(input_name)(func)
|
|
if test.get('fail', False):
|
|
func = unittest.expectsFailure(func)
|
|
attrs[attr_name] = func
|
|
return type.__new__(meta, name, bases, attrs)
|
|
|
|
|
|
class MarkdownTests(with_metaclass(MarkdownTestsMeta, unittest.TestCase)):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|