diff --git a/testing/test_dsession.py b/testing/test_dsession.py index 92746b3..c01f899 100644 --- a/testing/test_dsession.py +++ b/testing/test_dsession.py @@ -1,4 +1,9 @@ -from xdist.dsession import DSession, LoadScheduling, EachScheduling +from xdist.dsession import ( + DSession, + LoadScheduling, + EachScheduling, + report_collection_diff, +) from _pytest import main as outcome import py import execnet @@ -165,3 +170,36 @@ class TestDistReporter: linecomp.assert_contains_lines([ "[X1,X2] rsyncing: hello", ]) + + +def test_report_collection_diff_equal(): + """Test reporting of equal collections.""" + from_collection = to_collection = ['aaa', 'bbb', 'ccc'] + assert report_collection_diff(from_collection, to_collection, 1, 2) + + +def test_report_collection_diff_different(): + """Test reporting of different collections.""" + from_collection = ['aaa', 'bbb', 'ccc', 'YYY'] + to_collection = ['aZa', 'bbb', 'XXX', 'ccc'] + error_message = ( + u'Different tests were collected between 1 and 2. The difference is:\n' + u'--- 1 \n' + u'\n' + u'+++ 2 \n' + u'\n' + u'@@ -1,4 +1,4 @@\n' + u'\n' + u'-aaa\n' + u'+aZa\n' + u' bbb\n' + u'+XXX\n' + u' ccc\n' + u'-YYY' + ) + + try: + report_collection_diff(from_collection, to_collection, 1, 2) + except AssertionError as e: + + assert unicode(e) == error_message diff --git a/xdist/dsession.py b/xdist/dsession.py index 6ffc80c..2fe785c 100644 --- a/xdist/dsession.py +++ b/xdist/dsession.py @@ -1,8 +1,14 @@ -import pytest, py import sys +import difflib + +import pytest +import py from xdist.slavemanage import NodeManager + + queue = py.builtin._tryimport('queue', 'Queue') + class EachScheduling: def __init__(self, numnodes, log=None): @@ -127,15 +133,17 @@ class LoadScheduling: assert not hasattr(self, 'item2nodes') self.item2nodes = {} # XXX allow nodes to have different collections - col = list(self.node2collection.values())[0] + first_node, col = list(self.node2collection.items())[0] for node, collection in self.node2collection.items(): - assert collection == col + if collection != col: + report_collection_diff(col, collection, first_node.gateway.id, node.gateway.id) + self.pending = col if not col: return available = list(self.node2pending.items()) num_available = self.numnodes - max_one_round = num_available * self.ITEM_CHUNKSIZE -1 + max_one_round = num_available * self.ITEM_CHUNKSIZE - 1 for i, item in enumerate(self.pending): nodeindex = i % num_available node, pending = available[nodeindex] @@ -144,7 +152,35 @@ class LoadScheduling: pending.append(item) if i >= max_one_round: break - del self.pending[:i+1] + del self.pending[:i + 1] + + +def report_collection_diff(from_collection, to_collection, from_id, to_id): + """Report the collected test difference between two nodes. + + :returns: True if collections are equal. + + :raises: AssertionError with a detailed error message describing the + difference between the collections. + + """ + if from_collection == to_collection: + return True + + diff = difflib.unified_diff( + from_collection, + to_collection, + fromfile=from_id, + tofile=to_id, + ) + error_message = ( + u'Different tests were collected between {from_id} and {to_id}. ' + u'The difference is:\n' + u'{diff}' + ).format(from_id=from_id, to_id=to_id, diff='\n'.join(diff)) + + raise AssertionError(error_message) + class Interrupted(KeyboardInterrupt): """ signals an immediate interruption. """