2 new commits in pytest-xdist:

https://bitbucket.org/hpk42/pytest-xdist/commits/e95235ad5f8e/
Changeset:   e95235ad5f8e
User:        hpk42
Date:        2014-07-20 16:41:03
Summary:     fix pytest issue503: avoid random re-setup of broad scoped fixtures
(anything above function).
Affected #:  5 files

diff -r 35661a1ed8b5c542fbee90206a0e5ddbefd7f729 -r 
e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,14 @@
-XXX
+1.11
 -------------------------
 
-- fix pytest/xdist issue485 (also depends on py-1.4.21.dev1): 
+- fix pytest/xdist issue485 (also depends on py-1.4.22):
   attach stdout/stderr on --boxed processes that die.
 
+- fix pytest/xdist issue503: make sure that a node has usually
+  two items to execute to avoid scoped fixtures to be torn down
+  pre-maturely (fixture teardown/setup is "nextitem" sensitive).
+  Thanks to Andreas Pelme for bug analysis and failing test.
+
 1.10
 -------------------------
 

diff -r 35661a1ed8b5c542fbee90206a0e5ddbefd7f729 -r 
e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 testing/acceptance_test.py
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -1,3 +1,4 @@
+import pytest
 import py
 import sys
 
@@ -459,3 +460,29 @@
     result.stdout.fnmatch_lines([
         "*1 passed*",
     ])
+
+
+def test_fixture_scope_caching_issue503(testdir):
+    p1 = testdir.makepyfile("""
+            import pytest
+
+            @pytest.fixture(scope='session')
+            def fix():
+                assert fix.counter == 0, 'session fixture was invoked multiple 
times'
+                fix.counter += 1
+            fix.counter = 0
+
+            def test_a(fix):
+                pass
+
+            def test_b(fix):
+                pass
+    """)
+    result = testdir.runpytest(p1, '-v', '-n1')
+    assert result.ret == 0
+    result.stdout.fnmatch_lines([
+    "*2 passed*",
+    ])
+
+
+

diff -r 35661a1ed8b5c542fbee90206a0e5ddbefd7f729 -r 
e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 testing/test_dsession.py
--- a/testing/test_dsession.py
+++ b/testing/test_dsession.py
@@ -98,15 +98,15 @@
         assert sched.node2collection[node1] == collection
         assert sched.node2collection[node2] == collection
         sched.init_distribute()
+        assert not sched.pending
+        assert not sched.tests_finished()
+        assert len(node1.sent) == 2
+        assert len(node2.sent) == 0
+        assert node1.sent == [0, 1]
+        sched.remove_item(node1, node1.sent[0])
         assert sched.tests_finished()
-        assert len(node1.sent) == 1
-        assert len(node2.sent) == 1
-        x = sorted(node1.sent + node2.sent)
-        assert x == [0, 1]
-        sched.remove_item(node1, node1.sent[0])
-        sched.remove_item(node2, node2.sent[0])
+        sched.remove_item(node1, node1.sent[1])
         assert sched.tests_finished()
-        assert not sched.pending
 
     def test_init_distribute_chunksize(self):
         sched = LoadScheduling(2)
@@ -114,22 +114,25 @@
         node2 = MockNode()
         sched.addnode(node1)
         sched.addnode(node2)
-        col = ["xyz"] * (3)
+        col = ["xyz"] * (6)
         sched.addnode_collection(node1, col)
         sched.addnode_collection(node2, col)
         sched.init_distribute()
         #assert not sched.tests_finished()
         sent1 = node1.sent
         sent2 = node2.sent
-        chunkitems = col[:1]
-        assert (sent1 == [0] and sent2 == [1]) or (
-                sent1 == [1] and sent2 == [0])
+        assert sent1 == [0, 1]
+        assert sent2 == [2, 3]
+        assert sched.pending == [4, 5]
         assert sched.node2pending[node1] == sent1
         assert sched.node2pending[node2] == sent2
-        assert len(sched.pending) == 1
-        for node in (node1, node2):
-            for i in sched.node2pending[node]:
-                sched.remove_item(node, i)
+        assert len(sched.pending) == 2
+        sched.remove_item(node1, 0)
+        assert node1.sent == [0, 1, 4]
+        assert sched.pending == [5]
+        assert node2.sent == [2, 3]
+        sched.remove_item(node1, 1)
+        assert node1.sent == [0, 1, 4, 5]
         assert not sched.pending
 
     def test_add_remove_node(self):

diff -r 35661a1ed8b5c542fbee90206a0e5ddbefd7f729 -r 
e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 xdist/dsession.py
--- a/xdist/dsession.py
+++ b/xdist/dsession.py
@@ -49,7 +49,7 @@
         if not pending:
             return
         crashitem = self.node2collection[node][pending.pop(0)]
