summaryrefslogtreecommitdiff
path: root/paper/lua-filters/minted/run_minted_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'paper/lua-filters/minted/run_minted_tests.py')
-rwxr-xr-xpaper/lua-filters/minted/run_minted_tests.py522
1 files changed, 522 insertions, 0 deletions
diff --git a/paper/lua-filters/minted/run_minted_tests.py b/paper/lua-filters/minted/run_minted_tests.py
new file mode 100755
index 0000000..15803da
--- /dev/null
+++ b/paper/lua-filters/minted/run_minted_tests.py
@@ -0,0 +1,522 @@
+#!/usr/bin/env python
+
+"""
+Unit tests for the pandoc minted.lua filter.
+"""
+
+# Lint this file with: flake8 --max-line-length=80
+import os
+import string
+import subprocess
+import sys
+import textwrap
+
+code_block = textwrap.dedent('''
+ ## A Code Block
+
+ ```{.cpp}
+ auto mult = []<typename T, typename U>(T const & x, U const & y) {
+ return x * y;
+ };
+ ```
+''')
+"""
+The base CodeBlock code. {.cpp} is used as a replacement marker in most tests!
+"""
+
+inline_delims = '|!@#^&*-=+' + string.digits + string.ascii_letters
+inline_code = textwrap.dedent('''
+ ## Inline Code
+
+ `#include <type_traits>`{.cpp}
+ C and C++ use `{` and `}` to delimit scopes.
+ Some other special characters:
+ These check bypass: `~!@#$%^&*()-=_+[]\\{}|;\':",./<>?`
+ These check regular inline: ''' + ' '.join(
+ '`{' + inline_delims[:i] + '`' for i in range(len(inline_delims))
+))
+"""
+The base Code code. {.cpp} is used as a replacement marker in most tests!
+"""
+
+
+def run_pandoc(pandoc_args, stdin):
+ """Run pandoc with the specified arguments, returning the output."""
+ # The input / output should be small enough for these tests that buffer
+ # overflows should not happen.
+ pandoc_proc = subprocess.Popen(
+ ["pandoc"] + pandoc_args,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE
+ )
+
+ # Python 3.x and later require communicating with bytes.
+ if sys.version_info[0] >= 3:
+ stdin = bytes(stdin, "utf-8")
+
+ stdout, stderr = pandoc_proc.communicate(input=stdin)
+ if pandoc_proc.returncode != 0:
+ sys.stderr.write("Non-zero exit code of {ret} from pandoc!\n".format(
+ ret=pandoc_proc.returncode
+ ))
+ sys.stderr.write("pandoc stderr: {stderr}".format(
+ stderr=stderr.decode("utf-8")
+ ))
+ sys.exit(1)
+
+ return stdout.decode("utf-8")
+
+
+def fail_test(test_name, messages, ansi_color_code="31"):
+ """
+ Print failure message and ``sys.exit(1)``.
+
+ ``test_name`` (str)
+ The name of the test (to make finding in code easier).
+
+ ``messages`` (list of str -- or -- str)
+ A single string, or list of strings, to print out to ``stderr`` that
+ explain the reason for the test failure.
+
+ ``ansi_color_code`` (str)
+ A an ANSI color code to use to colorize the failure message :) Default
+ is ``"31"``, which is red.
+ """
+ sys.stderr.write(
+ "\033[0;{ansi_color_code}mTest {test_name} FAILED\033[0m\n".format(
+ ansi_color_code=ansi_color_code, test_name=test_name
+ )
+ )
+ if isinstance(messages, list):
+ for m in messages:
+ sys.stderr.write("--> {m}\n".format(m=m))
+ else:
+ sys.stderr.write("--> {messages}\n".format(messages=messages))
+ sys.exit(1)
+
+
+def ensure_fragile(test_name, pandoc_output):
+ r"""
+ Ensure that every \begin{frame} has (at least one) fragile.
+
+ ``test_name`` (str)
+ The name of the test (forwards to ``fail_test``).
+
+ ``pandoc_output`` (str)
+ The pandoc output for the test case.
+ """
+ for line in pandoc_output.splitlines():
+ if r"\begin{frame}" in line:
+ if "fragile" not in line:
+ fail_test(
+ test_name,
+ r"\begin{frame} without 'fragile': {line}".format(line=line)
+ )
+
+
+def ensure_present(test_name, string, pandoc_output):
+ """
+ Assert that ``string`` is found in ``pandoc_output``.
+
+ ``test_name`` (str)
+ The name of the test (forwards to ``fail_test``).
+
+ ``string`` (str)
+ The string to check verbatim ``string in pandoc_output``.
+
+ ``pandoc_output`` (str)
+ The pandoc output for the test case.
+ """
+ if string not in pandoc_output:
+ fail_test(
+ test_name,
+ "The requested string '{string}' was not found in:\n{pout}".format(
+ string=string, pout=pandoc_output
+ )
+ )
+
+
+def ensure_not_present(test_name, string, pandoc_output):
+ """
+ Assert that ``string`` is **not** found in ``pandoc_output``.
+
+ ``test_name`` (str)
+ The name of the test (forwards to ``fail_test``).
+
+ ``string`` (str)
+ The string to check verbatim ``string not in pandoc_output``.
+
+ ``pandoc_output`` (str)
+ The pandoc output for the test case.
+ """
+ if string in pandoc_output:
+ fail_test(
+ test_name,
+ "The forbidden string '{string}' was found in:\n{pout}".format(
+ string=string, pout=pandoc_output
+ )
+ )
+
+
+def run_tex_tests(pandoc_args, fmt):
+ """
+ Run same tests for latex writers.
+
+ ``pandoc_args`` (list of str)
+ The base list of arguments to forward to pandoc. Some tests may remove
+ the ``--no-highlight`` flag to validate whether or not pandoc
+ highlighting macros appear as expected (or not at all).
+
+ ``fmt`` (str)
+ The format is assumed to be either 'latex' or 'beamer'.
+ """
+ def verify(test_name, args, md, *strings):
+ """Run pandoc, ensure fragile, and string in output."""
+ output = run_pandoc(args + ["-t", fmt], md)
+ if fmt == "beamer":
+ ensure_fragile(test_name, output)
+ else: # latex writer
+ ensure_not_present(test_name, "fragile", output)
+ for s in strings:
+ ensure_present(test_name, s, output)
+ # Make sure the pandoc highlighting is not being used
+ if "--no-highlight" in args:
+ ensure_not_present(test_name, r"\VERB", output)
+ # if `nil` is present, that likely means a problem parsing the metadata
+ ensure_not_present(test_name, "nil", output)
+
+ ############################################################################
+ # CodeBlock tests. #
+ ############################################################################
+ begin_minted = r"\begin{{minted}}[{attrs}]{{{lang}}}"
+ verify(
+ "[code-block] default",
+ pandoc_args,
+ code_block,
+ begin_minted.format(attrs="autogobble", lang="cpp")
+ )
+ verify(
+ "[code-block] no_default_autogobble",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ no_default_autogobble: true
+ ---
+ {code_block}
+ ''').format(code_block=code_block),
+ begin_minted.format(attrs="", lang="cpp")
+ )
+ verify(
+ "[code-block] default block language is 'text'",
+ pandoc_args,
+ code_block.replace("{.cpp}", ""),
+ begin_minted.format(attrs="autogobble", lang="text")
+ )
+ verify(
+ "[code-block] user provided default_block_language",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ default_block_language: "haskell"
+ ---
+ {code_block}
+ ''').format(code_block=code_block.replace("{.cpp}", "")),
+ begin_minted.format(attrs="autogobble", lang="haskell")
+ )
+ verify(
+ "[code-block] user provided block_attributes",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ block_attributes:
+ - "showspaces"
+ - "space=."
+ ---
+ {code_block}
+ ''').format(code_block=code_block),
+ begin_minted.format(
+ attrs=",".join(["showspaces", "space=.", "autogobble"]),
+ lang="cpp"
+ )
+ )
+ verify(
+ "[code-block] user provided block_attributes and no_default_autogobble",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ no_default_autogobble: true
+ block_attributes:
+ - "style=monokai"
+ - "bgcolor=monokai_bg"
+ ---
+ {code_block}
+ ''').format(code_block=code_block),
+ begin_minted.format(
+ attrs=",".join(["style=monokai", "bgcolor=monokai_bg"]), lang="cpp"
+ )
+ )
+ verify(
+ "[code-block] attributes on code block",
+ pandoc_args,
+ code_block.replace(
+ "{.cpp}", "{.cpp .showspaces bgcolor=tango_bg style=tango}"
+ ),
+ begin_minted.format(
+ attrs=",".join([
+ "showspaces", "bgcolor=tango_bg", "style=tango", "autogobble"
+ ]),
+ lang="cpp"
+ )
+ )
+ verify(
+ "[code-block] attributes on code block + user block_attributes",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ block_attributes:
+ - "showspaces"
+ - "space=."
+ ---
+ {code_block}
+ ''').format(
+ code_block=code_block.replace(
+ "{.cpp}", "{.cpp bgcolor=tango_bg style=tango}"
+ )
+ ),
+ begin_minted.format(
+ attrs=",".join([
+ "bgcolor=tango_bg",
+ "style=tango",
+ "showspaces",
+ "space=.",
+ "autogobble"
+ ]),
+ lang="cpp"
+ )
+ )
+ verify(
+ "[code-block] traditional fenced code block",
+ pandoc_args,
+ code_block.replace("{.cpp}", "cpp"),
+ begin_minted.format(attrs="autogobble", lang="cpp")
+ )
+ verify(
+ "[code-block] non-minted attributes not forwarded",
+ pandoc_args,
+ code_block.replace("{.cpp}", "{.cpp .showspaces .hello}"),
+ begin_minted.format(
+ attrs=",".join(["showspaces", "autogobble"]), lang="cpp"
+ )
+ )
+
+ ############################################################################
+ # Inline Code tests. #
+ ############################################################################
+ mintinline = r"\mintinline[{attrs}]{{{lang}}}"
+ verify(
+ "[inline-code] default",
+ pandoc_args,
+ inline_code,
+ mintinline.format(attrs="", lang="cpp"),
+ "|{|",
+ "|}|",
+ *[
+ delim + '{' + inline_delims[:i] + delim
+ for i, delim in enumerate(inline_delims)
+ ]
+ )
+ verify(
+ "[inline-code] default language is text",
+ pandoc_args,
+ inline_code,
+ mintinline.format(attrs="", lang="text"),
+ "|{|",
+ "|}|"
+ )
+ # begin: global no_mintinline shared testing with / without --no-highlight
+ inline_no_mintinline_globally_md = textwrap.dedent('''
+ ---
+ minted:
+ no_mintinline: true
+ ---
+ {inline_code}
+ ''').format(inline_code=inline_code)
+ inline_no_mintinline_globally_strings = [
+ r"\texttt{\{}",
+ r"\texttt{\}}",
+ (r"\texttt{" +
+ r"\textasciitilde{}!@\#\$\%\^{}\&*()-=\_+{[}{]}\textbackslash{}\{\}" +
+ r"""\textbar{};\textquotesingle{}:",./\textless{}\textgreater{}?}""")
+ ]
+ verify(
+ "[inline-code] no_mintinline off globally",
+ pandoc_args,
+ inline_no_mintinline_globally_md,
+ r"\texttt{\#include\ \textless{}type\_traits\textgreater{}}",
+ *inline_no_mintinline_globally_strings
+ )
+ verify(
+ "[inline-code] no_mintinline off globally, remove --no-highlight",
+ [arg for arg in pandoc_args if arg != "--no-highlight"],
+ inline_no_mintinline_globally_md,
+ r"\VERB|\PreprocessorTok{#include }\ImportTok{<type_traits>}|",
+ *inline_no_mintinline_globally_strings
+ )
+ # end: global no_mintinline shared testing with / without --no-highlight
+ # begin: no_minted shared testing with / without --no-highlight
+ inline_no_minted_md = inline_code.replace("{.cpp}", "{.cpp .no_minted}")
+ inline_no_minted_strings = ["|{|", "|}|"]
+ verify(
+ "[inline-code] .no_minted on single inline Code",
+ pandoc_args,
+ inline_no_minted_md,
+ r"texttt{\#include\ \textless{}type\_traits\textgreater{}}",
+ *inline_no_minted_strings
+ )
+ verify(
+ "[inline-code] .no_minted on single inline Code, remove --no-highlight",
+ [arg for arg in pandoc_args if arg != "--no-highlight"],
+ inline_no_minted_md,
+ r"\VERB|\PreprocessorTok{#include }\ImportTok{<type_traits>}|",
+ *inline_no_minted_strings
+ )
+ # end: no_minted shared testing with / without --no-highlight
+ verify(
+ "[inline-code] user provided default_inline_language",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ default_inline_language: "haskell"
+ ---
+ {inline_code}
+ ''').format(inline_code=inline_code),
+ mintinline.format(attrs="", lang="haskell")
+ )
+ verify(
+ "[inline-code] user provided inline_attributes",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ inline_attributes:
+ - "showspaces"
+ - "space=."
+ ---
+ {inline_code}
+ ''').format(inline_code=inline_code),
+ mintinline.format(
+ attrs=",".join(["showspaces", "space=."]), lang="cpp"
+ ),
+ mintinline.format(
+ attrs=",".join(["showspaces", "space=."]), lang="text"
+ )
+ )
+ verify(
+ "[inline-code] attributes on inline code",
+ pandoc_args,
+ inline_code.replace(
+ "{.cpp}", "{.cpp .showspaces bgcolor=tango_bg style=tango}"
+ ),
+ mintinline.format(
+ attrs=",".join(["showspaces", "bgcolor=tango_bg", "style=tango"]),
+ lang="cpp"
+ )
+ )
+ verify(
+ "[inline-code] attributes on inline code + user inline_attributes",
+ pandoc_args,
+ textwrap.dedent('''
+ ---
+ minted:
+ inline_attributes:
+ - "showspaces"
+ - "space=."
+ ---
+ {inline_code}
+ ''').format(
+ inline_code=inline_code.replace(
+ "{.cpp}", "{.cpp bgcolor=tango_bg style=tango}"
+ )
+ ),
+ mintinline.format(
+ attrs=",".join([
+ "bgcolor=tango_bg",
+ "style=tango",
+ "showspaces",
+ "space=."
+ ]),
+ lang="cpp"
+ )
+ )
+ verify(
+ "[inline-code] non-minted attributes not forwarded",
+ pandoc_args,
+ inline_code.replace("{.cpp}", "{.cpp .showspaces .hello}"),
+ mintinline.format(attrs="showspaces", lang="cpp")
+ )
+
+
+def run_html_tests(args):
+ """
+ Run tests with an html5 writer to make sure minted commands are not used.
+ Also make sure minted specific attributes are indeed stripped.
+
+ ``args`` (list of str)
+ The base list of arguments to forward to pandoc.
+ """
+ def verify(test_name, md, attrs=[]):
+ """Verify minted and any strings in attrs not produced"""
+ output = run_pandoc(args + ["-t", "html5"], md)
+ ensure_not_present(test_name, "mint", output)
+ ensure_not_present(test_name, "fragile", output)
+ if attrs:
+ for a in attrs:
+ ensure_not_present(test_name, a, output)
+ # if `nil` is present, that likely means a problem parsing the metadata
+ ensure_not_present(test_name, "nil", output)
+
+ verify(r"[html] no \begin{minted}", code_block)
+ verify(r"[html] no \mintinline", inline_code)
+ verify(
+ r"[html] no \begin{minted} or \mintinline",
+ "{code_block}\n\n{inline_code}".format(
+ code_block=code_block, inline_code=inline_code
+ )
+ )
+ verify(
+ "[html] code block minted specific attributes stripped",
+ code_block.replace(
+ "{.cpp}",
+ "{.cpp .showspaces space=. bgcolor=minted_bg style=minted}"
+ ),
+ ["showspaces", "space", "bgcolor", "style"]
+ )
+ verify(
+ "[html] inline code minted specific attributes stripped",
+ inline_code.replace(
+ "{.cpp}",
+ "{.cpp .showspaces space=. bgcolor=minted_bg style=minted}"
+ ),
+ ["showspaces", "space", "bgcolor", "style"]
+ )
+
+
+if __name__ == "__main__":
+ # Initial path setup for input tests and lua filter
+ this_file_dir = os.path.abspath(os.path.dirname(__file__))
+ minted_lua = os.path.join(this_file_dir, "minted.lua")
+ if not os.path.isfile(minted_lua):
+ sys.stderr.write("Cannot find '{minted_lua}'...".format(
+ minted_lua=minted_lua
+ ))
+ sys.exit(1)
+
+ args = ["--fail-if-warnings", "--no-highlight", "--lua-filter", minted_lua]
+ run_tex_tests(args, "beamer")
+ run_tex_tests(args, "latex")
+ run_html_tests(args)