Files
plugins/.ci/test.py
2022-01-04 18:17:24 +01:00

183 lines
5.2 KiB
Python

from pathlib import Path
import subprocess
from pprint import pprint
from collections import namedtuple
from typing import Generator
import sys
import tempfile
import shlex
import os
# Directories that are not plugins
exclude = [
'.ci',
'.git',
'.github',
'lightning',
'feeadjuster'
]
global_dependencies = [
'pytest==5.*',
'pytest-xdist',
'pytest-timeout',
'pyln-testing',
'pytest-rerunfailures',
]
Plugin = namedtuple('Plugin', ['name', 'path', 'requirements', 'devrequirements'])
def enumerate_plugins(basedir: Path) -> Generator[Plugin, None, None]:
plugins = [x for x in basedir.iterdir() if x.is_dir() and x.name not in exclude]
pytests = [x for x in plugins if (x / Path('requirements.txt')).exists()]
for p in sorted(pytests):
yield Plugin(
name=p.name,
path=p,
requirements=p/Path('requirements.txt'),
devrequirements=p/Path('requirements-dev.txt'),
)
def run_one(p: Plugin) -> bool:
print("Running tests on plugin {p.name}".format(p=p))
testfiles = [
x for x in p.path.iterdir()
if (x.is_dir() and x.name == 'tests')
or (x.name.startswith("test_") and x.name.endswith('.py'))
]
if len(testfiles) == 0:
print("No test files found, skipping plugin {p.name}".format(p=p))
return True
print("Found {ctestfiles} test files, creating virtualenv and running tests".format(ctestfiles=len(testfiles)))
print("##[group]{p.name}".format(p=p))
# Create a virtual env
vdir = tempfile.TemporaryDirectory()
vpath = Path(vdir.name)
subprocess.check_output(['virtualenv', '--clear', '-q', vpath])
bin_path = vpath / 'bin'
pip_path = vpath / 'bin' / 'pip3'
python_path = vpath / 'bin' / 'python'
pytest_path = vpath / 'bin' / 'pytest'
pip_opts = ['-qq']
# Install pytest (eventually we'd want plugin authors to include
# it in their requirements-dev.txt, but for now let's help them a
# bit).
subprocess.check_output(
[pip_path, 'install', *pip_opts, *global_dependencies],
stderr=subprocess.STDOUT,
)
# Now install all the requirements
print("Installing requirements from {p.requirements}".format(p=p))
subprocess.check_output(
[pip_path, 'install', '-U', *pip_opts, '-r', p.requirements],
stderr=subprocess.STDOUT,
)
if p.devrequirements.exists():
print("Installing requirements from {p.devrequirements}".format(p=p))
subprocess.check_output(
[pip_path, 'install', '-U', *pip_opts, '-r', p.devrequirements],
stderr=subprocess.STDOUT,
)
if os.environ.get("PYLN_MASTER", "0") == "1":
pass
assert pytest_path.exists()
# Update pyln-testing to master since we're running against c-lightning master.
install_pyln_testing(pip_path)
print("Running tests")
try:
env = os.environ.copy()
env.update({
# Need to customize PATH so lightningd can find the correct python3
'PATH': "{}:{}".format(bin_path, os.environ['PATH']),
# Some plugins require a valid locale to be set
'LC_ALL': 'C.UTF-8',
'LANG': 'C.UTF-8',
})
subprocess.check_call(
[
pytest_path,
p.path,
'-vvv',
'--timeout=600',
'--timeout-method=thread',
'--junitxml=report-{}.xml'.format(p.name),
'--reruns=2',
'--color=no',
'-o', 'junit_family=xunit2',
'-n=5',
],
stderr=subprocess.STDOUT,
env=env,
)
return True
except:
return False
finally:
print("##[endgroup]")
def install_pyln_testing(pip_path):
# Update pyln-testing to master since we're running against c-lightning master.
dest = Path('/tmp/lightning')
repo = 'https://github.com/ElementsProject/lightning.git'
if not dest.exists():
subprocess.check_output([
'git', 'clone', repo, dest
])
subprocess.check_output([
pip_path, 'install', '-U', f'{dest}/contrib/pyln-testing'
])
subprocess.check_output([
pip_path, 'install', '-U', f'{dest}/contrib/pyln-client'
])
subprocess.check_output([
pip_path, 'install', '-U', f'{dest}/contrib/pyln-proto'
])
def run_all(args):
root_path = subprocess.check_output([
'git',
'rev-parse',
'--show-toplevel'
]).decode('ASCII').strip()
root = Path(root_path)
plugins = list(enumerate_plugins(root))
if args != []:
plugins = [p for p in plugins if p.name in args]
print("Testing the following plugins: {names}".format(names=[p.name for p in plugins]))
else:
print("Testing all plugins in {root}".format(root=root))
results = [(p, run_one(p)) for p in plugins]
success = all([t[1] for t in results])
if not success:
print("The following tests failed:")
for t in filter(lambda t: not t[1], results):
print(" - {p.name} ({p.path})".format(p=t[0]))
sys.exit(1)
if __name__ == "__main__":
run_all(sys.argv[1:])