Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
722006f7f6 | ||
|
|
d9c5f18478 | ||
|
|
bd30e96ab0 | ||
|
|
eb09e22b62 | ||
|
|
319e247de5 | ||
|
|
1c712fe6a0 | ||
|
|
a6a4158b78 |
1
.hgtags
1
.hgtags
@@ -5,3 +5,4 @@ e6c4ce20db4bf65086ff55807a3c306cad7ca393 1.3
|
|||||||
e6c4ce20db4bf65086ff55807a3c306cad7ca393 1.3
|
e6c4ce20db4bf65086ff55807a3c306cad7ca393 1.3
|
||||||
eaf8b1cb7c312883598677231be5bbeea3b5c127 1.3
|
eaf8b1cb7c312883598677231be5bbeea3b5c127 1.3
|
||||||
a423748bf17ee778a37853225210257699cad9c1 1.4
|
a423748bf17ee778a37853225210257699cad9c1 1.4
|
||||||
|
cd44a941c833c098e4899fe3d42a96703754d0d5 1.5
|
||||||
|
|||||||
10
CHANGELOG
10
CHANGELOG
@@ -1,3 +1,13 @@
|
|||||||
|
1.6
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- terser collection reporting
|
||||||
|
|
||||||
|
- fix issue34 - distributed testing with -p plugin now works correctly
|
||||||
|
|
||||||
|
- fix race condition in looponfail mode where a concurrent file removal
|
||||||
|
could cause a crash
|
||||||
|
|
||||||
1.5
|
1.5
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="pytest-xdist",
|
name="pytest-xdist",
|
||||||
version='1.5',
|
version='1.6',
|
||||||
description='py.test xdist plugin for distributed testing and loop-on-failing modes',
|
description='py.test xdist plugin for distributed testing and loop-on-failing modes',
|
||||||
long_description=open('README.txt').read(),
|
long_description=open('README.txt').read(),
|
||||||
license='GPLv2 or later',
|
license='GPLv2 or later',
|
||||||
@@ -13,7 +13,7 @@ setup(
|
|||||||
packages = ['xdist'],
|
packages = ['xdist'],
|
||||||
entry_points = {'pytest11': ['xdist = xdist.plugin'],},
|
entry_points = {'pytest11': ['xdist = xdist.plugin'],},
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires = ['execnet>=1.0.8', 'pytest>1.9.9'],
|
install_requires = ['execnet>=1.0.8', 'pytest>2.0.2'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
|
|||||||
@@ -407,5 +407,18 @@ def test_skipping(testdir):
|
|||||||
"*1 skipped*"
|
"*1 skipped*"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_issue34_pluginloading_in_subprocess(testdir):
|
||||||
|
testdir.tmpdir.join("plugin123.py").write(py.code.Source("""
|
||||||
|
def pytest_namespace():
|
||||||
|
return {'sample_variable': 'testing'}
|
||||||
|
"""))
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
def test_hello():
|
||||||
|
assert pytest.sample_variable == "testing"
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-n1", "-p", "plugin123")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 passed*",
|
||||||
|
])
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from xdist.dsession import DSession, LoadScheduling, EachScheduling
|
from xdist.dsession import DSession, LoadScheduling, EachScheduling
|
||||||
from _pytest import session as outcome
|
from _pytest import main as outcome
|
||||||
import py
|
import py
|
||||||
import execnet
|
import execnet
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ class TestStatRecorder:
|
|||||||
changed = sd.check()
|
changed = sd.check()
|
||||||
assert changed
|
assert changed
|
||||||
|
|
||||||
tmp.ensure("new.py")
|
p = tmp.ensure("new.py")
|
||||||
changed = sd.check()
|
changed = sd.check()
|
||||||
assert changed
|
assert changed
|
||||||
|
|
||||||
tmp.join("new.py").remove()
|
p.remove()
|
||||||
changed = sd.check()
|
changed = sd.check()
|
||||||
assert changed
|
assert changed
|
||||||
|
|
||||||
@@ -36,6 +36,24 @@ class TestStatRecorder:
|
|||||||
changed = sd.check()
|
changed = sd.check()
|
||||||
assert changed
|
assert changed
|
||||||
|
|
||||||
|
def test_filechange_deletion_race(self, tmpdir, monkeypatch):
|
||||||
|
tmp = tmpdir
|
||||||
|
sd = StatRecorder([tmp])
|
||||||
|
changed = sd.check()
|
||||||
|
assert not changed
|
||||||
|
|
||||||
|
p = tmp.ensure("new.py")
|
||||||
|
changed = sd.check()
|
||||||
|
assert changed
|
||||||
|
|
||||||
|
p.remove()
|
||||||
|
# make check()'s visit() call return our just removed
|
||||||
|
# path as if we were in a race condition
|
||||||
|
monkeypatch.setattr(tmp, 'visit', lambda *args: [p])
|
||||||
|
|
||||||
|
changed = sd.check()
|
||||||
|
assert changed
|
||||||
|
|
||||||
def test_pycremoval(self, tmpdir):
|
def test_pycremoval(self, tmpdir):
|
||||||
tmp = tmpdir
|
tmp = tmpdir
|
||||||
hello = tmp.ensure("hello.py")
|
hello = tmp.ensure("hello.py")
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ def test_remoteinitconfig(testdir):
|
|||||||
config1 = testdir.parseconfig()
|
config1 = testdir.parseconfig()
|
||||||
config2 = remote_initconfig(config1.option.__dict__, config1.args)
|
config2 = remote_initconfig(config1.option.__dict__, config1.args)
|
||||||
assert config2.option.__dict__ == config1.option.__dict__
|
assert config2.option.__dict__ == config1.option.__dict__
|
||||||
py.test.raises(KeyError, 'config2.pluginmanager.getplugin("terminal")')
|
assert config2.pluginmanager.getplugin("terminal") in (-1, None)
|
||||||
|
|
||||||
class TestReportSerialization:
|
class TestReportSerialization:
|
||||||
def test_itemreport_outcomes(self, testdir):
|
def test_itemreport_outcomes(self, testdir):
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#
|
#
|
||||||
__version__ = '1.5'
|
__version__ = '1.6'
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ class DSession:
|
|||||||
|
|
||||||
if self.sched.collection_is_completed:
|
if self.sched.collection_is_completed:
|
||||||
if self.terminal:
|
if self.terminal:
|
||||||
|
self.trdist.ensure_show_status()
|
||||||
self.terminal.write_line("")
|
self.terminal.write_line("")
|
||||||
self.terminal.write_line("scheduling tests via %s" %(
|
self.terminal.write_line("scheduling tests via %s" %(
|
||||||
self.sched.__class__.__name__))
|
self.sched.__class__.__name__))
|
||||||
@@ -327,13 +328,19 @@ class TerminalDistReporter:
|
|||||||
def write_line(self, msg):
|
def write_line(self, msg):
|
||||||
self.tr.write_line(msg)
|
self.tr.write_line(msg)
|
||||||
|
|
||||||
|
def ensure_show_status(self):
|
||||||
|
if not self.tr.hasmarkup:
|
||||||
|
self.write_line(self.getstatus())
|
||||||
|
|
||||||
def setstatus(self, spec, status, show=True):
|
def setstatus(self, spec, status, show=True):
|
||||||
self._status[spec.id] = status
|
self._status[spec.id] = status
|
||||||
if show:
|
if show and self.tr.hasmarkup:
|
||||||
parts = ["%s %s" %(spec.id, self._status[spec.id])
|
self.rewrite(self.getstatus())
|
||||||
for spec in self._specs]
|
|
||||||
line = " / ".join(parts)
|
def getstatus(self):
|
||||||
self.rewrite(line)
|
parts = ["%s %s" %(spec.id, self._status[spec.id])
|
||||||
|
for spec in self._specs]
|
||||||
|
return " / ".join(parts)
|
||||||
|
|
||||||
def rewrite(self, line, newline=False):
|
def rewrite(self, line, newline=False):
|
||||||
pline = line + " " * max(self._lastlen-len(line), 0)
|
pline = line + " " * max(self._lastlen-len(line), 0)
|
||||||
@@ -347,8 +354,9 @@ class TerminalDistReporter:
|
|||||||
def pytest_xdist_setupnodes(self, specs):
|
def pytest_xdist_setupnodes(self, specs):
|
||||||
self._specs = specs
|
self._specs = specs
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
self.setstatus(spec, "initializing", show=False)
|
self.setstatus(spec, "I", show=False)
|
||||||
self.setstatus(spec, "initializing", show=True)
|
self.setstatus(spec, "I", show=True)
|
||||||
|
self.ensure_show_status()
|
||||||
|
|
||||||
def pytest_xdist_newgateway(self, gateway):
|
def pytest_xdist_newgateway(self, gateway):
|
||||||
if self.config.option.verbose > 0:
|
if self.config.option.verbose > 0:
|
||||||
@@ -357,7 +365,7 @@ class TerminalDistReporter:
|
|||||||
self.rewrite("[%s] %s Python %s cwd: %s" % (
|
self.rewrite("[%s] %s Python %s cwd: %s" % (
|
||||||
gateway.id, rinfo.platform, version, rinfo.cwd),
|
gateway.id, rinfo.platform, version, rinfo.cwd),
|
||||||
newline=True)
|
newline=True)
|
||||||
self.setstatus(gateway.spec, "collecting")
|
self.setstatus(gateway.spec, "C")
|
||||||
|
|
||||||
def pytest_testnodeready(self, node):
|
def pytest_testnodeready(self, node):
|
||||||
if self.config.option.verbose > 0:
|
if self.config.option.verbose > 0:
|
||||||
@@ -366,7 +374,7 @@ class TerminalDistReporter:
|
|||||||
d['id'],
|
d['id'],
|
||||||
d['version'].replace('\n', ' -- '),)
|
d['version'].replace('\n', ' -- '),)
|
||||||
self.rewrite(infoline, newline=True)
|
self.rewrite(infoline, newline=True)
|
||||||
self.setstatus(node.gateway.spec, "ready")
|
self.setstatus(node.gateway.spec, "ok")
|
||||||
|
|
||||||
def pytest_testnodedown(self, node, error):
|
def pytest_testnodedown(self, node, error):
|
||||||
if not error:
|
if not error:
|
||||||
|
|||||||
@@ -120,9 +120,7 @@ def init_slave_session(channel, args, option_dict):
|
|||||||
|
|
||||||
#fullwidth, hasmarkup = channel.receive()
|
#fullwidth, hasmarkup = channel.receive()
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
config = Config()
|
config = Config.fromdictargs(option_dict, list(args))
|
||||||
config.option.__dict__.update(option_dict)
|
|
||||||
config._preparse(list(args))
|
|
||||||
config.args = args
|
config.args = args
|
||||||
from xdist.looponfail import SlaveFailSession
|
from xdist.looponfail import SlaveFailSession
|
||||||
SlaveFailSession(config, channel).main()
|
SlaveFailSession(config, channel).main()
|
||||||
@@ -203,14 +201,11 @@ class StatRecorder:
|
|||||||
newstat = {}
|
newstat = {}
|
||||||
for rootdir in self.rootdirlist:
|
for rootdir in self.rootdirlist:
|
||||||
for path in rootdir.visit(self.fil, self.rec):
|
for path in rootdir.visit(self.fil, self.rec):
|
||||||
oldstat = statcache.get(path, None)
|
oldstat = statcache.pop(path, None)
|
||||||
if oldstat is not None:
|
|
||||||
del statcache[path]
|
|
||||||
try:
|
try:
|
||||||
newstat[path] = curstat = path.stat()
|
newstat[path] = curstat = path.stat()
|
||||||
except py.error.ENOENT:
|
except py.error.ENOENT:
|
||||||
if oldstat:
|
if oldstat:
|
||||||
del statcache[path]
|
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if oldstat:
|
if oldstat:
|
||||||
|
|||||||
@@ -109,10 +109,8 @@ def getinfodict():
|
|||||||
|
|
||||||
def remote_initconfig(option_dict, args):
|
def remote_initconfig(option_dict, args):
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
config = Config()
|
option_dict['plugins'].append("no:terminal")
|
||||||
config.pluginmanager.unregister(name="terminal")
|
config = Config.fromdictargs(option_dict, args)
|
||||||
config._preparse(args, addopts=False)
|
|
||||||
config.option.__dict__.update(option_dict)
|
|
||||||
config.option.looponfail = False
|
config.option.looponfail = False
|
||||||
config.option.usepdb = False
|
config.option.usepdb = False
|
||||||
config.option.dist = "no"
|
config.option.dist = "no"
|
||||||
|
|||||||
Reference in New Issue
Block a user