From bd8f68555d92f58a566d7d160d3970406ed4ddb1 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Sun, 28 Jun 2009 13:19:43 +0200 Subject: [PATCH 1/4] re-adding py.test.mark as documented (!) by adding pytest_keyword plugin --HG-- branch : 1.0.x --- CHANGELOG | 2 + MANIFEST | 3 +- doc/test/features.txt | 4 +- py/__init__.py | 2 +- py/test/defaultconftest.py | 2 +- py/test/plugin/pytest_keyword.py | 79 ++++++++++++++++++++++++++++++++ setup.py | 7 +-- 7 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 py/test/plugin/pytest_keyword.py diff --git a/CHANGELOG b/CHANGELOG index f0a0e9e91..b6be77d1f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 1.0.0b3 and 1.0.0 ============================================= +* re-added py.test.mark decorator for setting keywords on functions + * remove scope-argument from request.addfinalizer() because request.cached_setup has the scope arg. TOOWTDI. diff --git a/MANIFEST b/MANIFEST index 2919e5156..e84a3d206 100644 --- a/MANIFEST +++ b/MANIFEST @@ -320,6 +320,7 @@ py/test/plugin/pytest_execnetcleanup.py py/test/plugin/pytest_figleaf.py py/test/plugin/pytest_hooklog.py py/test/plugin/pytest_iocapture.py +py/test/plugin/pytest_keyword.py py/test/plugin/pytest_monkeypatch.py py/test/plugin/pytest_pdb.py py/test/plugin/pytest_pocoo.py @@ -383,4 +384,4 @@ py/xmlobj/testing/__init__.py py/xmlobj/testing/test_html.py py/xmlobj/testing/test_xml.py py/xmlobj/visit.py -py/xmlobj/xml.py \ No newline at end of file +py/xmlobj/xml.py diff --git a/doc/test/features.txt b/doc/test/features.txt index 7cd9babba..5898e2b0e 100644 --- a/doc/test/features.txt +++ b/doc/test/features.txt @@ -237,13 +237,15 @@ keyword. By default, all filename parts and class/function names of a test function are put into the set -of keywords for a given test. You may specify additional +of keywords for a given test. You can specify additional kewords like this:: @py.test.mark(webtest=True) def test_send_http(): ... +and then use those keywords to select tests. + disabling a test class ---------------------- diff --git a/py/__init__.py b/py/__init__.py index 1d156dacf..840f77681 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -19,7 +19,7 @@ For questions please check out http://pylib.org/contact.html """ from initpkg import initpkg -version = "1.0.0b5" +version = "1.0.0b6" initpkg(__name__, description = "py.test and pylib: advanced testing tool and networking lib", diff --git a/py/test/defaultconftest.py b/py/test/defaultconftest.py index 78596f81a..6c7459df5 100644 --- a/py/test/defaultconftest.py +++ b/py/test/defaultconftest.py @@ -10,5 +10,5 @@ Generator = py.test.collect.Generator Function = py.test.collect.Function Instance = py.test.collect.Instance -pytest_plugins = "default runner terminal xfail tmpdir execnetcleanup monkeypatch recwarn pdb".split() +pytest_plugins = "default runner terminal keyword xfail tmpdir execnetcleanup monkeypatch recwarn pdb".split() diff --git a/py/test/plugin/pytest_keyword.py b/py/test/plugin/pytest_keyword.py new file mode 100644 index 000000000..290187226 --- /dev/null +++ b/py/test/plugin/pytest_keyword.py @@ -0,0 +1,79 @@ +""" + py.test.mark / keyword plugin + +""" +import py + +def pytest_namespace(config): + mark = KeywordDecorator({}) + return {'mark': mark} + +class KeywordDecorator: + """ decorator for setting function attributes. """ + def __init__(self, keywords, lastname=None): + self._keywords = keywords + self._lastname = lastname + + def __call__(self, func=None, **kwargs): + if func is None: + kw = self._keywords.copy() + kw.update(kwargs) + return KeywordDecorator(kw) + elif not hasattr(func, 'func_dict'): + kw = self._keywords.copy() + name = self._lastname + if name is None: + name = "mark" + kw[name] = func + return KeywordDecorator(kw) + func.func_dict.update(self._keywords) + return func + + def __getattr__(self, name): + if name[0] == "_": + raise AttributeError(name) + kw = self._keywords.copy() + kw[name] = True + return self.__class__(kw, lastname=name) + +def test_pytest_mark_getattr(): + mark = KeywordDecorator({}) + def f(): pass + + mark.hello(f) + assert f.hello == True + + mark.hello("test")(f) + assert f.hello == "test" + + py.test.raises(AttributeError, "mark._hello") + py.test.raises(AttributeError, "mark.__str__") + +def test_pytest_mark_call(): + mark = KeywordDecorator({}) + def f(): pass + mark(x=3)(f) + assert f.x == 3 + def g(): pass + mark(g) + assert not g.func_dict + + mark.hello(f) + assert f.hello == True + + mark.hello("test")(f) + assert f.hello == "test" + + mark("x1")(f) + assert f.mark == "x1" + +def test_mark_plugin(testdir): + p = testdir.makepyfile(""" + import py + pytest_plugins = "keyword" + @py.test.mark.hello + def test_hello(): + assert hasattr(test_hello, 'hello') + """) + result = testdir.runpytest(p) + assert result.stdout.fnmatch_lines(["*passed*"]) diff --git a/setup.py b/setup.py index 390b597cc..88c89085f 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,5 @@ """ autogenerated by gensetup.py -setup file for 'py' package based on: - -revision: 1181:8a8203ee5eb85837b6a40d95d861af42008d1a4c """ import os, sys @@ -35,7 +32,7 @@ def main(): name='py', description='py.test and pylib: advanced testing tool and networking lib', long_description = long_description, - version='1.0.0b5', + version='1.0.0b6', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -149,4 +146,4 @@ def main(): if __name__ == '__main__': main() - \ No newline at end of file + From 8b7dfb29c5936227494c671a6855c4efacacd934 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Sun, 28 Jun 2009 13:27:34 +0200 Subject: [PATCH 2/4] re-introduce py.test.mark and move py.test.xfail to py.test.mark.xfail --HG-- branch : 1.0.x --- CHANGELOG | 6 +++++- py/execnet/testing/test_gateway.py | 6 +++--- py/test/dist/testing/test_dsession.py | 2 +- py/test/dist/testing/test_nodemanage.py | 2 +- py/test/plugin/pytest_xfail.py | 14 +++++--------- py/test/testing/test_pluginmanager.py | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b6be77d1f..7fb6ca16e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,11 @@ -Changes between 1.0.0b3 and 1.0.0 +Changes between 1.0.0b3 and 1.0.0b6 ============================================= +* renamed py.test.xfail back to py.test.mark.xfail to avoid + two ways to decorate for xfail + * re-added py.test.mark decorator for setting keywords on functions + (it was actually documented so removing it was not nice) * remove scope-argument from request.addfinalizer() because request.cached_setup has the scope arg. TOOWTDI. diff --git a/py/execnet/testing/test_gateway.py b/py/execnet/testing/test_gateway.py index f5bba5b90..7b8514e38 100644 --- a/py/execnet/testing/test_gateway.py +++ b/py/execnet/testing/test_gateway.py @@ -543,7 +543,7 @@ class TestPopenGateway(PopenGatewayTestSetup, BasicRemoteExecution): ret = channel.receive() assert ret == 42 - @py.test.xfail # "fix needed: dying remote process does not cause waitclose() to fail" + @py.test.mark.xfail # "fix needed: dying remote process does not cause waitclose() to fail" def test_waitclose_on_remote_killed(self): gw = py.execnet.PopenGateway() channel = gw.remote_exec(""" @@ -616,12 +616,12 @@ class TestSshGateway(BasicRemoteExecution): def test_sshaddress(self): assert self.gw.remoteaddress == self.sshhost - @py.test.xfail # XXX ssh-gateway error handling + @py.test.mark.xfail # XXX ssh-gateway error handling def test_connexion_failes_on_non_existing_hosts(self): py.test.raises(IOError, "py.execnet.SshGateway('nowhere.codespeak.net')") - @py.test.xfail # "XXX ssh-gateway error handling" + @py.test.mark.xfail # "XXX ssh-gateway error handling" def test_deprecated_identity(self): py.test.deprecated_call( py.test.raises, IOError, diff --git a/py/test/dist/testing/test_dsession.py b/py/test/dist/testing/test_dsession.py index 542ae9ba8..02679a75c 100644 --- a/py/test/dist/testing/test_dsession.py +++ b/py/test/dist/testing/test_dsession.py @@ -367,7 +367,7 @@ class TestDSession: assert node.gateway.spec.popen #XXX eq.geteventargs("pytest_sessionfinish") - @py.test.xfail + @py.test.mark.xfail def test_collected_function_causes_remote_skip_at_module_level(self, testdir): p = testdir.makepyfile(""" import py diff --git a/py/test/dist/testing/test_nodemanage.py b/py/test/dist/testing/test_nodemanage.py index b95a1c4c8..da4fd979b 100644 --- a/py/test/dist/testing/test_nodemanage.py +++ b/py/test/dist/testing/test_nodemanage.py @@ -11,7 +11,7 @@ class pytest_funcarg__mysetup: request.getfuncargvalue("_pytest") class TestNodeManager: - @py.test.xfail + @py.test.mark.xfail def test_rsync_roots_no_roots(self, mysetup): mysetup.source.ensure("dir1", "file1").write("hello") config = py.test.config._reparse([source]) diff --git a/py/test/plugin/pytest_xfail.py b/py/test/plugin/pytest_xfail.py index 77e9274b0..d53bd1a6b 100644 --- a/py/test/plugin/pytest_xfail.py +++ b/py/test/plugin/pytest_xfail.py @@ -3,13 +3,15 @@ mark tests as expected-to-fail and report them separately. example: - @py.test.xfail + @py.test.mark.xfail def test_hello(): ... assert 0 """ import py +pytest_plugins = ['keyword'] + def pytest_runtest_makereport(__call__, item, call): if call.when != "call": return @@ -52,12 +54,6 @@ def pytest_terminal_summary(terminalreporter): for event in xpassed: tr._tw.line("%s: xpassed" %(event.item,)) -def xfail_decorator(func): - func.xfail = True - return func - -def pytest_namespace(config): - return dict(xfail=xfail_decorator) # =============================================================================== # @@ -68,11 +64,11 @@ def pytest_namespace(config): def test_xfail(testdir, linecomp): p = testdir.makepyfile(test_one=""" import py - @py.test.xfail + @py.test.mark.xfail def test_this(): assert 0 - @py.test.xfail + @py.test.mark.xfail def test_that(): assert 1 """) diff --git a/py/test/testing/test_pluginmanager.py b/py/test/testing/test_pluginmanager.py index e1d4ec3e8..b1fc23f28 100644 --- a/py/test/testing/test_pluginmanager.py +++ b/py/test/testing/test_pluginmanager.py @@ -221,7 +221,7 @@ class TestPytestPluginInteractions: assert not pluginmanager.listattr("hello") assert pluginmanager.listattr("x") == [42] - @py.test.xfail # setup call methods + @py.test.mark.xfail # setup call methods def test_call_setup_participants(self, testdir): testdir.makepyfile( conftest=""" From 7cb096373bc2a3312ed4c1ebdfde1a0d35a7dbb8 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 29 Jun 2009 07:32:33 +0100 Subject: [PATCH 3/4] fix typo --HG-- branch : 1.0.x --- py/test/plugin/hookspec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/test/plugin/hookspec.py b/py/test/plugin/hookspec.py index 07c65ce99..f1cfc108b 100644 --- a/py/test/plugin/hookspec.py +++ b/py/test/plugin/hookspec.py @@ -90,7 +90,7 @@ def pytest_runtest_protocol(item): pytest_runtest_protocol.firstresult = True def pytest_runtest_makereport(item, call): - """ make ItemTestReport for the specified test outcome. """ + """ make ItemTestReport for the given item and call outcome. """ pytest_runtest_makereport.firstresult = True def pytest_runtest_logreport(rep): From 608ff40cc13709c45141de4bdd0edc8c731eb192 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Mon, 29 Jun 2009 11:40:24 +0100 Subject: [PATCH 4/4] Added tag 1.0.0b6 for changeset 2cc0507f117f --HG-- branch : 1.0.x --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index e5cfd8b7a..973aa6d7d 100644 --- a/.hgtags +++ b/.hgtags @@ -6,3 +6,4 @@ 8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4 8cd6eb91eba313b012d6e568f37d844dc0751f2e 1.0.0b4 0000000000000000000000000000000000000000 1.0.0b4 +2cc0507f117ffe721dff7ee026648cfce00ec92f 1.0.0b6