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
|
"""
This file runs end-to-end tests by compiling full TeX files and comparing
with reference output files.
The folder containing this test file and each of its subfolder contains a
"sources" folder for TeX inputs, an optional "extras" folder for auxilliary
input, and a "benchmarks" folder containing the expected outputs.
Each input TeX file can start with the following species of special lines:
%* command to run which runs a command before compilation
%# filename which copies extras/filename to the output folder
before compilation
%python 3.X which skips the test if python is older than 3.X
Each time such a test fails, the output is placed into a "new" folder.
This allows both easy comparison and replacement of the benchmark in
case the change is desired.
"""
import sys, os, shutil, difflib, subprocess
from pathlib import Path
import pytest
plastex_dir = str(Path(__file__).absolute().parent.parent)
def which(name, path=None, exts=('',)):
"""
Search PATH for a binary.
Args:
name -- the filename to search for
path -- the optional path string (default: os.environ['PATH')
exts -- optional list/tuple of extensions to try (default: ('',))
Returns:
The abspath to the binary or None if not found.
"""
path = os.environ.get('PATH', path)
for dir in path.split(os.pathsep):
for ext in exts:
binpath = os.path.abspath(os.path.join(dir, name) + ext)
if os.path.isfile(binpath):
return binpath
return None
class Process(object):
""" Simple subprocess wrapper """
def __init__(self, *args, **kwargs):
if 'stdin' not in kwargs:
kwargs['stdin'] = subprocess.PIPE
if 'stdout' not in kwargs:
kwargs['stdout'] = subprocess.PIPE
if 'stderr' not in kwargs:
kwargs['stderr'] = subprocess.STDOUT
if 'env' not in kwargs:
ppath = ':'.join([
os.environ.get('PYTHONPATH', ''),
str(plastex_dir),
])
env = os.environ.copy()
env['PYTHONPATH'] = ppath
kwargs['env'] = env
self.process = subprocess.Popen(args, **kwargs)
self.log = self.process.stdout.read().decode('utf8')
self.process.wait()
self.returncode = self.process.returncode
self.process.stdout.close()
self.process.stdin.close()
files = [path for path in Path(__file__).parent.glob('**/*.tex')
if 'sources' in path.parts]
@pytest.mark.parametrize('src', files,
ids=lambda p: p.name)
def test_benchmark(src, tmpdir):
tmpdir = Path(str(tmpdir)) # For old python
root = src.parent.parent
# Create temp dir and files
texfile = tmpdir/src.name
shutil.copyfile(str(src), str(texfile))
outdir = str(tmpdir)
# Run preprocessing commands
with src.open() as fh:
for line in fh:
if line.startswith('%*'):
command = line[2:].strip()
p = Process(cwd=outdir, *command.split())
if p.returncode:
raise OSError('Preprocessing command exited abnormally with return code %s: %s' % (command, p.log))
elif line.startswith('%#'):
filename = line[2:].strip()
shutil.copyfile(str(root/'extras'/filename),
str(tmpdir/filename))
elif line.startswith('%python '):
# skip this test on old python
version = tuple(int(n) for n in line[8:].strip().split('.'))
if sys.version_info < version:
pytest.skip('This python is too old')
elif line.startswith('%'):
continue
elif not line.strip():
continue
else:
break
# Run plastex
outfile = (tmpdir/src.name).with_suffix('.html')
plastex = which('plastex') or 'plastex'
python = sys.executable
p = Process(python, plastex,'--renderer=HTML5',
'--split-level=0','--no-theme-extras',
'--dir=%s' % outdir,'--theme=minimal',
'--filename=%s' % outfile.name, src.name,
cwd=outdir)
if p.returncode:
shutil.rmtree(outdir, ignore_errors=True)
raise OSError('plastex failed with code %s: %s' % (p.returncode, p.log))
benchfile = root/'benchmarks'/outfile.name
if benchfile.exists():
bench = benchfile.read_text().split('\n')
output = outfile.read_text().split('\n')
else:
(root/'new').mkdir(parents=True, exist_ok=True)
shutil.copyfile(str(outfile), str(root/'new'/outfile.name))
shutil.rmtree(outdir, ignore_errors=True)
raise OSError('No benchmark file: %s' % benchfile)
# Compare files
diff = '\n'.join(difflib.unified_diff(bench, output,
fromfile='benchmark', tofile='output')).strip()
if diff:
(root/'new').mkdir(parents=True, exist_ok=True)
shutil.copyfile(str(outfile), str(root/'new'/outfile.name))
shutil.rmtree(outdir, ignore_errors=True)
assert not(diff), 'Differences were found: {}\nplasTeX log:\n{}'.format(diff, p.log)
# Clean up
shutil.rmtree(outdir, ignore_errors=True)
|