1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
import argparse
import sys
import platform
import itertools
import multiprocessing
import time
from cmark import CMark
TIMEOUT = 10
parser = argparse.ArgumentParser(description='Run cmark tests.')
parser.add_argument('--program', dest='program', nargs='?', default=None,
help='program to test')
parser.add_argument('--library-dir', dest='library_dir', nargs='?',
default=None, help='directory containing dynamic library')
args = parser.parse_args(sys.argv[1:])
allowed_failures = {"many references": True}
cmark = CMark(prog=args.program, library_dir=args.library_dir)
def hash_collisions():
REFMAP_SIZE = 16
COUNT = 50000
def badhash(ref):
h = 0
for c in ref:
a = (h << 6) & 0xFFFFFFFF
b = (h << 16) & 0xFFFFFFFF
h = ord(c) + a + b - h
h = h & 0xFFFFFFFF
return (h % REFMAP_SIZE) == 0
keys = ("x%d" % i for i in itertools.count())
collisions = itertools.islice((k for k in keys if badhash(k)), COUNT)
bad_key = next(collisions)
document = ''.join("[%s]: /url\n\n[%s]\n\n" % (key, bad_key) for key in collisions)
return document, re.compile("(<p>\[%s\]</p>\n){%d}" % (bad_key, COUNT-1))
# list of pairs consisting of input and a regex that must match the output.
pathological = {
# note - some pythons have limit of 65535 for {num-matches} in re.
"nested strong emph":
(("*a **a " * 65000) + "b" + (" a** a*" * 65000),
re.compile("(<em>a <strong>a ){65000}b( a</strong> a</em>){65000}")),
"many emph closers with no openers":
(("a_ " * 65000),
re.compile("(a[_] ){64999}a_")),
"many emph openers with no closers":
(("_a " * 65000),
re.compile("(_a ){64999}_a")),
"many link closers with no openers":
(("a]" * 65000),
re.compile("(a\]){65000}")),
"many link openers with no closers":
(("[a" * 65000),
re.compile("(\[a){65000}")),
"mismatched openers and closers":
(("*a_ " * 50000),
re.compile("([*]a[_] ){49999}[*]a_")),
"issue #389":
(("*a " * 20000 + "_a*_ " * 20000),
re.compile("(<em>a ){20000}(_a<\/em>_ ?){20000}")),
"openers and closers multiple of 3":
(("a**b" + ("c* " * 50000)),
re.compile("a[*][*]b(c[*] ){49999}c[*]")),
"link openers and emph closers":
(("[ a_" * 50000),
re.compile("(\[ a_){50000}")),
"pattern [ (]( repeated":
(("[ (](" * 80000),
re.compile("(\[ \(\]\(){80000}")),
"hard link/emph case":
("**x [a*b**c*](d)",
re.compile("\\*\\*x <a href=\"d\">a<em>b\\*\\*c</em></a>")),
"nested brackets":
(("[" * 50000) + "a" + ("]" * 50000),
re.compile("\[{50000}a\]{50000}")),
"nested block quotes":
((("> " * 50000) + "a"),
re.compile("(<blockquote>\n){50000}")),
"deeply nested lists":
("".join(map(lambda x: (" " * x + "* a\n"), range(0,1000))),
re.compile("<ul>\n(<li>a\n<ul>\n){999}<li>a</li>\n</ul>\n(</li>\n</ul>\n){999}")),
"U+0000 in input":
("abc\u0000de\u0000",
re.compile("abc\ufffd?de\ufffd?")),
"backticks":
("".join(map(lambda x: ("e" + "`" * x), range(1,5000))),
re.compile("^<p>[e`]*</p>\n$")),
"unclosed links A":
("[a](<b" * 30000,
re.compile("(\[a\]\(<b){30000}")),
"unclosed links B":
("[a](b" * 30000,
re.compile("(\[a\]\(b){30000}")),
"reference collisions": hash_collisions()
# "many references":
# ("".join(map(lambda x: ("[" + str(x) + "]: u\n"), range(1,5000 * 16))) + "[0] " * 5000,
# re.compile("(\[0\] ){4999}"))
}
whitespace_re = re.compile('/s+/')
results = {'passed': [], 'errored': [], 'failed': [], 'ignored': []}
def run_pathological_test(description, results):
(inp, regex) = pathological[description]
[rc, actual, err] = cmark.to_html(inp)
extra = ""
if rc != 0:
print(description, '[ERRORED (return code %d)]' %rc)
print(err)
if allowed_failures[description]:
results['ignored'].append(description)
else:
results['errored'].append(description)
elif regex.search(actual):
print(description, '[PASSED]')
results['passed'].append(description)
else:
print(description, '[FAILED]')
print(repr(actual))
if allowed_failures[description]:
results['ignored'].append(description)
else:
results['failed'].append(description)
def run_tests():
print("Testing pathological cases:")
for description in pathological:
p = multiprocessing.Process(target=run_pathological_test,
args=(description, results,))
p.start()
# wait TIMEOUT seconds or until it finishes
p.join(TIMEOUT)
# kill it if still active
if p.is_alive():
print(description, '[TIMEOUT]')
if allowed_failures[description]:
results['ignored'].append(description)
else:
results['errored'].append(description)
p.terminate()
p.join()
passed = len(results['passed'])
failed = len(results['failed'])
errored = len(results['errored'])
ignored = len(results['ignored'])
print("%d passed, %d failed, %d errored" % (passed, failed, errored))
if ignored > 0:
print("Ignoring these allowed failures:")
for x in results['ignored']:
print(x)
if failed == 0 and errored == 0:
exit(0)
else:
exit(1)
if __name__ == "__main__":
run_tests()
|