-        # XXX what about the rest of pending?
+        # XXX do or report something wrt the remaining per-node pending items?
         return crashitem
 
     def init_distribute(self):
@@ -58,11 +58,13 @@
             node.send_runtest_all()
             pending[:] = range(len(self.node2collection[node]))
 
+
 class LoadScheduling:
     def __init__(self, numnodes, log=None):
         self.numnodes = numnodes
         self.node2pending = {}
         self.node2collection = {}
+        self.nodes = []
         self.pending = []
         if log is None:
             self.log = py.log.Producer("loadsched")
@@ -75,13 +77,14 @@
 
     def addnode(self, node):
         self.node2pending[node] = []
+        self.nodes.append(node)
 
     def tests_finished(self):
-        if not self.collection_is_completed or self.pending:
+        if not self.collection_is_completed:
             return False
-        #for items in self.node2pending.values():
-        #    if items:
-        #        return False
+        for pending in self.node2pending.values():
+            if len(pending) >= 2:
+                return False
         return True
 
     def addnode_collection(self, node, collection):
@@ -92,37 +95,46 @@
             self.collection_is_completed = True
 
     def remove_item(self, node, item_index, duration=0):
-        node_pending = self.node2pending[node]
-        node_pending.remove(item_index)
-        # pre-load items-to-test if the node may become ready
+        self.node2pending[node].remove(item_index)
+        self.check_schedule(node, duration=duration)
 
+    def check_schedule(self, node, duration=0):
         if self.pending:
