pytest-dev / pytest-dev/pytest-xdist

Question: How the fixture can communicate that it is terminating so that the master can re-schedule the tests to other workers

Open
#289 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
Python
Stars
1.9k
Forks
287
Avg merge
9h 30m
Merged PRs (30d)
2

Description

I tried sys.exit() in a session scope fixture but even then the test is marked as Error. Also the same worker was allocated with more tests and they also end up in error. What I am trying to achieve is if a worker terminates then the master should re-schedule the tests to other workers.

Contests of conftest.py:

import pytest
import sys
import os

sys.stdout = sys.stderr

def pytest_cmdline_preparse(args):
    worker = os.environ.get('PYTEST_XDIST_WORKER')
    if 'xdist' in sys.modules and not worker:  # pytest-xdist plugin
        num = 2
        args[:] = ["-sv", "-n", str(num)] + args

def read_device_list():
    return ["192.168.72.11", "192.168.72.12"]

def pytest_configure(config):
    # read device list if we are on the master
    if not hasattr(config, "slaveinput"):
        config.iplist = read_device_list()

def pytest_configure_node(node):
    # the master for each node fills slaveinput dictionary
    # which pytest-xdist will transfer to the subprocess
    node.slaveinput["ipadr"] = node.config.iplist.pop()

@pytest.fixture(scope="session")
def device(request):
    slaveinput = getattr(request.config, "slaveinput", None)
    if slaveinput is None: # single-process execution
        ipadr = read_device_list()[0]
    else: # running in a subprocess here
        ipadr = slaveinput["ipadr"]
    if ipadr == "192.168.72.11":
        print("192.168.72.11 Crashed")
        sys.exit(-11)
    return Device(ipadr)

class Device:
    def __init__(self, ipadr):
        self.ipadr = ipadr

    def __repr__(self):
        return "<Device ip=%s>" % (self.ipadr)

Contents of test_parallel.py:

import time
import pytest


class Test1:
    def test_device1(self, device):
        time.sleep(2)
        assert 1, device

    def test_device2(self, device):
        time.sleep(2)
        assert 1, device

    def test_device3(self, device):
        time.sleep(2)
        assert 1, device

    def test_device4(self, device):
        time.sleep(2)
        assert 1, device

    def test_device5(self, device):
        time.sleep(2)
        assert 1, device

Command line with output:

(base) E:\Personal\13_Materials\Python\RA\parallel_tests>pytest --max-worker-restart=0
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.3.2, py-1.5.2, pluggy-0.6.0 -- C:\Users\sunda\Anaconda3\python.exe
cachedir: .cache
rootdir: E:\Personal\13_Materials\Python\RA\parallel_tests, inifile:
plugins: xdist-1.22.2, forked-0.2
[gw0] win32 Python 3.6.4 cwd: E:\Personal\13_Materials\Python\RA\parallel_tests
[gw1] win32 Python 3.6.4 cwd: E:\Personal\13_Materials\Python\RA\parallel_tests
[gw0] Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]
[gw1] Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]
gw0 [5] / gw1 [5]
scheduling tests via LoadScheduling

test_parallel.py::Test1::test_device2
test_parallel.py::Test1::test_device1 192.168.72.11 Crashed

[gw1] [ 20%] ERROR test_parallel.py::Test1::test_device2
192.168.72.11 Crashed
test_parallel.py::Test1::test_device4
[gw1] [ 40%] ERROR test_parallel.py::Test1::test_device4
[gw0] [ 60%] PASSED test_parallel.py::Test1::test_device1 192.168.72.11 Crashed

test_parallel.py::Test1::test_device5
test_parallel.py::Test1::test_device3
[gw1] [ 80%] ERROR test_parallel.py::Test1::test_device5
[gw0] [100%] PASSED test_parallel.py::Test1::test_device3

=================================== ERRORS ====================================
____________________ ERROR at setup of Test1.test_device2 _____________________
[gw1] win32 -- Python 3.6.4 C:\Users\sunda\Anaconda3\python.exe

request = <SubRequest 'device' for <Function 'test_device2'>>

    @pytest.fixture(scope="session")
    def device(request):
        slaveinput = getattr(request.config, "slaveinput", None)
        if slaveinput is None: # single-process execution
            ipadr = read_device_list()[0]
        else: # running in a subprocess here
            ipadr = slaveinput["ipadr"]
        if ipadr == "192.168.72.11":
            print("192.168.72.11 Crashed")
>           sys.exit(-11)
E           SystemExit: -11

conftest.py:35: SystemExit
____________________ ERROR at setup of Test1.test_device4 _____________________
[gw1] win32 -- Python 3.6.4 C:\Users\sunda\Anaconda3\python.exe

request = <SubRequest 'device' for <Function 'test_device4'>>

    @pytest.fixture(scope="session")
    def device(request):
        slaveinput = getattr(request.config, "slaveinput", None)
        if slaveinput is None: # single-process execution
            ipadr = read_device_list()[0]
        else: # running in a subprocess here
            ipadr = slaveinput["ipadr"]
        if ipadr == "192.168.72.11":
            print("192.168.72.11 Crashed")
>           sys.exit(-11)
E           SystemExit: -11

conftest.py:35: SystemExit
____________________ ERROR at setup of Test1.test_device5 _____________________
[gw1] win32 -- Python 3.6.4 C:\Users\sunda\Anaconda3\python.exe

request = <SubRequest 'device' for <Function 'test_device5'>>

    @pytest.fixture(scope="session")
    def device(request):
        slaveinput = getattr(request.config, "slaveinput", None)
        if slaveinput is None: # single-process execution
            ipadr = read_device_list()[0]
        else: # running in a subprocess here
            ipadr = slaveinput["ipadr"]
        if ipadr == "192.168.72.11":
            print("192.168.72.11 Crashed")
>           sys.exit(-11)
E           SystemExit: -11

conftest.py:35: SystemExit
====================== 2 passed, 3 error in 7.19 seconds ======================

(base) E:\Personal\13_Materials\Python\RA\parallel_tests>

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the worker and master behavior shown by conftest.py and test_parallel.py, then reproduce the command using pytest-xdist with --max-worker-restart=0. Determine whether a session-fixture termination can be distinguished from a test setup error and whether the master can re-schedule the affected tests to another worker. Done means the requested termination scenario is handled without marking the remaining tests as errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
distributed-systems, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.