aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWaylan Limberg <waylan.limberg@icloud.com>2018-01-06 01:04:11 -0500
committerWaylan Limberg <waylan.limberg@icloud.com>2018-01-08 20:41:18 -0500
commit49249b3fb6c458c2cbc34c409ba57cfae6e72320 (patch)
treee5cdbb09bf193f40ccc8735679d93a8c14aa591a
parent76e0a63e12dd964311c4350a5735bb7f51ef87a6 (diff)
downloadmarkdown-49249b3fb6c458c2cbc34c409ba57cfae6e72320.tar.gz
markdown-49249b3fb6c458c2cbc34c409ba57cfae6e72320.tar.bz2
markdown-49249b3fb6c458c2cbc34c409ba57cfae6e72320.zip
Switch from nose to unittest
All file-based tests are now defined as unittest test cases via a metaclass which walks a directory and builds a unittest for each pair of test files. To run the tests just run `python -m unittest discover tests`. Or use tox as the tox config has been updated to run the new tests and all nose specific code has been removed. The test generator tools have been removed as well. If any changes or additions need to be made to tests, they should be implemented using the new framework rather than with the file-based tests. Eventually, only the PHP and pl tests should remain as file-based tests.
-rw-r--r--MANIFEST.in3
-rw-r--r--makefile8
-rw-r--r--markdown/test_tools.py125
-rwxr-xr-xrun-tests.py23
-rw-r--r--setup.cfg2
-rw-r--r--test-requirements.txt1
-rw-r--r--tests/__init__.py189
-rw-r--r--tests/basic/benchmark.dat20
-rw-r--r--tests/extensions/extra/test.cfg36
-rw-r--r--tests/extensions/test.cfg73
-rw-r--r--tests/options/test.cfg11
-rw-r--r--tests/php/extra/test.cfg7
-rw-r--r--tests/php/test.cfg50
-rw-r--r--tests/pl/Tests_2004/test.cfg10
-rw-r--r--tests/pl/Tests_2007/test.cfg25
-rw-r--r--tests/plugins.py127
-rw-r--r--tests/safe_mode/test.cfg8
-rw-r--r--tests/test_legacy.py213
-rw-r--r--tox.ini4
19 files changed, 342 insertions, 593 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index c3d529d..a14d4ea 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,9 +1,8 @@
recursive-include markdown *.py
recursive-include docs *
-recursive-include tests *.txt *.html *.cfg *.py
+recursive-include tests *.txt *.html *.py
include setup.py
include setup.cfg
-include run-tests.py
include tox.ini
include makefile
include LICENSE.md
diff --git a/makefile b/makefile
index 8cfd0ea..3eb7d48 100644
--- a/makefile
+++ b/makefile
@@ -11,7 +11,6 @@ help:
@echo ' build-win Build a Windows exe distribution'
@echo ' docs Build documentation'
@echo ' test Run all tests'
- @echo ' update-tests Generate html files for updated text files in tests'
@echo ' clean Clean up the source directories'
.PHONY : install
@@ -38,11 +37,8 @@ docs:
.PHONY : test
test:
- tox
-
-.PHONY : update-tests
-update-tests:
- python run-tests.py update
+ coverage run --source=markdown -m unittest discover tests
+ coverage report --show-missing
.PHONY : clean
clean:
diff --git a/markdown/test_tools.py b/markdown/test_tools.py
index cebb2bb..9324bd4 100644
--- a/markdown/test_tools.py
+++ b/markdown/test_tools.py
@@ -1,6 +1,16 @@
+from __future__ import absolute_import
+import os
+import io
import unittest
import textwrap
-from markdown import markdown
+from . import markdown
+
+try:
+ import tidylib
+except ImportError:
+ tidylib = None
+
+__all__ = ['TestCase', 'LegacyTestCase', 'Kwargs']
class TestCase(unittest.TestCase):
@@ -42,3 +52,116 @@ class TestCase(unittest.TestCase):
# TODO: If/when actual output ends with a newline, then use:
# return textwrap.dedent(text.strip('/n'))
return textwrap.dedent(text).strip()
+
+
+#########################
+# Legacy Test Framework #
+#########################
+
+
+class Kwargs(dict):
+ """ A dict like class for holding keyword arguments. """
+ pass
+
+
+def _normalize_whitespace(text):
+ """ Normalize whitespace for a string of html using tidylib. """
+ output, errors = tidylib.tidy_fragment(text, options={
+ 'drop_empty_paras': 0,
+ 'fix_backslash': 0,
+ 'fix_bad_comments': 0,
+ 'fix_uri': 0,
+ 'join_styles': 0,
+ 'lower_literals': 0,
+ 'merge_divs': 0,
+ 'output_xhtml': 1,
+ 'quote_ampersand': 0,
+ 'newline': 'LF'
+ })
+ return output
+
+
+class LegacyTestMeta(type):
+ def __new__(cls, name, bases, dct):
+
+ def generate_test(infile, outfile, normalize, kwargs):
+ def test(self):
+ with io.open(infile, encoding="utf-8") as f:
+ input = f.read()
+ with io.open(outfile, encoding="utf-8") as f:
+ # Normalize line endings
+ # (on Windows, git may have altered line endings).
+ expected = f.read().replace("\r\n", "\n")
+ output = markdown(input, **kwargs)
+ if tidylib and normalize:
+ expected = _normalize_whitespace(expected)
+ output = _normalize_whitespace(output)
+ elif normalize:
+ self.skipTest('Tidylib not available.')
+ self.assertMultiLineEqual(output, expected)
+ return test
+
+ location = dct.get('location', '')
+ exclude = dct.get('exclude', [])
+ normalize = dct.get('normalize', False)
+ input_ext = dct.get('input_ext', '.txt')
+ output_ext = dct.get('output_ext', '.html')
+ kwargs = dct.get('default_kwargs', Kwargs())
+
+ if os.path.isdir(location):
+ for file in os.listdir(location):
+ infile = os.path.join(location, file)
+ if os.path.isfile(infile):
+ tname, ext = os.path.splitext(file)
+ if ext == input_ext:
+ outfile = os.path.join(location, tname + output_ext)
+ tname = tname.replace(' ', '_').replace('-', '_')
+ kws = kwargs.copy()
+ if tname in dct:
+ kws.update(dct[tname])
+ test_name = 'test_%s' % tname
+ if tname not in exclude:
+ dct[test_name] = generate_test(infile, outfile, normalize, kws)
+ else:
+ dct[test_name] = unittest.skip('Excluded')(lambda: None)
+
+ return type.__new__(cls, name, bases, dct)
+
+
+# Define LegacyTestCase class with metaclass in Py2 & Py3 compatable way.
+# See https://stackoverflow.com/a/38668373/866026
+# TODO: If/when py2 support is dropped change to:
+# class LegacyTestCase(unittest.Testcase, metaclass=LegacyTestMeta)
+
+
+class LegacyTestCase(LegacyTestMeta('LegacyTestCase', (unittest.TestCase,), {'__slots__': ()})):
+ """
+ A `unittest.TestCase` subclass for running Markdown's legacy file-based tests.
+
+ A subclass should define various properties which point to a directory of
+ text-based test files and define various behaviors/defaults for those tests.
+ The following properties are supported:
+
+ location: A path to the directory fo test files. An absolute path is prefered.
+ exclude: A list of tests to exclude. Each test name should comprise the filename
+ without an extension.
+ normalize: A boolean value indicating if the HTML should be normalized.
+ Default: `False`.
+ input_ext: A string containing the file extension of input files. Default: `.txt`.
+ ouput_ext: A string containing the file extension of expected output files.
+ Default: `html`.
+ default_kwargs: A `Kwargs` instance which stores the default set of keyword
+ arguments for all test files in the directory.
+
+ In addition, properties can be defined for each individual set of test files within
+ the directory. The property should be given the name of the file wihtout the file
+ extension. Any spaces and dashes in the filename should be replaced with
+ underscores. The value of the property should be a `Kwargs` instance which
+ contains the keyword arguments that should be passed to `Markdown` for that
+ test file. The keyword arguments will "update" the `default_kwargs`.
+
+ When the class instance is created, it will walk the given directory and create
+ a seperate unitttest for each set of test files using the naming scheme:
+ `test_filename`. One unittest will be run for each set of input and output files.
+ """
+ pass
diff --git a/run-tests.py b/run-tests.py
deleted file mode 100755
index 5748953..0000000
--- a/run-tests.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python
-
-import tests
-import os
-import sys
-
-if len(sys.argv) > 1 and sys.argv[1] == "update":
- if len(sys.argv) > 2:
- config = tests.get_config(os.path.dirname(sys.argv[2]))
- root, ext = os.path.splitext(sys.argv[2])
- if ext == config.get(
- config.get_section(os.path.basename(root)), 'input_ext'
- ):
- tests.generate(root, config)
- else:
- print(
- sys.argv[2],
- 'does not have a valid file extension. Check config.'
- )
- else:
- tests.generate_all()
-else:
- tests.run()
diff --git a/setup.cfg b/setup.cfg
index 81482a9..5e17c83 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,3 @@
-[nosetests]
-
[bdist_wheel]
universal=1
diff --git a/test-requirements.txt b/test-requirements.txt
index 65bfae0..12244ab 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,4 +1,3 @@
-nose
coverage<4.0
pyyaml
pytidylib
diff --git a/tests/__init__.py b/tests/__init__.py
index 6826ff3..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,189 +0,0 @@
-import os
-import markdown
-import codecs
-import difflib
-import warnings
-try:
- import nose
-except ImportError as e:
- msg = e.args[0]
- msg = msg + ". The nose testing framework is required to run the Python-" \
- "Markdown tests. Run `pip install nose` to install the latest version."
- e.args = (msg,) + e.args[1:]
- raise
-from .plugins import HtmlOutput, Markdown, MarkdownSyntaxError
-try:
- import tidylib
-except ImportError:
- tidylib = None
-try:
- import yaml
-except ImportError as e:
- msg = e.args[0]
- msg = msg + ". A YAML library is required to run the Python-Markdown " \
- "tests. Run `pip install pyyaml` to install the latest version."
- e.args = (msg,) + e.args[1:]
- raise
-
-test_dir = os.path.abspath(os.path.dirname(__file__))
-
-
-class YamlConfig():
- def __init__(self, defaults, filename):
- """ Set defaults and load config file if it exists. """
- self.DEFAULT_SECTION = 'DEFAULT'
- self._defaults = defaults
- self._config = {}
- if os.path.exists(filename):
- with codecs.open(filename, encoding="utf-8") as f:
- self._config = yaml.load(f)
-
- def get(self, section, option):
- """ Get config value for given section and option key. """
- if section in self._config and option in self._config[section]:
- return self._config[section][option]
- return self._defaults[option]
-
- def get_section(self, file):
- """ Get name of config section for given file. """
- filename = os.path.basename(file)
- if filename in self._config:
- return filename
- else:
- return self.DEFAULT_SECTION
-
- def get_args(self, file):
- """ Get args to pass to markdown from config for a given file. """
- args = {}
- section = self.get_section(file)
- if section in self._config:
- for key in self._config[section].keys():
- # Filter out args unique to testing framework
- if key not in self._defaults.keys():
- args[key] = self.get(section, key)
- return args
-
-
-def get_config(dir_name):
- """ Get config for given directory name. """
- defaults = {
- 'normalize': False,
- 'skip': False,
- 'input_ext': '.txt',
- 'output_ext': '.html'
- }
- config = YamlConfig(defaults, os.path.join(dir_name, 'test.cfg'))
- return config
-
-
-def normalize(text):
- """ Normalize whitespace for a string of html using tidylib. """
- output, errors = tidylib.tidy_fragment(text, options={
- 'drop_empty_paras': 0,
- 'fix_backslash': 0,
- 'fix_bad_comments': 0,
- 'fix_uri': 0,
- 'join_styles': 0,
- 'lower_literals': 0,
- 'merge_divs': 0,
- 'output_xhtml': 1,
- 'quote_ampersand': 0,
- 'newline': 'LF'
- })
- return output
-
-
-class CheckSyntax(object):
- def __init__(self, description=None):
- if description:
- self.description = 'TestSyntax: "%s"' % description
-
- def __call__(self, file, config):
- """ Compare expected output to actual output and report result. """
- cfg_section = config.get_section(file)
- if config.get(cfg_section, 'skip'):
- raise nose.plugins.skip.SkipTest('Test skipped per config.')
- input_file = file + config.get(cfg_section, 'input_ext')
- with codecs.open(input_file, encoding="utf-8") as f:
- input = f.read()
- output_file = file + config.get(cfg_section, 'output_ext')
- with codecs.open(output_file, encoding="utf-8") as f:
- # Normalize line endings
- # (on windows, git may have altered line endings).
- expected_output = f.read().replace("\r\n", "\n")
- output = markdown.markdown(input, **config.get_args(file))
- if tidylib and config.get(cfg_section, 'normalize'):
- # Normalize whitespace with tidylib before comparing.
- expected_output = normalize(expected_output)
- output = normalize(output)
- elif config.get(cfg_section, 'normalize'):
- # Tidylib is not available. Skip this test.
- raise nose.plugins.skip.SkipTest(
- 'Test skipped. Tidylib not available on system.'
- )
- diff = [l for l in difflib.unified_diff(
- expected_output.splitlines(True),
- output.splitlines(True),
- output_file,
- 'actual_output.html',
- n=3
- )]
- if diff:
- raise MarkdownSyntaxError(
- 'Output from "%s" failed to match expected '
- 'output.\n\n%s' % (input_file, ''.join(diff))
- )
-
-
-def TestSyntax():
- for dir_name, sub_dirs, files in os.walk(test_dir):
- # Get dir specific config settings.
- config = get_config(dir_name)
- # Loop through files and generate tests.
- for file in files:
- root, ext = os.path.splitext(file)
- if ext == config.get(config.get_section(file), 'input_ext'):
- path = os.path.join(dir_name, root)
- check_syntax = CheckSyntax(
- description=os.path.relpath(path, test_dir)
- )
- yield check_syntax, path, config
-
-
-def generate(file, config):
- """ Write expected output file for given input. """
- cfg_section = config.get_section(file)
- if config.get(cfg_section, 'skip') or config.get(cfg_section, 'normalize'):
- print('Skipping:', file)
- return None
- input_file = file + config.get(cfg_section, 'input_ext')
- output_file = file + config.get(cfg_section, 'output_ext')
- if not os.path.isfile(output_file) or \
- os.path.getmtime(output_file) < os.path.getmtime(input_file):
- print('Generating:', file)
- markdown.markdownFromFile(input=input_file, output=output_file,
- encoding='utf-8', **config.get_args(file))
- else:
- print('Already up-to-date:', file)
-
-
-def generate_all():
- """ Generate expected output for all outdated tests. """
- for dir_name, sub_dirs, files in os.walk(test_dir):
- # Get dir specific config settings.
- config = get_config(dir_name)
- # Loop through files and generate tests.
- for file in files:
- root, ext = os.path.splitext(file)
- if ext == config.get(config.get_section(file), 'input_ext'):
- generate(os.path.join(dir_name, root), config)
-
-
-def run():
- # Warnings should cause tests to fail...
- warnings.simplefilter('error')
- # Except for the warnings that shouldn't
- warnings.filterwarnings('default', category=PendingDeprecationWarning)
- warnings.filterwarnings('default', category=DeprecationWarning, module='markdown')
-
- nose.main(addplugins=[HtmlOutput(), Markdown()])
diff --git a/tests/basic/benchmark.dat b/tests/basic/benchmark.dat
deleted file mode 100644
index 3d549dd..0000000
--- a/tests/basic/benchmark.dat
+++ /dev/null
@@ -1,20 +0,0 @@
-construction:0.000000:0.000000
-amps-and-angle-encoding:0.070000:131072.000000
-auto-links:0.080000:397312.000000
-backlash-escapes:0.270000:884736.000000
-blockquotes-with-dode-blocks:0.020000:0.000000
-hard-wrapped:0.020000:0.000000
-horizontal-rules:0.180000:135168.000000
-inline-html-advanced:0.070000:0.000000
-inline-html-comments:0.080000:0.000000
-inline-html-simple:0.210000:0.000000
-links-inline:0.140000:0.000000
-links-reference:0.170000:0.000000
-literal-quotes:0.090000:0.000000
-markdown-documentation-basics:0.690000:1806336.000000
-markdown-syntax:3.310000:6696960.000000
-nested-blockquotes:0.200000:0.000000
-ordered-and-unordered-list:0.530000:0.000000
-strong-and-em-together:0.200000:0.000000
-tabs:0.200000:0.000000
-tidyness:0.200000:0.000000
diff --git a/tests/extensions/extra/test.cfg b/tests/extensions/extra/test.cfg
deleted file mode 100644
index d956e2a..0000000
--- a/tests/extensions/extra/test.cfg
+++ /dev/null
@@ -1,36 +0,0 @@
-DEFAULT:
- extensions:
- - markdown.extensions.extra
-
-loose_def_list:
- extensions:
- - markdown.extensions.def_list
-
-simple_def-lists:
- extensions:
- - markdown.extensions.def_list
-
-abbr:
- extensions:
- - markdown.extensions.abbr
-
-footnotes:
- extensions:
- - markdown.extensions.footnotes
-
-tables:
- extensions:
- - markdown.extensions.tables
-
-tables_and_attr_list:
- extensions:
- - markdown.extensions.tables
- - markdown.extensions.attr_list
-
-extra_config:
- extensions:
- - markdown.extensions.extra
- extension_configs:
- markdown.extensions.extra:
- markdown.extensions.footnotes:
- PLACE_MARKER: ~~~placemarker~~~
diff --git a/tests/extensions/test.cfg b/tests/extensions/test.cfg
deleted file mode 100644
index ce66cfc..0000000
--- a/tests/extensions/test.cfg
+++ /dev/null
@@ -1,73 +0,0 @@
-attr_list:
- extensions:
- - markdown.extensions.attr_list
- - markdown.extensions.def_list
- - markdown.extensions.smarty
-
-codehilite:
- extensions:
- - markdown.extensions.codehilite
- # This passes or not based on version of pygments.
- skip: True
-
-toc:
- extensions:
- - markdown.extensions.toc
-
-toc_invalid:
- extensions:
- - markdown.extensions.toc
-
-toc_out_of_order:
- extensions:
- - markdown.extensions.toc
-
-toc_nested:
- extensions:
- - markdown.extensions.toc
- extension_configs:
- markdown.extensions.toc:
- permalink: True
-
-toc_nested2:
- extensions:
- - markdown.extensions.toc
- extension_configs:
- markdown.extensions.toc:
- permalink: "[link]"
-
-toc_nested_list:
- extensions:
- - markdown.extensions.toc
-
-wikilinks:
- extensions:
- - markdown.extensions.wikilinks
-
-fenced_code:
- extensions:
- - markdown.extensions.fenced_code
-
-github_flavored:
- extensions:
- - markdown.extensions.fenced_code
-
-sane_lists:
- extensions:
- - markdown.extensions.sane_lists
-
-nl2br_w_attr_list:
- extensions:
- - markdown.extensions.nl2br
- - markdown.extensions.attr_list
-
-admonition:
- extensions:
- - markdown.extensions.admonition
-
-smarty:
- extensions:
- - markdown.extensions.smarty
- extension_configs:
- markdown.extensions.smarty:
- smart_angled_quotes: True \ No newline at end of file
diff --git a/tests/options/test.cfg b/tests/options/test.cfg
deleted file mode 100644
index 2e14f1f..0000000
--- a/tests/options/test.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-lazy_ol_off:
- lazy_ol: False
-
-html4:
- output_format: html4
-
-no-attributes:
- enable_attributes: False
-
-no-smart-emphasis:
- smart_emphasis: False \ No newline at end of file
diff --git a/tests/php/extra/test.cfg b/tests/php/extra/test.cfg
deleted file mode 100644
index c6011d6..0000000
--- a/tests/php/extra/test.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-DEFAULT:
- extensions:
- - extra
- normalize: True
- input_ext: .text
- output_ext: .xhtml
- skip: True
diff --git a/tests/php/test.cfg b/tests/php/test.cfg
deleted file mode 100644
index 70c2601..0000000
--- a/tests/php/test.cfg
+++ /dev/null
@@ -1,50 +0,0 @@
-DEFAULT:
- normalize: True
- input_ext: .text
- output_ext: .xhtml
- #skip: True
-
-Quotes in attributes:
- # attributes get output in differant order
- skip: True
-
-Inline HTML (Span):
- # Backtick in raw HTML attribute TODO: fixme
- skip: True
-
-Backslash escapes:
- # Weird whitespace issue in output
- skip: True
-
-Ins & del:
- # Our behavior follows markdown.pl I think PHP is wrong here
- skip: True
-
-Auto Links:
- # TODO: fix raw HTML so is doesn't match <hr@example.com> as a <hr>.
- skip: True
-
-Empty List Item:
- # We match markdown.pl here. Maybe someday we'll support this
- skip: True
-
-Headers:
- # TODO: fix headers to not require blank line before
- skip: True
-
-Mixed OLs and ULs:
- # We match markdown.pl here. I think PHP is wrong here
- skip: True
-
-Emphasis:
- # We have various minor differances in combined & incorrect em markup.
- # Maybe fix a few of them - but most aren't too important
- skip: True
-
-Code block in a list item:
- # We match markdown.pl - not sure how php gets that output??
- skip: True
-
-PHP-Specific Bugs:
- # Not sure what to make of the escaping stuff here. Why is PHP not removing a blackslash?
- skip: True
diff --git a/tests/pl/Tests_2004/test.cfg b/tests/pl/Tests_2004/test.cfg
deleted file mode 100644
index 80f48e5..0000000
--- a/tests/pl/Tests_2004/test.cfg
+++ /dev/null
@@ -1,10 +0,0 @@
-DEFAULT:
- input_ext: .text
- normalize: True
- # comment out next line to run these tests
- #skip: True
-
-Yuri-Footnotes:
- extensions: footnotes
- skip: True
-
diff --git a/tests/pl/Tests_2007/test.cfg b/tests/pl/Tests_2007/test.cfg
deleted file mode 100644
index 097aa0d..0000000
--- a/tests/pl/Tests_2007/test.cfg
+++ /dev/null
@@ -1,25 +0,0 @@
-DEFAULT:
- input_ext: .text
- normalize: True
- # comment out next line to run these tests
- #skip: True
-
-Images:
- # the attributes don't get ordered the same so we skip this
- skip: True
-
-Code Blocks:
- # some weird whitespace issue
- skip: True
-
-Links, reference style:
- # weird issue with nested brackets TODO: fixme
- skip: True
-
-Backslash escapes:
- # backticks in raw html attributes TODO: fixme
- skip: True
-
-Code Spans:
- # more backticks in raw html attributes TODO: fixme
- skip: True
diff --git a/tests/plugins.py b/tests/plugins.py
deleted file mode 100644
index 4e7af97..0000000
--- a/tests/plugins.py
+++ /dev/null
@@ -1,127 +0,0 @@
-import traceback
-from nose.plugins import Plugin
-from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin
-
-
-class MarkdownSyntaxError(Exception):
- pass
-
-
-class Markdown(ErrorClassPlugin):
- """ Add MarkdownSyntaxError and ensure proper formatting. """
- mdsyntax = ErrorClass(
- MarkdownSyntaxError,
- label='MarkdownSyntaxError',
- isfailure=True
- )
- enabled = True
-
- def configure(self, options, conf):
- self.conf = conf
-
- def addError(self, test, err):
- """ Ensure other plugins see the error by returning nothing here. """
- pass
-
- def formatError(self, test, err):
- """ Remove unnessecary and unhelpful traceback from error report. """
- et, ev, tb = err
- if et.__name__ == 'MarkdownSyntaxError':
- return et, ev, ''
- return err
-
-
-def escape(html):
- """ Escape HTML for display as source within HTML. """
- html = html.replace('&', '&amp;')
- html = html.replace('<', '&lt;')
- html = html.replace('>', '&gt;')
- return html
-
-
-class HtmlOutput(Plugin):
- """Output test results as ugly, unstyled html. """
-
- name = 'html-output'
- score = 2 # run late
- enabled = True
-
- def __init__(self):
- super(HtmlOutput, self).__init__()
- self.html = [
- '<html><head>',
- '<title>Test output</title>',
- '</head><body>'
- ]
-
- def configure(self, options, conf):
- self.conf = conf
-
- def addSuccess(self, test):
- self.html.append('<span>ok</span>')
-
- def addError(self, test, err):
- err = self.formatErr(err)
- self.html.append('<span>ERROR</span>')
- self.html.append('<pre>%s</pre>' % escape(err))
-
- def addFailure(self, test, err):
- err = self.formatErr(err)
- self.html.append('<span>FAIL</span>')
- self.html.append('<pre>%s</pre>' % escape(err))
-
- def finalize(self, result):
- self.html.append('<div>')
- self.html.append(
- "Ran %d test%s" %
- (result.testsRun, result.testsRun != 1 and "s" or "")
- )
- self.html.append('</div>')
- self.html.append('<div>')
- if not result.wasSuccessful():
- self.html.extend(['<span>FAILED (',
- 'failures=%d ' % len(result.failures),
- 'errors=%d' % len(result.errors)])
- for cls in list(result.errorClasses.keys()):
- storage, label, isfail = result.errorClasses[cls]
- if len(storage):
- self.html.append(' %ss=%d' % (label, len(storage)))
- self.html.append(')</span>')
- else:
- self.html.append('OK')
- self.html.append('</div></body></html>')
- f = open('test-output.html', 'w')
- for l in self.html:
- f.write(l)
- f.close()
-
- def formatErr(self, err):
- exctype, value, tb = err
- if not isinstance(value, exctype):
- value = exctype(value)
- return ''.join(traceback.format_exception(exctype, value, tb))
-
- def startContext(self, ctx):
- try:
- n = ctx.__name__
- except AttributeError:
- n = str(ctx).replace('<', '').replace('>', '')
- self.html.extend(['<fieldset>', '<legend>', n, '</legend>'])
- try:
- path = ctx.__file__.replace('.pyc', '.py')
- self.html.extend(['<div>', path, '</div>'])
- except AttributeError:
- pass
-
- def stopContext(self, ctx):
- self.html.append('</fieldset>')
-
- def startTest(self, test):
- self.html.extend([
- '<div><span>',
- test.shortDescription() or str(test),
- '</span>'
- ])
-
- def stopTest(self, test):
- self.html.append('</div>')
diff --git a/tests/safe_mode/test.cfg b/tests/safe_mode/test.cfg
deleted file mode 100644
index 4e1720e..0000000
--- a/tests/safe_mode/test.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-DEFAULT:
- safe_mode: escape
-
-remove:
- safe_mode: remove
-
-replace:
- safe_mode: replace
diff --git a/tests/test_legacy.py b/tests/test_legacy.py
new file mode 100644
index 0000000..17c4282
--- /dev/null
+++ b/tests/test_legacy.py
@@ -0,0 +1,213 @@
+from markdown.test_tools import LegacyTestCase, Kwargs
+import os
+import warnings
+
+# Warnings should cause tests to fail...
+warnings.simplefilter('error')
+# Except for the warnings that shouldn't
+warnings.filterwarnings('default', category=PendingDeprecationWarning)
+warnings.filterwarnings('default', category=DeprecationWarning, module='markdown')
+
+parent_test_dir = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestBasic(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'basic')
+
+
+class TestMisc(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'misc')
+
+
+class TestOptions(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'options')
+
+ lazy_ol_off = Kwargs(lazy_ol=False)
+
+ html4 = Kwargs(output_format='html4')
+
+ no_attributes = Kwargs(enable_attributes=False)
+
+ no_smart_emphasis = Kwargs(smart_emphasis=False)
+
+
+class TestSafeMode(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'safe_mode')
+ default_kwargs = Kwargs(safe_mode='escape')
+
+ remove = Kwargs(safe_mode='remove')
+
+ replace = Kwargs(safe_mode='replace')
+
+
+class TestPhp(LegacyTestCase):
+ """
+ Notes on "excluded" tests:
+
+ Quotes in attributes: attributes get output in differant order
+
+ Inline HTML (Span): Backtick in raw HTML attribute TODO: fixme
+
+ Backslash escapes: Weird whitespace issue in output
+
+ Ins & del: Our behavior follows markdown.pl I think PHP is wrong here
+
+ Auto Links: TODO: fix raw HTML so is doesn't match <hr@example.com> as a <hr>.
+
+ Empty List Item: We match markdown.pl here. Maybe someday we'll support this
+
+ Headers: TODO: fix headers to not require blank line before
+
+ Mixed OLs and ULs: We match markdown.pl here. I think PHP is wrong here
+
+ Emphasis: We have various minor differances in combined & incorrect em markup.
+ Maybe fix a few of them - but most aren't too important
+
+ Code block in a list item: We match markdown.pl - not sure how php gets that output??
+
+ PHP-Specific Bugs: Not sure what to make of the escaping stuff here.
+ Why is PHP not removing a blackslash?
+ """
+ location = os.path.join(parent_test_dir, 'php')
+ normalize = True
+ input_ext = '.text'
+ output_ext = '.xhtml'
+ exclude = [
+ 'Quotes_in_attributes',
+ 'Inline_HTML_(Span)',
+ 'Backslash_escapes',
+ 'Ins_&_del',
+ 'Auto_Links',
+ 'Empty_List_Item',
+ 'Headers',
+ 'Mixed_OLs_and_ULs',
+ 'Emphasis',
+ 'Code_block_in_a_list_item',
+ 'PHP_Specific_Bugs'
+ ]
+
+
+# class TestPhpExtra(LegacyTestCase):
+# location = os.path.join(parent_test_dir, 'php/extra')
+# normalize = True
+# input_ext = '.text'
+# output_ext = '.xhtml'
+# default_kwargs = Kwargs(extensions=['extra'])
+
+
+class TestPl2004(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'pl/Tests_2004')
+ normalize = True
+ input_ext = '.text'
+ exclude = ['Yuri_Footnotes']
+
+
+class TestPl2007(LegacyTestCase):
+ """
+ Notes on "excluded" tests:
+
+ Images: the attributes don't get ordered the same so we skip this
+
+ Code Blocks: some weird whitespace issue
+
+ Links, reference style: weird issue with nested brackets TODO: fixme
+
+ Backslash escapes: backticks in raw html attributes TODO: fixme
+
+ Code Spans: more backticks in raw html attributes TODO: fixme
+ """
+ location = os.path.join(parent_test_dir, 'pl/Tests_2007')
+ normalize = True
+ input_ext = '.text'
+ exclude = [
+ 'Images',
+ 'Code_Blocks',
+ 'Links,_reference_style',
+ 'Backslash_escapes',
+ 'Code_Spans'
+ ]
+
+
+class TestExtensions(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'extensions')
+ exclude = ['codehilite']
+
+ attr_list = Kwargs(
+ extensions=[
+ 'markdown.extensions.attr_list',
+ 'markdown.extensions.def_list',
+ 'markdown.extensions.smarty'
+ ]
+ )
+
+ codehilite = Kwargs(extensions=['markdown.extensions.codehilite'])
+
+ toc = Kwargs(extensions=['markdown.extensions.toc'])
+
+ toc_invalid = Kwargs(extensions=['markdown.extensions.toc'])
+
+ toc_out_of_order = Kwargs(extensions=['markdown.extensions.toc'])
+
+ toc_nested = Kwargs(
+ extensions=['markdown.extensions.toc'],
+ extension_configs={'markdown.extensions.toc': {'permalink': True}}
+ )
+
+ toc_nested2 = Kwargs(
+ extensions=['markdown.extensions.toc'],
+ extension_configs={'markdown.extensions.toc': {'permalink': "[link]"}}
+ )
+
+ toc_nested_list = Kwargs(extensions=['markdown.extensions.toc'])
+
+ wikilinks = Kwargs(extensions=['markdown.extensions.wikilinks'])
+
+ fenced_code = Kwargs(extensions=['markdown.extensions.fenced_code'])
+
+ github_flavored = Kwargs(extensions=['markdown.extensions.fenced_code'])
+
+ sane_lists = Kwargs(extensions=['markdown.extensions.sane_lists'])
+
+ nl2br_w_attr_list = Kwargs(
+ extensions=[
+ 'markdown.extensions.nl2br',
+ 'markdown.extensions.attr_list'
+ ]
+ )
+
+ admonition = Kwargs(extensions=['markdown.extensions.admonition'])
+
+ smarty = Kwargs(
+ extensions=['markdown.extensions.smarty'],
+ extension_configs={'markdown.extensions.smarty': {'smart_angled_quotes': True}}
+ )
+
+
+class TestExtensionsExtra(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'extensions/extra')
+ default_kwargs = Kwargs(extensions=['markdown.extensions.extra'])
+
+ loose_def_list = Kwargs(extensions=['markdown.extensions.def_list'])
+
+ simple_def_lists = Kwargs(extensions=['markdown.extensions.def_list'])
+
+ abbr = Kwargs(extensions=['markdown.extensions.abbr'])
+
+ footnotes = Kwargs(extensions=['markdown.extensions.footnotes'])
+
+ tables = Kwargs(extensions=['markdown.extensions.tables'])
+
+ tables_and_attr_list = Kwargs(
+ extensions=['markdown.extensions.tables', 'markdown.extensions.attr_list']
+ )
+
+ extra_config = Kwargs(
+ extensions=['markdown.extensions.extra'],
+ extension_configs={
+ 'markdown.extensions.extra': {
+ 'markdown.extensions.footnotes': {
+ 'PLACE_MARKER': '~~~placemarker~~~'
+ }
+ }
+ }
+ )
diff --git a/tox.ini b/tox.ini
index 89cc397..7a415bf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,12 +3,12 @@ envlist = py27, py32, py33, py34, py35, py36, pypy, flake8, checkspelling
[testenv]
deps = -rtest-requirements.txt
-commands = coverage run --source=markdown {toxinidir}/run-tests.py {posargs}
+commands = coverage run --source=markdown -m unittest discover {toxinidir}/tests
coverage report --show-missing
[testenv:flake8]
deps = flake8
-commands = flake8 {toxinidir}/markdown {toxinidir}/tests {toxinidir}/setup.py {toxinidir}/run-tests.py
+commands = flake8 {toxinidir}/markdown {toxinidir}/tests {toxinidir}/setup.py
[testenv:checkspelling]
deps =