120 lines
3.3 KiB
Python
Executable File
120 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Exercise the transform program.
|
|
|
|
Sample inputs are fed to the transform program on stdin.
|
|
The transform program writes its output to stdout.
|
|
This test program compares actual output with expected output,
|
|
and exits with non-zero status upon any mismatch.
|
|
"""
|
|
|
|
# This test program is automatically executed on container build.
|
|
# Test dependencies, namely pytest, are supplied by the container image.
|
|
#
|
|
# This file must have a .py extension.
|
|
# pytest maintainers refuse to allow test collection otherwise.
|
|
|
|
# pylint: disable=missing-function-docstring
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import subprocess
|
|
from contextlib import ExitStack
|
|
|
|
import pytest
|
|
|
|
|
|
def test_null():
|
|
assert run_prog([]) == []
|
|
|
|
|
|
def test_ignores_plumbing_branch():
|
|
assert run_prog([{"name": "plumbing"}]) == []
|
|
|
|
|
|
def test_ignores_trunkish_branch():
|
|
assert (run_prog([{"name": "main"},
|
|
{"name": "master"}])
|
|
== [])
|
|
|
|
|
|
def test_topic_branch():
|
|
assert (run_prog([{"name": "bobtest"}])
|
|
==
|
|
[{"name": "bobtest",
|
|
"tag": "bobtest"}])
|
|
assert (run_prog([{"name": "bob-test"}])
|
|
==
|
|
[{"name": "bob-test",
|
|
"tag": "bob-test"}])
|
|
|
|
# use - as a substitute for characters that would not be valid in a tag
|
|
assert (run_prog([{"name": "bob/frob"}])
|
|
==
|
|
[{"name": "bob/frob",
|
|
"tag": "bob-frob"}])
|
|
assert (run_prog([{"name": "bob/-frob"}])
|
|
==
|
|
[{"name": "bob/-frob",
|
|
"tag": "bob-frob"}])
|
|
|
|
|
|
PROG = os.environ.get("TEST_PROG") # see bottom
|
|
|
|
|
|
def run_prog(input_obj):
|
|
with ExitStack() as st:
|
|
fi = st.enter_context(tempfile.TemporaryFile(mode="w+", encoding="utf8"))
|
|
fo = st.enter_context(tempfile.TemporaryFile(mode="w+", encoding="utf8"))
|
|
json.dump(input_obj, fi)
|
|
fi.seek(0)
|
|
subprocess.run([PROG], stdin=fi, stdout=fo, check=True)
|
|
fo.seek(0)
|
|
return json.load(fo)
|
|
|
|
|
|
class Args:
|
|
|
|
@classmethod
|
|
def parse(cls, argv=None):
|
|
if argv is None:
|
|
argv = sys.argv
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("transform-prog",
|
|
help="Path to the program under test.")
|
|
args = parser.parse_args(argv[1:])
|
|
return cls(args)
|
|
|
|
def __init__(self, args):
|
|
self._args = args
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self._args, name)
|
|
|
|
@property
|
|
def prog(self):
|
|
return getattr(self, "transform-prog")
|
|
|
|
|
|
def main(argv=None):
|
|
if not argv:
|
|
argv = sys.argv
|
|
# Global bindings we make here are not visible when pytest is executing,
|
|
# probably because it (re)imports the module. ¯\_(ツ)_/¯
|
|
# Funnel whatever we need through the process environment instead.
|
|
args = Args.parse(argv)
|
|
os.environ["TEST_PROG"] = args.prog
|
|
return pytest.main(["-vv", # dump full structured diffs upon any mismatch
|
|
"-o", "console_output_style=classic",
|
|
# suppress useless progress markers
|
|
"--tb=short", # suppress outrageously verbose tracebacks
|
|
argv[0]])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|