Add a library "tasst" for use in avocado tests of passt & pasta. We start by adding the outline of logic to run commands in various places (e.g. namespaces, VMs). We add some avocado tests for the test library itself, tagged 'meta' to distinguish it from tests for passt/pasta proper. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- test/Makefile | 8 +++- test/tasst/exesite.py | 99 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 test/tasst/exesite.py diff --git a/test/Makefile b/test/Makefile index c06d63b6..0e641024 100644 --- a/test/Makefile +++ b/test/Makefile @@ -243,10 +243,14 @@ flake8: $(VENV) # # C0116 missing-function-docstring # We have a bunch of trivial functions for which docstrings would be overkill -# +# C0103 invalid-name +# As well as complaining about bad formatting style, this complains +# about short identifiers (< 3 characters?). For many locals and +# some methods, this is stupid. pylint: $(VENV) $(VENV)/bin/pylint \ - --disable=C0116 \ + --disable=C0116 \ + --disable=C0103 \ $(PYPKGS) .PHONY: check diff --git a/test/tasst/exesite.py b/test/tasst/exesite.py new file mode 100644 index 00000000..9f037f77 --- /dev/null +++ b/test/tasst/exesite.py @@ -0,0 +1,99 @@ +#! /usr/bin/env avocado-runner-avocado-classless + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +""" +Test A Simple Socket Transport + +tasst/exesite.py - Manage simulated network sites for testing +""" + + +import contextlib + +import avocado +from avocado.utils.process import CmdError +from avocado_classless.test import assert_eq, assert_raises, test_output + + +class Site(contextlib.AbstractContextManager): + """ + A (usually virtual or simulated) location where we can execute + commands and configure networks. + + """ + + def __init__(self, name): + self.name = name # For debugging + + def hostify(self, cmd, **kwargs): + raise NotImplementedError + + def __enter__(self): + return self + + def __exit__(self, *exc_details): + pass + + def output(self, cmd, strip_trail_nl=False, **kwargs): + kwargs['strip_trail_nl'] = strip_trail_nl + cmd, kwargs = self.hostify(cmd, **kwargs) + return avocado.utils.process.system_output(cmd, **kwargs) + + def fg(self, cmd, **kwargs): + cmd, kwargs = self.hostify(cmd, **kwargs) + return avocado.utils.process.system(cmd, **kwargs) + + def require_cmds(self, *cmds): + missing = [c for c in cmds + if self.fg(f'type {c}', ignore_status=True) != 0] + if missing: + raise avocado.TestCancel( + f"Missing commands {', '.join(missing)} on {self.name}") + + +def test_site(sitefn): + def test_true(s): + with s as site: + site.fg('true') + + def test_false(s): + with s as site: + assert_raises(CmdError, site.fg, 'false') + + def test_echo(s): + with s as site: + msg = 'Hello tasst' + out = site.output(f'echo {msg}') + assert_eq(out, msg.encode('utf-8') + b'\n') + + def test_timeout(s): + with s as site: + site.fg('sleep infinity', timeout=0.1, ignore_status=True) + + return test_output(test_true, test_false, test_echo, test_timeout)(sitefn) + + +class RealHost(Site): + """Represents the host on which the tests are running (as opposed + to some simulated host created by the tests) + + """ + + def __init__(self): + super().__init__('REAL_HOST') + + def hostify(self, cmd, *, sudo=False, **kwargs): + assert not sudo, "BUG: Shouldn't run commands with privilege on host" + return cmd, kwargs + + +REAL_HOST = RealHost() + + +@test_site +def real_host(): + return REAL_HOST -- 2.41.0