hoedown/test/runner.py
2014-10-22 13:12:03 +08:00

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()