wip/2023-06/python-suite-runner
Dylan Baker 2023-06-08 16:10:16 -07:00
parent 77904819f2
commit 1961be9a0e
2 changed files with 142 additions and 17 deletions

View File

@ -1,2 +1,99 @@
[noargs]
exitcode = 1
exitcode = 1
[libs]
stdout = "-L/test/lib -lfoo\n"
args = ["--libs", "foo"]
[libs.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lfoo\n"
args = ["--cflags", "--libs", "foo"]
[lib_cflags.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lfoo\n"
args = ["--cflags", "--libs", "foo > 1.2"]
[lib_cflags_version.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version_multiple]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n"
args = ["--cflags", "--libs", "foo > 1.2 bar >= 1.3"]
[lib_cflags_version_multiple.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version_multiple_comma]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n"
args = ["--cflags", "--libs", "foo > 1.2,bar >= 1.3"]
[lib_cflags_version_multiple_comma.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version_alt]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lfoo\n"
args = ["--cflags", "--libs", "foo", ">", "1.2"]
[lib_cflags_version_alt.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version_different]
stdout = "-fPIC -I/test/include/foo -L/test/lib -lfoo\n"
args = ["--cflags", "--libs", "foo", "!=", "1.3"]
[lib_cflags_version_different.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[lib_cflags_version_different_bad]
exitcode = 1
stderr = "Package dependency requirement 'foo != 1.2.3' could not be satisfied.\nPackage 'foo' has version '1.2.3', required version is '!= 1.2.3'\n"
args = ["--cflags", "--libs", "foo", "!=", "1.2.3"]
[lib_cflags_version_different_bad.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[exists_nonexistent]
exitcode = 1
args = ["--exists", "nonexistant"]
[exists_nonexistent.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[nonexistent]
exitcode = 1
args = ["nonexistant"]
[lexists_nonexistent.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[exists_version]
args = ["--exists", "foo > 1.2"]
[exists_version.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[exists_version_alt]
args = ["--exists", "foo", ">", "1.2"]
[exists_version_alt.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[exists_version_bad]
exitcode = 1
args = ["--exists", "foo > 1.2.3"]
[exists_version_bad.env]
PKG_CONFIG_PATH="{test_root}/lib1"
[uninstalled_bad]
exitcode = 1
args = ["--uninstalled", "foo"]
[uninstalled_bad.env]
PKG_CONFIG_PATH="{test_root}/lib1"

View File

@ -13,9 +13,10 @@
"""Simple runner for test cases."""
from __future__ import annotations
import asyncio
import argparse
import asyncio
import dataclasses
import os
import sys
import typing
@ -28,36 +29,60 @@ if typing.TYPE_CHECKING:
class TestDefinition(typing.TypedDict, total=False):
exitcode: int
stdout: str
stderr: str
args: typing.List[str]
env: typing.Dict[str, str]
class Arguments(typing.Protocol):
suite: str
suites: typing.List[str]
pkgconf: str
verbose: bool
TEST_ROOT = os.path.dirname(os.path.abspath(__file__))
@dataclasses.dataclass
class Result:
name: str
success: bool = True
success: bool = False
reason: typing.Optional[str] = None
def interpolate(input: str) -> str:
return input.format(
test_root=TEST_ROOT,
)
async def run_test(pkgconf: str, name: str, test: TestDefinition, verbose: bool) -> Result:
args: typing.List[str] = []
env: typing.Dict[str, str] = {}
if (renv := test.get('env', None)) is not None:
env = {k: interpolate(v) for k, v in renv.items()}
proc = await asyncio.create_subprocess_exec(
pkgconf, *args,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
pkgconf, *test.get('args', []),
stdout=asyncio.subprocess.PIPE if 'stdout' in test is not None else asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.PIPE if 'stderr' in test is not None else asyncio.subprocess.DEVNULL,
env=env)
out, err = await proc.communicate()
rout, rerr = await proc.communicate()
out = rout.decode() if rout is not None else ''
err = rerr.decode() if rerr is not None else ''
result = Result(name)
if (ret := test.get('exitcode')) is not None and proc.returncode != ret:
result.success = False
if (ret := test.get('exitcode', 0)) and proc.returncode != ret:
result.reason = f"Return code was {proc.returncode}, but expected {ret}"
elif (exp := test.get('stdout')) is not None and out != exp:
result.reason = f"Stdout was {out}, but expected {exp}"
elif (exp := test.get('stderr')) is not None and err != exp:
result.reason = f"Stdout was {err}, but expected {err}"
else:
result.success = True
if verbose:
if result.success:
@ -69,20 +94,23 @@ async def run_test(pkgconf: str, name: str, test: TestDefinition, verbose: bool)
async def run(args: Arguments) -> None:
with open(args.suite, 'rb') as f:
suite: typing.Dict[str, TestDefinition] = tomllib.load(f)
tests: typing.List[typing.Coroutine[typing.Any, typing.Any, Result]] = []
for suite in args.suites:
with open(suite, 'rb') as f:
suite: typing.Dict[str, TestDefinition] = tomllib.load(f)
results = await asyncio.gather(
*[run_test(args.pkgconf, n, s, args.verbose) for n, s in suite.items()]
)
tests.extend(
[run_test(args.pkgconf, n, s, args.verbose) for n, s in suite.items()])
results = await asyncio.gather(*tests)
return all(r.success for r in results)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('suite', help="A toml test suite definition")
parser.add_argument('pkgconf', help="Path to built pkgconf executable")
parser.add_argument('suites', nargs="+", help="One or more toml test suite definition")
parser.add_argument('-v', '--verbose', action='store_true',
help="Print more information while running")
args: Arguments = parser.parse_args()