'How can I synchronize and join pytest-xdist workers?
I have a set of pytest tests that I run on multiple remotely-connected (embedded) devices (that do not have pytest on itself).
Basically, the tests secure-copy (via scp) an update image to each device, installs it running bash commands on the target (via ssh), and then check for the system "health" after installing the update.
I use @pytest.mark.parametrize
to target each remotely-connected device per test.
For this set of tests, I could heavily benefit from parallel execution, since I can copy and install the images all in parallel, excepts that I need to impose some precedence on the tests: I have to ensure that the transfer_update_image
happens before install_update_image
for all workers.
I achieved that with @pytest.mark.dependency
, which works just fine on single threaded execution, but not when using pytest ... -n>1
.
When run in parallel, workers will eventually not have their dependency met at the time they reach the next test, since some other worker will not yet be done with the previous test.
How do you suggest I approach this problem? I really could not see hot the pytest-xdist groups could solve this problem, neither any other solution I could find, except making parallel execution inside the test itself, which is the solution I prefer the least, so that I don't loose the per-device test granularity.
Here is my code:
@pytest.mark.dependency(name='transfer_update_image')
@pytest.mark.parametrize('port', ports)
@pytest.mark.parametrize('host', hosts)
def test_transfer_update_image(host, port):
"""Copy update image to controllers"""
...
client = SSHClient(host, port=port, user='root', password='', num_retries=1)
client.copy_file(update_image, '/tmp/update-image')
...
## join all the workers here
@pytest.mark.dependency(name='install_update_image', depends=['transfer_update_image'])
@pytest.mark.parametrize('port', ports)
@pytest.mark.parametrize('host', hosts)
def test_install_update_image(host, port):
"""Install update image to controllers"""
...
client = SSHClient(host, port=port, user='root', password='', num_retries=1)
output = client.run_command(update_cmd, read_timeout=80)
...
## join all the workers here
@pytest.mark.dependency(name='swap_active_rgbc', depends=['install_update_image'])
@pytest.mark.parametrize('port', [23])
@pytest.mark.parametrize('host', hosts)
def test_swap_active_rgbc(host, port):
__swap_active(host,port)
## join all the workers here
@pytest.mark.dependency(name='test_swap_active_wlc', depends=['swap_active_rgbc'])
@pytest.mark.parametrize('port', [22])
@pytest.mark.parametrize('host', hosts)
def test_swap_active_wlc(host, port):
__swap_active(host, port)
## join all the workers here
@pytest.mark.dependency(name='check_installed_image', depends=['test_swap_active_wlc'])
@pytest.mark.parametrize('port', ports)
@pytest.mark.parametrize('host', hosts)
def test_check_installed_image(host, port):
"""Check installed image"""
...
And the output when I run in parallel, which shows tests being skipped since their dependencies were not met:
➜ files git:(develop) ✗ pytest image-installer.py -vv -ss -n2
============================================================================== test session starts ==============================================================================
platform linux -- Python 3.8.10, pytest-7.0.1, pluggy-1.0.0 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: /system-update-tester/files
plugins: dependency-0.5.1, forked-1.4.0, subtests-0.7.0, parallel-0.1.1, xdist-2.5.0, logassert-6
[gw0] linux Python 3.8.10 cwd: /system-update-tester/files
[gw1] linux Python 3.8.10 cwd: /system-update-tester/files
[gw0] Python 3.8.10 (default, Nov 26 2021, 20:14:08) -- [GCC 9.3.0]
[gw1] Python 3.8.10 (default, Nov 26 2021, 20:14:08) -- [GCC 9.3.0]
gw0 [14] / gw1 [14]
scheduling tests via LoadScheduling
image-installer.py::test_get_active_slot[10.0.0.102-22]
image-installer.py::test_get_active_slot[10.0.0.102-23]
[gw1] PASSED image-installer.py::test_get_active_slot[10.0.0.102-22]
image-installer.py::test_uptime[10.0.0.102-22]
[gw0] PASSED image-installer.py::test_get_active_slot[10.0.0.102-23]
image-installer.py::test_uptime[10.0.0.102-23]
[gw1] PASSED image-installer.py::test_uptime[10.0.0.102-22]
image-installer.py::test_transfer_update_image[10.0.0.102-23]
[gw0] PASSED image-installer.py::test_uptime[10.0.0.102-23]
image-installer.py::test_transfer_update_image[10.0.0.102-22]
[gw1] PASSED image-installer.py::test_transfer_update_image[10.0.0.102-23]
image-installer.py::test_install_update_image[10.0.0.102-23]
[gw0] PASSED image-installer.py::test_transfer_update_image[10.0.0.102-22]
image-installer.py::test_install_update_image[10.0.0.102-22]
[gw1] PASSED image-installer.py::test_install_update_image[10.0.0.102-23]
image-installer.py::test_swap_active_rgbc[10.0.0.102-23]
[gw1] PASSED image-installer.py::test_swap_active_rgbc[10.0.0.102-23]
image-installer.py::test_check_installed_image[10.0.0.102-23]
[gw1] SKIPPED image-installer.py::test_check_installed_image[10.0.0.102-23]
image-installer.py::test_check_installed_image[10.0.0.102-22]
[gw1] SKIPPED image-installer.py::test_check_installed_image[10.0.0.102-22]
image-installer.py::test_ensure_slot_swapped[10.0.0.102-23]
[gw1] SKIPPED image-installer.py::test_ensure_slot_swapped[10.0.0.102-23]
image-installer.py::test_ensure_slot_swapped[10.0.0.102-22]
[gw1] SKIPPED image-installer.py::test_ensure_slot_swapped[10.0.0.102-22]
[gw0] PASSED image-installer.py::test_install_update_image[10.0.0.102-22]
image-installer.py::test_swap_active_wlc[10.0.0.102-22]
[gw0] SKIPPED image-installer.py::test_swap_active_wlc[10.0.0.102-22]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|