Artifact Content
Not logged in

Artifact e664991b3e89b7b8923476d74bf6816223ea4768:


#!/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)):
    def test_lines(self):
        from os import listdir
        from os.path import isfile, join, splitext

        LINE_TESTS = "./test/line_tests"
        files = [join(LINE_TESTS, f) for f in listdir(LINE_TESTS)
                 if isfile(join(LINE_TESTS, f))]
        indexes = {}
        for f in files:
            filename, file_extension = splitext(f)
            if file_extension == ".md":
                indexes[f] = filename + '.out'
        for index in indexes:
            content = ''
            try:
                with open(indexes[index], 'r') as fdd:
                    content = fdd.read()
            except Exception:
                raise TestFailed(index,
                                 'Missing file: "{}"'.format(indexes[index]),
                                 '')
            child = subprocess.Popen(["./test/tester", index],
                                     stdout=subprocess.PIPE)
            stdout = child.communicate()[0]
            if stdout != content:
                raise TestFailed(index, content, stdout)


if __name__ == '__main__':
    unittest.main()