1 new commit in pytest:
https://bitbucket.org/hpk42/pytest/commits/bd9f28f011ea/
Changeset: bd9f28f011ea
Branch: yield-test-run-inline
User: RonnyPfannschmidt
Date: 2015-02-10 23:33:18+00:00
Summary: addresses #16 - replace Generator with
pytest_pyfunc_interpret_result
Affected #: 10 files
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,16 @@
2.7.0.dev (compared to 2.6.4)
-----------------------------
+- introduce the hook pytest_pyfunc_interpret_result
+ this eases interpreting test functions results like generators,
+ twisted inline defereds, futures and asyncio generators
+
+- replace the Generator concept with the pytest_pyfunc_interpret_result hook
+ (Note: this change reduces reporting detail for generator tests,
+ that will be addressed in a later release)
+
+ the Generator class is now a alias to Function
+
- add ability to set command line options by environment variable
PYTEST_ADDOPTS.
- fix issue655: work around different ways that cause python2/3
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -136,6 +136,10 @@
""" call underlying test function. """
pytest_pyfunc_call.firstresult = True
+
+def pytest_pyfunc_interpret_result(pyfuncitem, result):
+ """ interpret the return value of the underlying test function. """
+
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f _pytest/nose.py
--- a/_pytest/nose.py
+++ b/_pytest/nose.py
@@ -27,13 +27,6 @@
@pytest.mark.trylast
def pytest_runtest_setup(item):
if is_potential_nosetest(item):
- if isinstance(item.parent, pytest.Generator):
- gen = item.parent
- if not hasattr(gen, '_nosegensetup'):
- call_optional(gen.obj, 'setup')
- if isinstance(gen.parent, pytest.Instance):
- call_optional(gen.parent.obj, 'setup')
- gen._nosegensetup = True
if not call_optional(item.obj, 'setup'):
# call module level setup if there is no object level one
call_optional(item.parent.obj, 'setup')
@@ -49,11 +42,6 @@
# del item.parent._nosegensetup
-def pytest_make_collect_report(collector):
- if isinstance(collector, pytest.Generator):
- call_optional(collector.obj, 'setup')
-
-
def is_potential_nosetest(item):
# extra check needed since we do not do nose style setup/teardown
# on direct unittest style classes
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -177,7 +177,9 @@
'raises' : raises,
'collect': {
'Module': Module, 'Class': Class, 'Instance': Instance,
- 'Function': Function, 'Generator': Generator,
+ 'Function': Function,
+ # TODO: backward compatibility check
+ 'Generator': Function,
'_fillfuncargs': fillfixtures}
}
@@ -188,16 +190,38 @@
@pytest.mark.trylast
+def pytest_pyfunc_interpret_result(pyfuncitem, result):
+ if inspect.isgenerator(result):
+ pyfuncitem.warn(
+ code='G01',
+ message='generator test, reporting is limited')
+ for check in result:
+ # TODO(ronny): subtest support needed
+ if callable(check):
+ check()
+ elif not isinstance(check, tuple):
+ pytest.fail('%r is not a check' % (check,))
+ elif callable(check[0]):
+ check[0](*check[1:])
+ elif callable(check[1]):
+ check[1](*check[2:])
+ else:
+ pytest.fail(
+ 'generator test check not callable \n%r' % (check,))
+
+
[email protected]
def pytest_pyfunc_call(pyfuncitem):
testfunction = pyfuncitem.obj
- if pyfuncitem._isyieldedfunction():
- testfunction(*pyfuncitem._args)
- else:
- funcargs = pyfuncitem.funcargs
- testargs = {}
- for arg in pyfuncitem._fixtureinfo.argnames:
- testargs[arg] = funcargs[arg]
- testfunction(**testargs)
+ funcargs = pyfuncitem.funcargs
+ testargs = {}
+ for arg in pyfuncitem._fixtureinfo.argnames:
+ testargs[arg] = funcargs[arg]
+ result = testfunction(**testargs)
+ if result is not None:
+ pyfuncitem.ihook.pytest_pyfunc_interpret_result(
+ pyfuncitem=pyfuncitem,
+ result=result)
return True
def pytest_collect_file(path, parent):
@@ -235,10 +259,7 @@
"cannot collect %r because it is not a function."
% name, )
if getattr(obj, "__test__", True):
- if is_generator(obj):
- res = Generator(name, parent=collector)
- else:
- res = list(collector._genfunctions(name, obj))
+ res = list(collector._genfunctions(name, obj))
outcome.force_result(res)
def is_generator(func):
@@ -621,43 +642,6 @@
return self._repr_failure_py(excinfo, style=style)
-class Generator(FunctionMixin, PyCollector):
- def collect(self):
- # test generators are seen as collectors but they also
- # invoke setup/teardown on popular request
- # (induced by the common "test_*" naming shared with normal tests)
- self.session._setupstate.prepare(self)
- # see FunctionMixin.setup and test_setupstate_is_preserved_134
- self._preservedparent = self.parent.obj
- l = []
- seen = {}
- for i, x in enumerate(self.obj()):
- name, call, args = self.getcallargs(x)
- if not callable(call):
- raise TypeError("%r yielded non callable test %r" %(self.obj,
call,))
- if name is None:
- name = "[%d]" % i
- else:
- name = "['%s']" % name
- if name in seen:
- raise ValueError("%r generated tests with non-unique name %r"
%(self, name))
- seen[name] = True
- l.append(self.Function(name, self, args=args, callobj=call))
- return l
-
- def getcallargs(self, obj):
- if not isinstance(obj, (tuple, list)):
- obj = (obj,)
- # explict naming
- if isinstance(obj[0], py.builtin._basestring):
- name = obj[0]
- obj = obj[1:]
- else:
- name = None
- call, args = obj[0], obj[1:]
- return name, call, args
-
-
def hasinit(obj):
init = getattr(obj, '__init__', None)
if init:
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -76,199 +76,6 @@
])
-class TestGenerator:
- def test_generative_functions(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
-
- def test_gen():
- yield func1, 17, 3*5
- yield func1, 42, 6*7
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == '[0]'
- assert gencolitems[0].obj.__name__ == 'func1'
-
- def test_generative_methods(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
- class TestGenMethods:
- def test_gen(self):
- yield func1, 17, 3*5
- yield func1, 42, 6*7
- """)
- gencol = modcol.collect()[0].collect()[0].collect()[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == '[0]'
- assert gencolitems[0].obj.__name__ == 'func1'
-
- def test_generative_functions_with_explicit_names(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
-
- def test_gen():
- yield "seventeen", func1, 17, 3*5
- yield "fortytwo", func1, 42, 6*7
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == "['seventeen']"
- assert gencolitems[0].obj.__name__ == 'func1'
- assert gencolitems[1].name == "['fortytwo']"
- assert gencolitems[1].obj.__name__ == 'func1'
-
- def test_generative_functions_unique_explicit_names(self, testdir):
- # generative
- modcol = testdir.getmodulecol("""
- def func(): pass
- def test_gen():
- yield "name", func
- yield "name", func
- """)
- colitems = modcol.collect()
- assert len(colitems) == 1
- gencol = colitems[0]
- assert isinstance(gencol, pytest.Generator)
- pytest.raises(ValueError, "gencol.collect()")
-
- def test_generative_methods_with_explicit_names(self, testdir):
- modcol = testdir.getmodulecol("""
- def func1(arg, arg2):
- assert arg == arg2
- class TestGenMethods:
- def test_gen(self):
- yield "m1", func1, 17, 3*5
- yield "m2", func1, 42, 6*7
- """)
- gencol = modcol.collect()[0].collect()[0].collect()[0]
- assert isinstance(gencol, pytest.Generator)
- gencolitems = gencol.collect()
- assert len(gencolitems) == 2
- assert isinstance(gencolitems[0], pytest.Function)
- assert isinstance(gencolitems[1], pytest.Function)
- assert gencolitems[0].name == "['m1']"
- assert gencolitems[0].obj.__name__ == 'func1'
- assert gencolitems[1].name == "['m2']"
- assert gencolitems[1].obj.__name__ == 'func1'
-
- def test_order_of_execution_generator_same_codeline(self, testdir, tmpdir):
- o = testdir.makepyfile("""
- def test_generative_order_of_execution():
- import py, pytest
- test_list = []
- expected_list = list(range(6))
-
- def list_append(item):
- test_list.append(item)
-
- def assert_order_of_execution():
- py.builtin.print_('expected order', expected_list)
- py.builtin.print_('but got ', test_list)
- assert test_list == expected_list
-
- for i in expected_list:
- yield list_append, i
- yield assert_order_of_execution
- """)
- reprec = testdir.inline_run(o)
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 7
- assert not skipped and not failed
-
- def test_order_of_execution_generator_different_codeline(self, testdir):
- o = testdir.makepyfile("""
- def test_generative_tests_different_codeline():
- import py, pytest
- test_list = []
- expected_list = list(range(3))
-
- def list_append_2():
- test_list.append(2)
-
- def list_append_1():
- test_list.append(1)
-
- def list_append_0():
- test_list.append(0)
-
- def assert_order_of_execution():
- py.builtin.print_('expected order', expected_list)
- py.builtin.print_('but got ', test_list)
- assert test_list == expected_list
-
- yield list_append_0
- yield list_append_1
- yield list_append_2
- yield assert_order_of_execution
- """)
- reprec = testdir.inline_run(o)
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 4
- assert not skipped and not failed
-
- def test_setupstate_is_preserved_134(self, testdir):
- # yield-based tests are messy wrt to setupstate because
- # during collection they already invoke setup functions
- # and then again when they are run. For now, we want to make sure
- # that the old 1.3.4 behaviour is preserved such that all
- # yielded functions all share the same "self" instance that
- # has been used during collection.
- o = testdir.makepyfile("""
- setuplist = []
- class TestClass:
- def setup_method(self, func):
- #print "setup_method", self, func
- setuplist.append(self)
- self.init = 42
-
- def teardown_method(self, func):
- self.init = None
-
- def test_func1(self):
- pass
-
- def test_func2(self):
- yield self.func2
- yield self.func2
-
- def func2(self):
- assert self.init
-
- def test_setuplist():
- # once for test_func2 during collection
- # once for test_func1 during test run
- # once for test_func2 during test run
- #print setuplist
- assert len(setuplist) == 3, len(setuplist)
- assert setuplist[0] == setuplist[2], setuplist
- assert setuplist[1] != setuplist[2], setuplist
- """)
- reprec = testdir.inline_run(o, '-v')
- passed, skipped, failed = reprec.countoutcomes()
- assert passed == 4
- assert not skipped and not failed
-
-
class TestFunction:
def test_getmodulecollector(self, testdir):
item = testdir.getitem("def test_func(): pass")
@@ -690,37 +497,6 @@
assert lineno == 1
assert msg == "TestClass"
- def test_generator_reportinfo(self, testdir):
- modcol = testdir.getmodulecol("""
- # lineno 0
- def test_gen():
- def check(x):
- assert x
- yield check, 3
- """)
- gencol = testdir.collect_by_name(modcol, "test_gen")
- fspath, lineno, modpath = gencol.reportinfo()
- assert fspath == modcol.fspath
- assert lineno == 1
- assert modpath == "test_gen"
-
- genitem = gencol.collect()[0]
- fspath, lineno, modpath = genitem.reportinfo()
- assert fspath == modcol.fspath
- assert lineno == 2
- assert modpath == "test_gen[0]"
- """
- def test_func():
- pass
- def test_genfunc():
- def check(x):
- pass
- yield check, 3
- class TestClass:
- def test_method(self):
- pass
- """
-
def test_customized_python_discovery(testdir):
testdir.makeini("""
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/python/fixture.py
--- a/testing/python/fixture.py
+++ b/testing/python/fixture.py
@@ -1246,9 +1246,7 @@
yield f, -3
""")
reprec = testdir.inline_run()
- reprec.assertoutcome(passed=2)
-
-
+ reprec.assertoutcome(passed=1)
def test_funcarg_and_setup(self, testdir):
testdir.makepyfile("""
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/test_nose.py
--- a/testing/test_nose.py
+++ b/testing/test_nose.py
@@ -146,6 +146,7 @@
])
[email protected](reason='generator tests dont hook into setup/teardown')
def test_nose_test_generator_fixtures(testdir):
p = testdir.makepyfile("""
# taken from nose-0.11.1 unit_tests/test_generator_fixtures.py
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/test_runner_xunit.py
--- a/testing/test_runner_xunit.py
+++ b/testing/test_runner_xunit.py
@@ -1,6 +1,7 @@
#
# test correct setup/teardowns at
# module, class, and instance level
+import pytest
def test_module_and_function_setup(testdir):
reprec = testdir.inline_runsource("""
@@ -141,6 +142,8 @@
""")
reprec.assertoutcome(failed=1, passed=1)
+
[email protected](reason='generator check reporting detail got reduced')
def test_method_generator_setup(testdir):
reprec = testdir.inline_runsource("""
class TestSetupTeardownOnInstance:
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/test_session.py
--- a/testing/test_session.py
+++ b/testing/test_session.py
@@ -61,9 +61,10 @@
def test_1():
yield None
""")
- failures = reprec.getfailedcollections()
- out = failures[0].longrepr.reprcrash.message
- i = out.find('TypeError')
+ passed, skipped, failed = reprec.listoutcomes()
+ assert len(failed) == 1
+ out = failed[0].longrepr.reprcrash.message
+ i = out.find('not a check')
assert i != -1
def test_syntax_error_module(self, testdir):
diff -r 46d5ac0ba396af887969a3cc9940fccba5ff1955 -r
bd9f28f011eafe48ddf95b6a3e9dd260aa69c99f testing/test_terminal.py
--- a/testing/test_terminal.py
+++ b/testing/test_terminal.py
@@ -453,7 +453,7 @@
"*test_verbose_reporting.py::test_fail *FAIL*",
"*test_verbose_reporting.py::test_pass *PASS*",
"*test_verbose_reporting.py::TestClass::test_skip *SKIP*",
- "*test_verbose_reporting.py::test_gen*0* *FAIL*",
+ "*test_verbose_reporting.py::test_gen *FAIL*",
])
assert result.ret == 1
Repository URL: https://bitbucket.org/hpk42/pytest/
--
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
[email protected]
https://mail.python.org/mailman/listinfo/pytest-commit