3.6 KiB
Overview
Here it is described a brief overview of xdist's internal architecture.
xdist works by spawning one or more worker nodes, which are controlled
by the master node. Each worker node is responsible for performing
a full test collection and afterwards running tests as dictated by the master node.
The execution flow is:
-
master node spawns one or more worker nodes at the begginning of the test session. The communication between master and worker nodes makes use of execnet and its gateways. The actual interpreters executing the code for the worker nodes might be remote or local.
-
Each worker node itself is a mini pytest runner. workers at this point perform a full test collection, sending back the collected test-ids back to the master node which does not perform any collection itself.
-
The master node receives the result of the collection from all nodes. At this point the master node performs some sanity check to ensure that all worker nodes collected the same tests (including order), bailing out otherwise. If all is well, it converts the list of test-ids into a list of simple indexes, where each index corresponds to the position of that test in the original collection list. This works because all nodes have the same collection list, and saves bandwidth because the master can now tell one of the workers to just execute test index 3 index of passing the full test id.
-
If dist-mode is each: the master node just sends the full list of test indexes to each node at this moment.
-
If dist-mode is load: the master node takes around 25% of the tests and sends them one by one to each worker node in a round robin fashion. The rest of the tests will be distributed later as worker nodes finish tests (see below).
-
worker nodes re-implement
pytest_runtestloop: pytest's default implementation basically loops over all collected items in thesessionobject and executes thepytest_runtest_protocolfor each test item, but in xdist workers sit idly waiting for master node to send tests for execution. As tests are received by workers,pytest_runtest_protocolis executed for each test. Here it worth noting an implementation detail: at least one test is kept always in worker nodes must they comply withpytest_runtest_protocolin that it needs to know which will be thenextitemin the hook call: either a new test in case the master node sends a new test, orNoneif the worker receives a "shutdown" request. -
As tests are started and completed at the workers, the results are sent back to the master node, which then just forwards the results to the appropriate pytest hooks:
pytest_runtest_logstartandpytest_runtest_logreport. This way other plugins (for examplejunitxml) can work normally. The master node (when in dist-mode load) decides to send more tests to a node when a test completes, using some heuristics such as test durations and how many tests each worker node still has to run. -
When the master node has no more pending tests it will send a "shutdown" signal to all workers, which will then run their remaining tests to completion and shut down. At this point the master node will sit waiting for workers to shut down, still processing events such as
pytest_runtest_logreport.