Skip to content

[RFC] Add configurable target setup context manager

Douglas Raillard requested to merge github/fork/douglas-raillard-arm/_pr173 into main

Allow specifying a list of functions taking a target and returning context managers inside TargetConf, which is then used by a Target.from_conf_setup() method to stream target objects configured by these context managers. This allows running the same tests under different conditions, with arbitrary setup and teardown code.

CAVEAT: this works when only one expression is being executed. When executing more, the same Target objects will be reused without calling the context manager. Since solely re-using a Target object will not automatically setup the board accordingly (something has to call some code to do it), it would not be very useful. The easiest way to make that work would be keeping a reference to the setup function in each Target object, and have the main affected step call it explicitly (e.g. in TestBundle.from_target()). At this point, it may be cleaner to just add a new parameter to from_target()` to specify some arbitrary setup to do.

target_conf.yml

target-conf:
    setup:
        - !var lisa.tests.foo.mysetup1
        - !var lisa.tests.foo.mysetup2

foo.py

from contextlib import contextmanager

@contextmanager
def mysetup1(target):
    target.write_value('/sys/myfile',  "start1")
    try:
        yield
    finally:
        target.write_value('/sys/myfile',  "stop1")

@contextmanager
def mysetup2(target):
    target.write_value('/sys/myfile',  "start2")
    try:
        yield
    finally:
        target.write_value('/sys/myfile',  "stop2")

Note: the setup context managers will be executed for each expression, since exekall needs to compute all the values of an expression before starting computing the next one.

Note 2: Parametric context managers can be achieved by using !call tag in target_conf.yml: target_conf.yml, in a way similar to decorators with parameters:

target-conf:
    setup:
        - !call:lisa.tests.foo.make_setup
            files_start:
                /sys/myfile: start1
                /sys/myfile2: start1
            files_stop:
                /sys/myfile: stop1
                /sys/myfile2: stop1

        - !call:lisa.tests.foo.make_setup
            files_start:
                /sys/myfile: start2
                /sys/myfile2: start2
            files_stop:
                /sys/myfile: stop2
                /sys/myfile2: stop2

foo.py

from contextlib import contextmanager

def make_setup(files_start, files_stop):
    @contextmanager
    def setup(tg):
        for path, value in files_start.items():
            tg.write_value(path, str(value))
        try:
            yield
        finally:
            for path, value in files_stop.items():
                tg.write_value(path, str(value))
    return setup

Merge request reports