Skip to content

Commit 33fa0dd

Browse files
tests/conftest: record coordinator's coverage
Recording the coordinator's coverage is useful to determine, which parts of the component are executed during tests. crossbar runs labgrid's coordinator component as a guest worker. Guest workers are spawned as subprocesses by crossbar. Therefore recording coverage of the coordinator component does not work out of the box. To allow recording coordinator coverage, set the coverage command line tool as the excutable in the crossbar configuration, whose 'run' subcommand works as a drop-in replacement for 'python'. This way the coverage of the guest worker is recorded separately. The '--parallel-mode' switch makes coverage add a unique suffix to the coverage data file. Set the location of the coverage data via '--data-file' to the root directory. pytest-cov automatically combines the coverage data in this directory. Since the crossbar process is forcefully terminated during the tests, the coverage data can not be written. Since the coverage tool registers an atexit SIGTERM handler to write its data, send crossbar SIGTERM, wait for any remaining output and the process to exit. Now the coverage data contains labgrid's coordinator component. Signed-off-by: Bastian Krause <bst@pengutronix.de>
1 parent 7a73dfc commit 33fa0dd

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

tests/conftest.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import logging
2+
from signal import SIGTERM
23
import sys
34
import threading
45
from importlib.util import find_spec
56

67
import pytest
78
import pexpect
9+
import yaml
810

911
from labgrid import Target
1012
from labgrid.driver import SerialDriver
@@ -98,10 +100,33 @@ def serial_driver_no_name(target, serial_port, mocker):
98100
return s
99101

100102
@pytest.fixture(scope='function')
101-
def crossbar(tmpdir, pytestconfig):
103+
def crossbar_config(tmpdir, pytestconfig):
104+
crossbar_config = '.crossbar/config.yaml'
105+
106+
pytestconfig.rootdir.join(crossbar_config).copy(tmpdir.mkdir('.crossbar'))
107+
108+
# crossbar runs labgrid's coordinator component as a guest, record its coverage
109+
if pytestconfig.pluginmanager.get_plugin('pytest_cov'):
110+
with open(tmpdir.join(crossbar_config), 'r+') as stream:
111+
conf = yaml.safe_load(stream)
112+
113+
for worker in conf['workers']:
114+
if worker['type'] == 'guest':
115+
worker['executable'] = 'coverage'
116+
worker['arguments'].insert(0, 'run')
117+
worker['arguments'].insert(1, '--parallel-mode')
118+
# pytest-cov combines coverage files in root dir automatically, so copy it there
119+
coverage_data = pytestconfig.rootdir.join('.coverage')
120+
worker['arguments'].insert(2, f'--data-file={coverage_data}')
121+
122+
stream.seek(0)
123+
yaml.safe_dump(conf, stream)
124+
125+
@pytest.fixture(scope='function')
126+
def crossbar(tmpdir, crossbar_config):
102127
if not find_spec('crossbar'):
103128
pytest.skip("crossbar not found")
104-
pytestconfig.rootdir.join('.crossbar/config.yaml').copy(tmpdir.mkdir('.crossbar'))
129+
105130
spawn = pexpect.spawn(
106131
'crossbar start --color false --logformat none',
107132
logfile=Prefixer(sys.stdout.buffer, 'crossbar'),
@@ -116,9 +141,14 @@ def crossbar(tmpdir, pytestconfig):
116141
reader = threading.Thread(target=keep_reading, name='crossbar-reader', args=(spawn,), daemon=True)
117142
reader.start()
118143
yield spawn
144+
145+
# let coverage write its data:
146+
# https://coverage.readthedocs.io/en/latest/subprocess.html#process-termination
119147
print("stopping crossbar")
120-
spawn.close(force=True)
121-
assert not spawn.isalive()
148+
spawn.kill(SIGTERM)
149+
spawn.expect(pexpect.EOF)
150+
spawn.wait()
151+
122152
reader.join()
123153

124154
@pytest.fixture(scope='function')

0 commit comments

Comments
 (0)