-            if duration >= 0.1 and node_pending:
-                # seems the node is doing long-running tests
-                # so let's rather wait with sending new items
-                return
-            # how many nodes do we have remaining per node roughly?
+            # how many nodes do we have?
             num_nodes = len(self.node2pending)
             # if our node goes below a heuristic minimum, fill it out to
             # heuristic maximum
             items_per_node_min = max(
-                    1, len(self.pending) // num_nodes // 4)
+                    2, len(self.pending) // num_nodes // 4)
             items_per_node_max = max(
-                    1, len(self.pending) // num_nodes // 2)
-            if len(node_pending) <= items_per_node_min:
-                num_send = items_per_node_max - len(node_pending) + 1
+                    2, len(self.pending) // num_nodes // 2)
+            node_pending = self.node2pending[node]
+            if len(node_pending) < items_per_node_min:
+                if duration >= 0.1 and len(node_pending) >= 2:
+                    # seems the node is doing long-running tests
+                    # and has enough items to continue
+                    # so let's rather wait with sending new items
+                    return
+                num_send = items_per_node_max - len(node_pending)
                 self._send_tests(node, num_send)
 
         self.log("num items waiting for node:", len(self.pending))
         #self.log("node2pending:", self.node2pending)
 
     def remove_node(self, node):
+        self.nodes.remove(node)
         pending = self.node2pending.pop(node)
         if not pending:
             return
-        # the node must have crashed on the item if there are pending ones
+        # the node has crashed on the item if there are pending ones
+        # and we are told to remove the node
         crashitem = self.collection[pending.pop(0)]
+
+        # put the remaining items back to the general pending list
         self.pending.extend(pending)
+        # see if some nodes can pick the remaining tests up already
+        for node in self.node2pending:
+            self.check_schedule(node)
         return crashitem
 
     def init_distribute(self):
@@ -147,9 +159,9 @@
         # how many items per node do we have about?
         items_per_node = len(self.collection) // len(self.node2pending)
         # take a fraction of tests for initial distribution
-        node_chunksize = max(items_per_node // 4, 1)
+        node_chunksize = max(items_per_node // 4, 2)
         # and initialize each node with a chunk of tests
-        for node in self.node2pending:
+        for node in self.nodes:
             self._send_tests(node, node_chunksize)
 
     #f = open("/tmp/sent", "w")

diff -r 35661a1ed8b5c542fbee90206a0e5ddbefd7f729 -r 
e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 xdist/remote.py
--- a/xdist/remote.py
+++ b/xdist/remote.py
@@ -51,9 +51,12 @@
             elif name == "runtests_all":
                 torun.extend(range(len(session.items)))
             self.log("items to run:", torun)
-            while torun:
+            # only run if we have an item and a next item
+            while len(torun) >= 2:
                 self.run_tests(torun)
             if name == "shutdown":
+                if torun:
+                    self.run_tests(torun)
                 break
         return True
 


https://bitbucket.org/hpk42/pytest-xdist/commits/4c6c39266031/
Changeset:   4c6c39266031
User:        hpk42
Date:        2014-07-20 16:56:21
Summary:     fix various flakes issues and add "flakes" to tox tests
Affected #:  10 files

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/acceptance_test.py
--- a/testing/acceptance_test.py
+++ b/testing/acceptance_test.py
@@ -1,6 +1,4 @@
-import pytest
 import py
-import sys
 
 class TestDistribution:
     def test_n1_pass(self, testdir):
@@ -194,7 +192,7 @@
         assert dest.join(subdir.basename).check(dir=1)
 
     def test_data_exchange(self, testdir):
-        c1 = testdir.makeconftest("""
+        testdir.makeconftest("""
             # This hook only called on master.
             def pytest_configure_node(node):
                 node.slaveinput['a'] = 42
@@ -251,7 +249,7 @@
 
     def test_keyboard_interrupt_dist(self, testdir):
         # xxx could be refined to check for return code
-        p = testdir.makepyfile("""
+        testdir.makepyfile("""
             def test_sleep():
                 import time
                 time.sleep(10)
@@ -301,7 +299,7 @@
 
 class TestTerminalReporting:
     def test_pass_skip_fail(self, testdir):
-        p = testdir.makepyfile("""
+        testdir.makepyfile("""
             import py
             def test_ok():
                 pass
@@ -323,7 +321,7 @@
         ])
 
     def test_fail_platinfo(self, testdir):
-        p = testdir.makepyfile("""
+        testdir.makepyfile("""
             def test_func():
                 assert 0
         """)

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/conftest.py
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -7,14 +7,11 @@
 
 def pytest_addoption(parser):
     parser.addoption('--gx',
-       action="append", dest="gspecs", 
+       action="append", dest="gspecs",
        help=("add a global test environment, XSpec-syntax. "))
 
 def pytest_funcarg__specssh(request):
     return getspecssh(request.config)
-def getgspecs(config):
-    return [execnet.XSpec(spec)
-                for spec in config.getvalueorskip("gspecs")]
 
 # configuration information for tests
 def getgspecs(config):

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/test_dsession.py
--- a/testing/test_dsession.py
+++ b/testing/test_dsession.py
@@ -4,7 +4,6 @@
     EachScheduling,
     report_collection_diff,
 )
-from _pytest import main as outcome
 import py
 import pytest
 import execnet

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/test_looponfail.py
--- a/testing/test_looponfail.py
+++ b/testing/test_looponfail.py
@@ -42,7 +42,7 @@
 
     def test_dirchange(self, tmpdir):
         tmp = tmpdir
-        hello = tmp.ensure("dir", "hello.py")
+        tmp.ensure("dir", "hello.py")
         sd = StatRecorder([tmp])
         assert not sd.fil(tmp.join("dir"))
 

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/test_remote.py
--- a/testing/test_remote.py
+++ b/testing/test_remote.py
@@ -3,7 +3,6 @@
 from xdist.remote import serialize_report
 import execnet
 queue = py.builtin._tryimport("queue", "Queue")
-from py.builtin import print_
 import marshal
 
 WAIT_TIMEOUT = 10.0
@@ -26,7 +25,7 @@
     use_callback = False
 
     def __init__(self, request):
-        self.testdir = testdir = request.getfuncargvalue("testdir")
+        self.testdir = request.getfuncargvalue("testdir")
         self.request = request
         self.events = queue.Queue()
 
@@ -140,7 +139,7 @@
 
 class TestSlaveInteractor:
     def test_basic_collect_and_runtests(self, slave):
-        p = slave.testdir.makepyfile("""
+        slave.testdir.makepyfile("""
             def test_func():
                 pass
         """)
@@ -170,7 +169,7 @@
         assert 'slaveoutput' in ev.kwargs
 
     def test_remote_collect_skip(self, slave):
-        p = slave.testdir.makepyfile("""
+        slave.testdir.makepyfile("""
             import py
             py.test.skip("hello")
         """)
@@ -187,7 +186,7 @@
         assert not ev.kwargs['ids']
 
     def test_remote_collect_fail(self, slave):
-        p = slave.testdir.makepyfile("""aasd qwe""")
+        slave.testdir.makepyfile("""aasd qwe""")
         slave.setup()
         ev = slave.popevent("collectionstart")
         assert not ev.kwargs
@@ -201,7 +200,7 @@
         assert not ev.kwargs['ids']
 
     def test_runtests_all(self, slave):
-        p = slave.testdir.makepyfile("""
+        slave.testdir.makepyfile("""
             def test_func(): pass
             def test_func2(): pass
         """)
@@ -228,7 +227,7 @@
     def test_happy_run_events_converted(self, testdir, slave):
         py.test.xfail("implement a simple test for event production")
         assert not slave.use_callback
-        p = slave.testdir.makepyfile("""
+        slave.testdir.makepyfile("""
             def test_func():
                 pass
         """)

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 testing/test_slavemanage.py
--- a/testing/test_slavemanage.py
+++ b/testing/test_slavemanage.py
@@ -1,5 +1,4 @@
 import py
-import os
 import execnet
 from xdist.slavemanage import HostRSync, NodeManager
 
@@ -108,7 +107,7 @@
         return mysetup(request)
 
     def test_hrsync_filter(self, mysetup):
-        source, dest = mysetup.source, mysetup.dest
+        source, _ = mysetup.source, mysetup.dest  # noqa
         source.ensure("dir", "file.txt")
         source.ensure(".svn", "entries")
         source.ensure(".somedotfile", "moreentries")
@@ -139,7 +138,7 @@
     @py.test.mark.xfail
     def test_rsync_roots_no_roots(self, testdir, mysetup):
         mysetup.source.ensure("dir1", "file1").write("hello")
-        config = testdir.parseconfig(source)
+        config = testdir.parseconfig(mysetup.source)
         nodemanager = NodeManager(config, ["popen//chdir=%s" % mysetup.dest])
         #assert nodemanager.config.topdir == source == config.topdir
         nodemanager.makegateways()
@@ -194,11 +193,11 @@
     def test_rsyncignore(self, testdir, mysetup):
         source, dest = mysetup.source, mysetup.dest
         dir2 = source.ensure("dir1", "dir2", dir=1)
-        dir5 = source.ensure("dir5", "dir6", "bogus")
-        dirf = source.ensure("dir5", "file")
+        source.ensure("dir5", "dir6", "bogus")
+        source.ensure("dir5", "file")
         dir2.ensure("hello")
-        dirfoo = source.ensure("foo", "bar")
-        dirbar = source.ensure("bar", "foo")
+        source.ensure("foo", "bar")
+        source.ensure("bar", "foo")
         source.join("tox.ini").write(py.std.textwrap.dedent("""
             [pytest]
             rsyncdirs = dir1 dir5
@@ -217,7 +216,7 @@
         assert not dest.join('bar').check()
 
     def test_optimise_popen(self, testdir, mysetup):
-        source, dest = mysetup.source, mysetup.dest
+        source = mysetup.source
         specs = ["popen"] * 3
         source.join("conftest.py").write("rsyncdirs = ['a']")
         source.ensure('a', dir=1)

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=py26,py32,py33,py34,py27,py27-pexpect,py33-pexpect,py26,py26-old,py33-old
+envlist=py26,py32,py33,py34,py27,py27-pexpect,py33-pexpect,py26,py26-old,py33-old,flakes
 
 [testenv]
 changedir=testing
@@ -13,6 +13,11 @@
 deps={[testenv]deps}
     pexpect
 
+[testenv:flakes]
+changedir=
+deps = pytest-flakes>=0.2
+commands = py.test --flakes -m flakes testing xdist
+
 [testenv:py26-old]
 deps=
     pytest==2.4.2

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 xdist/dsession.py
--- a/xdist/dsession.py
+++ b/xdist/dsession.py
@@ -1,4 +1,3 @@
-import sys
 import difflib
 
 import pytest
@@ -295,7 +294,7 @@
             self.slave_errordown(node, "keyboard-interrupt")
             return
         crashitem = self.sched.remove_node(node)
-        #assert not crashitem, (crashitem, node)
+        assert not crashitem, (crashitem, node)
         if self.shuttingdown and not self.sched.hasnodes():
             self.session_finished = True
 

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 xdist/looponfail.py
--- a/xdist/looponfail.py
+++ b/xdist/looponfail.py
@@ -87,7 +87,7 @@
         result = self.runsession()
         failures, reports, collection_failed = result
         if collection_failed:
-            reports = ["Collection failed, keeping previous failure set"]
+            pass # "Collection failed, keeping previous failure set"
         else:
             uniq_failures = []
             for failure in failures:
@@ -109,7 +109,6 @@
 
 def init_slave_session(channel, args, option_dict):
     import os, sys
-    import py
     outchannel = channel.gateway.newchannel()
     sys.stdout = sys.stderr = outchannel.makefile('w')
     channel.send(outchannel)

diff -r e95235ad5f8e07f3ba0cad07a0d76e899066ffd1 -r 
4c6c3926603135015691bcf537ed8dfe15b59874 xdist/remote.py
--- a/xdist/remote.py
+++ b/xdist/remote.py
@@ -128,6 +128,7 @@
 
 
 if __name__ == '__channelexec__':
+    channel = channel  # noqa
     # python3.2 is not concurrent import safe, so let's play it safe
     # https://bitbucket.org/hpk42/pytest/issue/347/pytest-xdist-and-python-32
     if sys.version_info[:2] == (3,2):

Repository URL: https://bitbucket.org/hpk42/pytest-xdist/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
https://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to