| #!/usr/bin/env python |
| |
| import os, difflib, time, gc, codecs, platform, sys |
| from pprint import pprint |
| import textwrap |
| |
| # Setup a logger manually for compatibility with Python 2.3 |
| import logging |
| logging.getLogger('MARKDOWN').addHandler(logging.StreamHandler()) |
| import markdown |
| |
| TEST_DIR = "tests" |
| TMP_DIR = "./tmp/" |
| WRITE_BENCHMARK = True |
| WRITE_BENCHMARK = False |
| ACTUALLY_MEASURE_MEMORY = True |
| |
| ###################################################################### |
| |
| if platform.system().lower() == "darwin": # Darwin |
| _proc_status = '/proc/%d/stat' % os.getpid() |
| else: # Linux |
| _proc_status = '/proc/%d/status' % os.getpid() |
| |
| _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, |
| 'KB': 1024.0, 'MB': 1024.0*1024.0} |
| |
| def _VmB(VmKey): |
| '''Private. |
| ''' |
| global _proc_status, _scale |
| # get pseudo file /proc/<pid>/status |
| try: |
| t = open(_proc_status) |
| v = t.read() |
| t.close() |
| except: |
| return 0.0 # non-Linux? |
| # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' |
| i = v.index(VmKey) |
| v = v[i:].split(None, 3) # whitespace |
| if len(v) < 3: |
| return 0.0 # invalid format? |
| # convert Vm value to bytes |
| return float(v[1]) * _scale[v[2]] |
| |
| |
| def memory(since=0.0): |
| '''Return memory usage in bytes. |
| ''' |
| if ACTUALLY_MEASURE_MEMORY : |
| return _VmB('VmSize:') - since |
| |
| |
| def resident(since=0.0): |
| '''Return resident memory usage in bytes. |
| ''' |
| return _VmB('VmRSS:') - since |
| |
| |
| def stacksize(since=0.0): |
| '''Return stack size in bytes. |
| ''' |
| return _VmB('VmStk:') - since |
| |
| |
| ############################################################ |
| |
| DIFF_FILE_TEMPLATE = """ |
| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
| <style> |
| td { |
| padding-left: 10px; |
| padding-right: 10px; |
| } |
| colgroup { |
| margin: 10px; |
| } |
| .diff_header { |
| color: gray; |
| } |
| .ok { |
| color: green; |
| } |
| .gray { |
| color: gray; |
| } |
| .failed a { |
| color: red; |
| } |
| .failed { |
| color: red; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Results Summary</h1> |
| <table rules="groups" > |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <th> |
| <td></td> |
| <td>Seconds</td> |
| <td></td> |
| <td>Memory</td> |
| </th> |
| <tbody> |
| """ |
| |
| FOOTER = """ |
| </body> |
| </html> |
| """ |
| |
| DIFF_TABLE_TEMPLATE = """ |
| <table class="diff" rules="groups" > |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <colgroup></colgroup> |
| <th> |
| <td></td> |
| <td>Expected</td> |
| <td></td> |
| <td></td> |
| <td>Actual</td> |
| </th> |
| <tbody> |
| %s |
| </tbody> |
| </table> |
| """ |
| |
| |
| def smart_split(text) : |
| result = [] |
| for x in text.splitlines() : |
| for y in textwrap.wrap(textwrap.dedent(x), 40): |
| result.append(y) |
| return result |
| |
| |
| differ = difflib.Differ() |
| try : |
| htmldiff = difflib.HtmlDiff() |
| except: |
| htmldiff = None |
| |
| class TestRunner : |
| |
| def __init__ (self) : |
| self.failedTests = [] |
| if not os.path.exists(TMP_DIR): |
| os.mkdir(TMP_DIR) |
| |
| def test_directory(self, dir, measure_time=False, safe_mode=False, encoding="utf-8", output_format='xhtml1') : |
| self.encoding = encoding |
| benchmark_file_name = os.path.join(dir, "benchmark.dat") |
| self.saved_benchmarks = {} |
| |
| if measure_time : |
| if os.path.exists(benchmark_file_name) : |
| file = open(benchmark_file_name) |
| for line in file.readlines() : |
| test, str_time, str_mem = line.strip().split(":") |
| self.saved_benchmarks[test] = (float(str_time), float(str_mem)) |
| repeat = range(10) |
| else : |
| repeat = (0,) |
| |
| # First, determine from the name of the directory if any extensions |
| # need to be loaded. |
| |
| parts = os.path.split(dir)[-1].split("-x-") |
| if len(parts) > 1 : |
| extensions = parts[1].split("-") |
| print extensions |
| else : |
| extensions = [] |
| |
| mem = memory() |
| start = time.clock() |
| self.md = markdown.Markdown(extensions=extensions, safe_mode = safe_mode, output_format=output_format) |
| construction_time = time.clock() - start |
| construction_mem = memory(mem) |
| |
| self.benchmark_buffer = "construction:%f:%f\n" % (construction_time, |
| construction_mem) |
| |
| html_diff_file_path = os.path.join(TMP_DIR, os.path.split(dir)[-1]) + ".html" |
| self.html_diff_file = codecs.open(html_diff_file_path, "w", encoding=encoding) |
| self.html_diff_file.write(DIFF_FILE_TEMPLATE) |
| |
| self.diffs_buffer = "" |
| |
| tests = [x.replace(".txt", "") |
| for x in os.listdir(dir) if x.endswith(".txt")] |
| tests.sort() |
| for test in tests : |
| self.run_test(dir, test, repeat) |
| |
| self.html_diff_file.write("</table>") |
| |
| if sys.version < "3.0": |
| self.html_diff_file.write(self.diffs_buffer.decode("utf-8")) |
| |
| self.html_diff_file.write(FOOTER) |
| self.html_diff_file.close() |
| print "Diff written to %s" % html_diff_file_path |
| |
| benchmark_output_file_name = benchmark_file_name |
| |
| if not WRITE_BENCHMARK: |
| benchmark_output_file_name += ".tmp" |
| |
| self.benchmark_file = open(benchmark_output_file_name, "w") |
| self.benchmark_file.write(self.benchmark_buffer) |
| self.benchmark_file.close() |
| |
| |
| #################### |
| |
| |
| def run_test(self, dir, test, repeat): |
| |
| print "--- %s ---" % test |
| self.html_diff_file.write("<tr><td>%s</td>" % test) |
| input_file = os.path.join(dir, test + ".txt") |
| output_file = os.path.join(dir, test + ".html") |
| |
| expected_output = codecs.open(output_file, encoding=self.encoding).read() |
| input = codecs.open(input_file, encoding=self.encoding).read() |
| actual_output = "" |
| actual_lines = [] |
| self.md.source = "" |
| gc.collect() |
| mem = memory() |
| start = time.clock() |
| for x in repeat: |
| actual_output = self.md.convert(input) |
| conversion_time = time.clock() - start |
| conversion_mem = memory(mem) |
| self.md.reset() |
| |
| expected_lines = [x.encode("utf-8") for x in smart_split(expected_output)] |
| actual_lines = [x.encode("utf-8") for x in smart_split(actual_output)] |
| |
| #diff = difflib.ndiff(expected_output.split("\n"), |
| # actual_output.split("\n")) |
| |
| diff = [x for x in differ.compare(expected_lines, |
| actual_lines) |
| if not x.startswith(" ")] |
| |
| if not diff: |
| self.html_diff_file.write("<td class='ok'>OK</td>") |
| else : |
| self.failedTests.append(test) |
| self.html_diff_file.write("<td class='failed'>" + |
| "<a href='#diff-%s'>FAILED</a></td>" % test) |
| print "MISMATCH on %s/%s.txt" % (dir, test) |
| print |
| for line in diff : |
| print line |
| if htmldiff!=None : |
| htmlDiff = htmldiff.make_table(expected_lines, actual_lines, |
| context=True) |
| htmlDiff = "\n".join( [x for x in htmlDiff.splitlines() |
| if x.strip().startswith("<tr>")] ) |
| self.diffs_buffer += "<a name='diff-%s'/><h2>%s</h2>" % (test, test) |
| self.diffs_buffer += DIFF_TABLE_TEMPLATE % htmlDiff |
| |
| expected_time, expected_mem = self.saved_benchmarks.get(test, ("na", "na")) |
| |
| self.html_diff_file.write(get_benchmark_html(conversion_time, expected_time)) |
| self.html_diff_file.write(get_benchmark_html(conversion_mem, expected_mem)) |
| self.html_diff_file.write("</tr>\n") |
| |
| self.benchmark_buffer += "%s:%f:%f\n" % (test, |
| conversion_time, conversion_mem) |
| |
| |
| |
| |
| |
| def get_benchmark_html (actual, expected) : |
| buffer = "" |
| if not expected == "na": |
| if actual > expected * 1.5: |
| tdiff = "failed" |
| elif actual * 1.5 < expected : |
| tdiff = "ok" |
| else : |
| tdiff = "same" |
| if ( (actual <= 0 and expected < 0.015) or |
| (expected <= 0 and actual < 0.015)) : |
| tdiff = "same" |
| else : |
| tdiff = "same" |
| buffer += "<td class='%s'>%.2f</td>" % (tdiff, actual) |
| if not expected == "na": |
| buffer += "<td class='gray'>%.2f</td>" % (expected) |
| return buffer |
| |
| |
| def run_tests() : |
| |
| tester = TestRunner() |
| #test.test_directory("tests/basic") |
| tester.test_directory("tests/markdown-test", measure_time=True) |
| tester.test_directory("tests/misc", measure_time=True) |
| tester.test_directory("tests/extensions-x-tables") |
| tester.test_directory("tests/extensions-x-footnotes") |
| #tester.test_directory("tests/extensions-x-ext1-ext2") |
| tester.test_directory("tests/safe_mode", measure_time=True, safe_mode="escape") |
| tester.test_directory("tests/extensions-x-wikilinks") |
| tester.test_directory("tests/extensions-x-toc") |
| tester.test_directory("tests/extensions-x-def_list") |
| tester.test_directory("tests/extensions-x-abbr") |
| tester.test_directory("tests/html4", output_format='html4') |
| |
| try: |
| import pygments |
| except ImportError: |
| # Dependancy not avalable - skip test |
| pass |
| else: |
| tester.test_directory("tests/extensions-x-codehilite") |
| |
| print "\n### Final result ###" |
| if len(tester.failedTests): |
| print "%d failed tests: %s" % (len(tester.failedTests), str(tester.failedTests)) |
| else: |
| print "All tests passed, no errors!" |
| |
| run_tests() |
| |
| |
| |
| |