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> --- Makefile | 11 +++- avocado/.gitignore | 1 + avocado/tasst/__init__.py | 27 ++++++++++ avocado/tasst/site.py | 104 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 avocado/.gitignore create mode 100644 avocado/tasst/__init__.py create mode 100644 avocado/tasst/site.py diff --git a/Makefile b/Makefile index d2daaa1..fc83cd2 100644 --- a/Makefile +++ b/Makefile @@ -135,6 +135,7 @@ clean: $(RM) $(BIN) *~ *.o seccomp.h pasta.1 \ passt.tar passt.tar.gz *.deb *.rpm \ passt.pid README.plain.md + $(RM) -r avocado/__pycache__ install: $(BIN) $(MANPAGES) docs mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) @@ -297,9 +298,15 @@ cppcheck: $(SRCS) $(HEADERS) AVOCADO = avocado +avocado-%: + PYTHONPATH=./avocado $(AVOCADO) run avocado --filter-by-tags=$* + +avocado-all: + PYTHONPATH=./avocado $(AVOCADO) run avocado + +# Default avocado tests to run, everything except the "meta" tests .PHONY: avocado -avocado: - $(AVOCADO) run avocado +avocado: avocado--meta check: avocado $(MAKE) -C test check diff --git a/avocado/.gitignore b/avocado/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/avocado/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/avocado/tasst/__init__.py b/avocado/tasst/__init__.py new file mode 100644 index 0000000..bd7994c --- /dev/null +++ b/avocado/tasst/__init__.py @@ -0,0 +1,27 @@ +#! /usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# tasst - Test A Simple Socket Transport +# library of test helpers for passt & pasta +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +import avocado + + +# Base class for avocado-based passt/pasta tests +class Tasst(avocado.Test): + # Fairly short default timeout + timeout = 10.0 + + def subsetup(self, class_, subdata): + assert isinstance(self, class_) + setattr(self, class_.__name__, subdata) + + def get_subsetup(self, class_): + assert hasattr(self, class_.__name__), \ + "{}.setUp() needs to call {}.subsetup()".format(self.__class__.__name__, + class_.__name__) + return getattr(self, class_.__name__) diff --git a/avocado/tasst/site.py b/avocado/tasst/site.py new file mode 100644 index 0000000..682baee --- /dev/null +++ b/avocado/tasst/site.py @@ -0,0 +1,104 @@ +#! /usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# tasst - Test A Simple Socket Transport +# library of test helpers for passt & pasta +# +# tasst/site.py - Manage simulated network sites for testing +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +import avocado +from avocado.utils.process import CmdError + +from tasst import Tasst + + +class Site: + """ + A (usually virtual or simulated) location where we can execute + commands and configure networks. + + """ + + def __init__(self, name): + self.name = name # For debugging + + # Shut down the site and release any resources it's using. We + # can't use __del__() for this (RAII like), because Python doesn't + # guarantee that will get called, and that's pretty easy to hit in + # practice. The modern Pythonic way of doing this is 'with' and + # ContextManagers, but that doesn't work with Avocado's jUnit + # derived format for setUp() and tearDown(). Oh well, do it + # manually. + def close(self): + pass + + def output(self, cmd, **kwargs): + raise NotImplementedError + + def fg(self, cmd, **kwargs): + self.output(cmd, **kwargs) + + def require_cmds(self, *cmds): + missing = [c for c in cmds + if self.fg('type {}'.format(c), ignore_status=True) != 0] + if missing: + raise avocado.TestCancel("Missing commands {} on {}" + .format(', '.join(missing), self.name)) + + +class BaseSiteTasst(Tasst): + """ + Basic tests for executing commands on sites + + :avocado: disable + :avocado: tags=meta + """ + timeout = 1.0 + + # Derived classes must call this from setUp() + def subsetup(self, site): + assert isinstance(site, Site) + site.require_cmds('true', 'false', 'echo') + Tasst.subsetup(self, BaseSiteTasst, site) + + def test_true(self): + site = self.get_subsetup(BaseSiteTasst) + site.fg('true') + + def test_false(self): + site = self.get_subsetup(BaseSiteTasst) + self.assertRaises(CmdError, site.fg, 'false') + + def test_echo(self): + site = self.get_subsetup(BaseSiteTasst) + s = 'Hello tasst' + out = site.output('echo {}'.format(s)) + self.assertEquals(out, s.encode('utf-8')) + + +# Represents the host on which the tests are running, as opposed to +# some simulated host created by the tests +class RealHost(Site): + def __init__(self): + super().__init__('REAL_HOST') + + def output(self, cmd, sudo=False, **kwargs): + assert not sudo, "BUG: Shouldn't run commands with privilege on host" + return avocado.utils.process.system_output(cmd, **kwargs) + + def fg(self, cmd, sudo=False, **kwargs): + assert not sudo, "BUG: Shouldn't run commands with privilege on host" + return avocado.utils.process.system(cmd, **kwargs) + + +REAL_HOST = RealHost() + + +class RealHostTasst(BaseSiteTasst): + def setUp(self): + super().setUp() + BaseSiteTasst.subsetup(self, REAL_HOST) -- 2.40.1