Files
Alessandro Di Federico 289c6d4c04 tmp
2022-11-15 16:46:42 +01:00

175 lines
4.7 KiB
Python

#
# This file is distributed under the MIT License. See LICENSE.md for details.
#
from pathlib import Path
from revng.cli.commands_registry import Command, Options
from revng.cli.revng import run_revng_command
import json
import os
import marko
import sys
import argparse
import subprocess
from tempfile import NamedTemporaryFile
from collections import defaultdict
_verbose = False
def log(message):
if _verbose:
sys.stderr.write(message + "\n")
def only(list):
assert len(list) == 1
return list[0]
class PythonDoctest:
def __init__(self):
self.extension = ".py"
def command(self, path):
return ["python", "-c", "import doctest; import sys; doctest.testfile(sys.argv[-1])", path]
def process(self, example):
return example + "\n"
class BashDoctest:
def __init__(self):
self.extension = ".sh"
def command(self, path):
return ["docshtest", path]
def process(self, example):
return " " + example.replace("\n", "\n ") + "\n"
def escape_js(string):
assert type(string) is str
return json.dumps(string)
def emit_assertion(expected):
return f"if (JSON.stringify(last) !== {escape_js(expected[:-1])})\n process.exit(1);\n"
# WIP
class TypeScriptDoctest:
def __init__(self):
self.extension = ".ts"
def command(self, path):
return ["bash", "-c", f"""./node_modules/.bin/tsc "{path}" && node "{path.replace(".ts", ".js")}" && rm "{path.replace(".ts", ".js")}" """]
def process(self, example):
output = "console.log = (x) => { return x; };\nlet last;\n"
last_output = ""
lines = list(map(str.strip, example.split("\n")))[:-1]
for line, next_line in zip(lines, lines[1:] + ["> "]):
is_command = line.startswith("> ")
next_is_command = next_line.startswith("> ")
if is_command:
if last_output:
output += emit_assertion(last_output)
last_output = ""
line = line[2:]
if next_is_command:
output += f"{line}\n"
else:
output += f"last = {line}\n"
else:
last_output += line + "\n"
if last_output:
output += emit_assertion(last_output)
return output
def run(arguments):
log(f"Running {str(arguments)}")
return subprocess.run(arguments, check=True)
class TestDocsCommand(Command):
def __init__(self):
super().__init__(("test-docs",), "revng documentation tester")
def register_arguments(self, parser):
parser.add_argument(
"--verbose", action="store_true", help="Be verbose."
)
parser.add_argument("files",
metavar="FILE",
nargs="*",
help="files to test.")
def run(self, options: Options):
args = options.parsed_args
global _verbose
_verbose = options.verbose
if args.files:
files = [Path(path) for path in args.files]
else:
files = []
for search_prefix in options.search_prefixes:
docs_path = Path(search_prefix) / "share" / "doc" / "revng"
reading_order_path = docs_path / "READING-ORDER"
if reading_order_path.exists():
files += [docs_path / docfile_path
for docfile_path
in reading_order_path.read_text().split("\n")
if docfile_path]
handlers = {
"python": PythonDoctest(),
"bash": BashDoctest(),
# "typescript": TypeScriptDoctest()
}
scripts = defaultdict(str)
for path in files:
document = marko.parse(path.read_text())
for block in document.children:
if type(block) is not marko.block.FencedCode:
continue
if block.lang not in handlers:
continue
rawtext = only(block.children)
assert type(rawtext) is marko.inline.RawText
text = rawtext.children
assert type(text) is str
scripts[block.lang] += handlers[block.lang].process(text)
for language, script in scripts.items():
handler = handlers[language]
with NamedTemporaryFile("w",
suffix=handler.extension,
dir=os.getcwd()) as temporary_file:
temporary_file.write(script)
temporary_file.flush()
run(handler.command(os.path.basename(temporary_file.name)))