lisa.tests.base.TestBundleMeta#

class lisa.tests.base.TestBundleMeta(cls_name, bases, dct, **kwargs)[source]#

Bases: ABCMeta

Metaclass of TestBundleBase.

Method with a return annotation of ResultBundleBase are wrapped to:

  • Update the context attribute of a returned ResultBundleBase

  • Add an undecided_filter attribute, with add_undecided_filter() decorator, so that any test method can be used as a pre-filter for another one right away.

  • Wrap _from_target to provide a single collector parameter, built from the composition of the collectors provided by _make_collector methods in the base class tree.

If _from_target is defined in the class but from_target is not, a stub is created and the annotation of _from_target is copied to the stub. The annotation is then removed from _from_target so that it is not picked up by exekall.

The signature of from_target is the result of merging the original cls.from_target parameters with the ones defined in _from_target.

Methods

add_undecided_filter()

Turn any method returning a ResultBundleBase into a decorator that can be used as a test method filter.

collector_factory()

test_method()

Decorator to intercept returned ResultBundle and attach some contextual information.

Methods#

static TestBundleMeta.add_undecided_filter(func)[source]#

Turn any method returning a ResultBundleBase into a decorator that can be used as a test method filter.

The filter decorator is accessible as the undecided_filter attribute of the decorated method.

Once a test is decorated, the filter method will be run in addition to the wrapped test, and if the filter does not succeed, the ResultBundleBase result will be set to Result.UNDECIDED.

Example:

class Foo(TestBundle):
    @TestBundle.add_undecided_filter
    def test_foo(self, xxx=42, ...):
        ...

    # Alternatively, ResultBundle return annotation will
    # automatically decorate the method with TestBundleMeta
    # metaclass.
    def test_foo(self, xxx=42, ...) -> ResultBundle:
        ...

class Bar(Foo):
    # Set xxx=55 as default, but this can be overriden when
    # test_bar() is called.
    @Foo.test_foo.undecided_filter(xxx=77)
    def test_bar(self, yyy=43, ...) -> ResultBundle:
        ...

The resulting decorated method can take the union of keyword parameters:

bar = Bar()
bar.test_bar(xxx=33, yyy=55)
# Same as
bar.test_bar(33, yyy=55)
# But this fails, since only keyword arguments can be passed to the
# wrapping pre-test
bar.test_bar(33, 55)

If there is a parameter conflict, it is detected at import time and will result in a TypeError.

Note

Even if the pre-test does not succeed, the wrapped test is still executed, so that the ResultBundle metrics are updated and the artifacts still produced. This can be important in order to manually analyse results in case the pre-filter was overly conservative and marked a usable result as UNDECIDED.

classmethod TestBundleMeta.collector_factory(f)[source]#
classmethod TestBundleMeta.test_method(func)[source]#

Decorator to intercept returned ResultBundle and attach some contextual information.